Assembling Partial Data Into Full Offices
To create a complete office, we need to handle anything between one and three general or partial models with appropriate logic for combining them. This work is split into two levels.
The first level handles the actual output of the data, and the various sets of conditions governing their combinations:
class IFullOfficeGenerator
{
public:
virtual ~IFullOfficeGenerator();
virtual void
generateFromSingleSource(const IOfficeInfoSource &inSource,
const IHoursInfo &inGeneralInfo) const = 0;
virtual void
generateFromCombinedSources(const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo,
const std::string &inCollect,
const int inDayOfMonth) const = 0;
virtual void generateFromCombinedSourcesWithVariantAntiphons(
const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo, const std::string &inCollect,
const int inDayOfMonth,
const std::pair<std::string, std::string> &inAntiphons) const = 0;
virtual void generateForSpecialDay(const SpecialDays inSpecialDay) const = 0;
virtual void generateOctaveDay(const IOfficeInfoSource &inSource,
const IHoursInfo &inGeneralInfo) const = 0;
virtual void
generateDayInGreaterOctave(const IOfficeInfoSource &inSource,
const IHoursInfo &inGeneralInfo,
const int inDaysAfterFeast) const = 0;
virtual void
generateOctaveDayForApostles(const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo) const = 0;
};
Builders
The Builder classes are the core of this logic in the Generator implementation.
Let's look at the simplest instance, used to build up the Prime office.
class PrimeBuilder
{
public:
std::unique_ptr<IPrimeInfo>
generatePrimeInfo(const IPsalter &inPsalter,
const IPartialOfficeInfo &inPartialInfo,
const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason, const Days inDay);
std::unique_ptr<IPrimeInfo>
generatePrimeInfo(const IPsalter &inPsalter,
const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason);
private:
bool isMarianDay(const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason) const;
class SeasonalPrimeInfo : public IPrimeInfo
{
public:
SeasonalPrimeInfo(const Grades inGrade, const HymnSeasons inSeason,
const IPsalter &inPsalter, const Days inDay)
: m_grade(inGrade), m_season(inSeason), m_psalter(inPsalter),
m_day(inDay)
{ }
~SeasonalPrimeInfo() override;
PrimeChapter generatePrimeChapter() const override;
PrimeAntiphons generatePrimeAntiphons() const override;
PrimePsalmSpec generatePrimePsalmSpec() const override;
protected:
Grades m_grade;
HymnSeasons m_season;
private:
const IPsalter &m_psalter;
Days m_day;
};
class ParsedPrimeInfo : public SeasonalPrimeInfo
{
public:
ParsedPrimeInfo(const IPsalter &inPsalter, const Grades inGrade,
const std::string &inAntiphon, const Days inDays,
const HymnSeasons inSeason, const bool inIsMarian)
: SeasonalPrimeInfo(inGrade, inSeason, inPsalter, inDays),
m_antiphon(inAntiphon), m_isMarian(inIsMarian)
{ }
~ParsedPrimeInfo() override;
PrimeChapter generatePrimeChapter() const override;
PrimeAntiphons generatePrimeAntiphons() const override;
private:
std::string m_antiphon;
bool m_isMarian;
};
};
The two variants returned are declared in the header. SeasonalPrimeInfo has the following function definitions:
PrimeChapter
PrimeBuilder::SeasonalPrimeInfo::generatePrimeChapter() const
{
return PrimeChapter(m_grade, m_season, false);
}
PrimeAntiphons
PrimeBuilder::SeasonalPrimeInfo::generatePrimeAntiphons() const
{
return PrimeAntiphons(m_season);
}
PrimePsalmSpec
PrimeBuilder::SeasonalPrimeInfo::generatePrimePsalmSpec() const
{
return PrimePsalmSpec{ m_psalter, m_day, m_season };
}
ParsedPrimeInfo uses the psalm spec function, but overrides the two others:
PrimeChapter
PrimeBuilder::ParsedPrimeInfo::generatePrimeChapter() const
{
return PrimeChapter(m_grade, m_season, m_isMarian);
}
PrimeAntiphons
PrimeBuilder::ParsedPrimeInfo::generatePrimeAntiphons() const
{
return PrimeAntiphons(m_antiphon);
}
This has one function to assemble two sets of records, and one to use a "complete" set.
Here is the simpler one that uses a single set:
std::unique_ptr<IPrimeInfo>
PrimeBuilder::generatePrimeInfo(const IPsalter &inPsalter,
const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason)
{
bool isMarian = isMarianDay(inStandardInfo, inSeason);
if (!inStandardInfo.getPrimeInfo().antiphon.empty())
return std::make_unique<ParsedPrimeInfo>(
inPsalter, inStandardInfo.getGrade(),
inStandardInfo.getPrimeInfo().antiphon, inStandardInfo.getDay(),
inSeason, isMarian);
else if (auto ant = inStandardInfo.getLaudsInfo().generateLaudsAntiphons();
ant.antiphons() >= 3)
return std::make_unique<ParsedPrimeInfo>(
inPsalter, inStandardInfo.getGrade(),
ant.getAntiphon(OfficeNames::PRIME), inStandardInfo.getDay(), inSeason,
isMarian);
else
return std::make_unique<SeasonalPrimeInfo>(inStandardInfo.getGrade(),
inSeason, inPsalter,
inStandardInfo.getDay());
}
and here is the slightly more complex one that uses two:
std::unique_ptr<IPrimeInfo>
PrimeBuilder::generatePrimeInfo(const IPsalter &inPsalter,
const IPartialOfficeInfo &inPartialInfo,
const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason, const Days inDay)
{
const IPartialOfficeInfo::HourInfo &info
= inPartialInfo.getHourInfo(OfficeNames::PRIME);
std::string antiphon;
if (!info.antiphon.empty())
{
antiphon = info.antiphon;
}
else if (auto ant = inStandardInfo.getLaudsInfo().generateLaudsAntiphons();
ant.antiphons() >= 3)
{
antiphon = ant.getAntiphon(OfficeNames::PRIME);
}
bool isMarian = isMarianDay(inStandardInfo, inSeason);
if (!antiphon.empty() || isMarian)
return std::make_unique<ParsedPrimeInfo>(
inPsalter, inPartialInfo.getGrade(), antiphon, inDay, inSeason,
isMarian);
return std::make_unique<SeasonalPrimeInfo>(inPartialInfo.getGrade(),
inSeason, inPsalter, inDay);
}
(That test: if (auto ant = inStandardInfo.getLaudsInfo().generateLaudsAntiphons(); ant.antiphons() >= 3) is almost past the border of being turned into a function, but it's only two uses, it's two lines, and it's not particularly confusing, so it stays.)
Prime is simple, though: only the antiphon can vary between the two-stage and the one-stage pieces of logic. Let's consider the Lauds version next:
class LaudsBuilder
{
public:
LaudsBuilder(const IPsalter &inPsalter, const IHoursInfo &inHoursInfo,
const Days inDay, const std::string &inName)
: m_hoursInfo(inHoursInfo), m_psalter(inPsalter), m_day(inDay),
m_name(inName)
{ }
std::unique_ptr<ILaudsInfo> generateLaudsInfo(
const IPartialOfficeInfo &inPartialInfo,
const IOfficeInfoHolder &inStandardInfo, const HymnSeasons inSeason,
const std::string &inOverrideBenedictus = "");
std::unique_ptr<ILaudsInfo>
generateLaudsInfo(const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason);
private:
class DetailedLaudsInfo : public ILaudsInfo
{
public:
DetailedLaudsInfo(
const IPsalter &inPsalter, const std::string &inName, Days inDay,
const IHoursInfo &inHoursInfo, const std::string &inChapterText,
const std::string &inChapterSrc, const std::string &inHymnName,
const BenedictusAntiphons &inBenedictusAntiphon,
const std::pair<std::string, std::string> &inInvitationResponses,
std::span<std::string> inChapterResponses,
const LaudsAntiphons &inAntiphons);
~DetailedLaudsInfo() override;
LaudsInvocation generateLaudsInvocation() const override;
LaudsPsalmSpec generateLaudsPsalmSpec() const override;
LaudsAntiphons generateLaudsAntiphons() const override;
LaudsChapter generateLaudsChapter() const override;
BenedictusAntiphons generateBenedictusAntiphons() const override;
std::unique_ptr<ILaudsInfo>
clone() const override
{
return std::make_unique<DetailedLaudsInfo>(*this);
}
void
resetBenedictusAntiphon(const std::string &inVal) override
{
m_benedictusAntiphon = BenedictusAntiphons(inVal);
}
const IPsalter& getPsalter() const override { return m_psalter; }
private:
std::string m_name;
std::string m_chapterText;
std::string m_chapterSrc;
BenedictusAntiphons m_benedictusAntiphon;
std::string m_hymnName;
std::pair<std::string, std::string> m_invitationResponses;
mutable std::array<std::string, 2> m_chapterResponses;
LaudsAntiphons m_antiphons;
const IPsalter &m_psalter;
Days m_day;
const IHoursInfo &m_hoursInfo;
};
const IHoursInfo &m_hoursInfo;
const IPsalter &m_psalter;
Days m_day;
std::string m_name;
};
DetailedLaudsInfo is the implementation of ILaudsInfo used in several contexts as the most general case, which is why it gets declared in the header. There are more specialized versions, but only in local contexts. Its implementations are straightforward (partly because other conditions are split off into other implementations of the interface:
LaudsBuilder::DetailedLaudsInfo::DetailedLaudsInfo(
const IPsalter &inPsalter, const std::string &inName, Days inDay,
const IHoursInfo &inHoursInfo, const std::string &inChapterText,
const std::string &inChapterSrc, const std::string &inHymnName,
const BenedictusAntiphons &inBenedictusAntiphon,
const std::pair<std::string, std::string> &inInvitationResponses,
std::span<std::string> inChapterResponses,
const LaudsAntiphons &inAntiphons):
m_name(inName),
m_chapterText(inChapterText), m_chapterSrc(inChapterSrc),
m_benedictusAntiphon(inBenedictusAntiphon), m_hymnName(inHymnName),
m_invitationResponses(inInvitationResponses), m_antiphons(inAntiphons),
m_psalter(inPsalter), m_day(inDay), m_hoursInfo(inHoursInfo)
{
if (inChapterResponses.size() != 2)
throw std::runtime_error(
"Cannot build lauds chapter responses except with two values");
std::ranges::copy(inChapterResponses, m_chapterResponses.begin());
}
LaudsBuilder::DetailedLaudsInfo::~DetailedLaudsInfo() {}
LaudsInvocation
LaudsBuilder::DetailedLaudsInfo::generateLaudsInvocation() const
{
return LaudsInvocation(m_invitationResponses.first,
m_invitationResponses.second,
isSemiLenten(m_hoursInfo.getSeason()));
}
LaudsPsalmSpec LaudsBuilder::DetailedLaudsInfo::generateLaudsPsalmSpec() const
{
return LaudsPsalmSpec(m_psalter, m_day, m_hoursInfo.grade(),
m_hoursInfo.getSeason());
}
LaudsAntiphons LaudsBuilder::DetailedLaudsInfo::generateLaudsAntiphons() const
{
return m_antiphons;
}
LaudsChapter LaudsBuilder::DetailedLaudsInfo::generateLaudsChapter() const
{
return LaudsChapter(m_chapterText, m_chapterSrc, m_chapterResponses,
m_hymnName);
}
BenedictusAntiphons
LaudsBuilder::DetailedLaudsInfo::generateBenedictusAntiphons() const
{
return BenedictusAntiphons(m_benedictusAntiphon);
}
For examples of the specialized versions, we can look at the single-input function:
std::unique_ptr<ILaudsInfo>
LaudsBuilder::generateLaudsInfo(const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason)
{
class SimpleLaudsInfo : public ILaudsInfo
{
public:
SimpleLaudsInfo(const IPsalter &inPsalter, const std::string &inName,
Days inDay, const IHoursInfo &inHoursInfo):
m_name(inName),
m_day(inDay), m_hoursInfo(inHoursInfo), m_psalter(inPsalter)
{ }
~SimpleLaudsInfo() override {}
LaudsInvocation generateLaudsInvocation() const override
{
return LaudsInvocation(m_day, isSemiLenten(m_hoursInfo.getSeason()));
}
LaudsPsalmSpec generateLaudsPsalmSpec() const override
{
return LaudsPsalmSpec(m_psalter, m_day, m_hoursInfo.grade(),
m_hoursInfo.getSeason());
}
LaudsAntiphons generateLaudsAntiphons() const override
{
return LaudsAntiphons(
m_name, LaudsAntiphonsGenerator::GenerateAntiphons(m_day), false);
}
LaudsChapter generateLaudsChapter() const override
{
return LaudsChapter(m_day, m_hoursInfo);
}
BenedictusAntiphons generateBenedictusAntiphons() const override
{
return BenedictusAntiphons(m_day);
}
std::unique_ptr<ILaudsInfo> clone() const override
{
return std::make_unique<SimpleLaudsInfo>(*this);
}
void resetBenedictusAntiphon(const std::string &inVal) override {}
const IPsalter &getPsalter() const override { return m_psalter; }
private:
std::string m_name;
Days m_day;
const IHoursInfo &m_hoursInfo;
const IPsalter &m_psalter;
};
if (((inSeason == HymnSeasons::NORMAL)
|| (inSeason == HymnSeasons::SEPTUAGESIMA))
&& ((inStandardInfo.getGrade() == Grades::FERIA)
|| (inStandardInfo.getGrade() == Grades::DOMINICA)
|| (inStandardInfo.getGrade() == Grades::DOMINICA_SECOND_CLASS)
|| (inStandardInfo.getGrade() == Grades::DOMINICA_FIRST_CLASS)))
{
if ((m_day == Days::SATURDAY)
&& (m_name.find("on Saturday") != std::string::npos))
{
return inStandardInfo.getLaudsInfo().clone();
}
return std::make_unique<SimpleLaudsInfo>(m_psalter, m_name, m_day,
m_hoursInfo);
}
else if ((m_day != Days::SUNDAY)
&& (inStandardInfo.getGrade() == Grades::FERIA)
&& ((m_hoursInfo.getSeason() == HymnSeasons::LENT)
|| (m_hoursInfo.getSeason() == HymnSeasons::MID_LENT)
|| (m_hoursInfo.getSeason() == HymnSeasons::PASSIONTIDE)))
{
return std::make_unique<SimpleLaudsInfo>(m_psalter, m_name, m_day,
m_hoursInfo);
}
else if ((m_hoursInfo.getSeason() == HymnSeasons::EASTER)
&& (inStandardInfo.getGrade() == Grades::FERIA))
{
class CoreEasterLaudsInfo : public ILaudsInfo
{
protected:
CoreEasterLaudsInfo(const IPsalter &inPsalter,
const std::string &inName, const Days inDay,
const Grades inGrade):
m_name(inName),
m_psalter(inPsalter), m_day(inDay), m_grade(inGrade)
{ }
public:
~CoreEasterLaudsInfo() override {}
LaudsInvocation generateLaudsInvocation() const override
{
return LaudsInvocation("In Thy Resurrection, O Christ.",
"Let heaven and earth rejoice, alleluia.",
false);
}
LaudsPsalmSpec generateLaudsPsalmSpec() const override
{
return LaudsPsalmSpec(m_psalter, m_day, m_grade,
HymnSeasons::EASTER);
}
LaudsAntiphons generateLaudsAntiphons() const override
{
std::array<std::string, 1> ant{ "Alleluia, alleluia." };
return LaudsAntiphons(m_name, ant, false);
}
void resetBenedictusAntiphon(const std::string &inVal) override {}
const IPsalter &getPsalter() const override { return m_psalter; }
protected:
LaudsChapter assembleLaudsChapter(const std::string &inText,
const std::string &inSrc) const
{
const static std::array<std::string, 2> resp{
"The Lord is risen from the sepulchre",
"Who for our sakes did hang upon the tree, alleluia"
};
return LaudsChapter(inText, inSrc, resp, "Sermone blando angelus");
}
std::string m_name;
const IPsalter &m_psalter;
Days m_day;
Grades m_grade;
};
if (m_name.find("Rogation") == std::string::npos)
{
class EasterLaudsInfo : public CoreEasterLaudsInfo
{
public:
EasterLaudsInfo(const IPsalter &inPsalter,
const std::string &inName, const Days inDay,
const IHoursInfo &inHoursInfo,
const std::string &inChapterText,
const std::string &inChapterSrc,
const EasterAntiphonConverter inConverter):
CoreEasterLaudsInfo(inPsalter, inName, inDay,
inHoursInfo.grade()),
m_chapterText(inChapterText), m_chapterSrc(inChapterSrc)
{
inConverter.convertEasterWeekToAntiphons(m_weeklyAntiphons,
inName);
}
~EasterLaudsInfo() override {}
LaudsChapter generateLaudsChapter() const override
{
return assembleLaudsChapter(m_chapterText, m_chapterSrc);
}
BenedictusAntiphons generateBenedictusAntiphons() const override
{
switch (m_day)
{
using enum Days;
case MONDAY:
return BenedictusAntiphons(m_weeklyAntiphons[0]);
break;
case TUESDAY:
return BenedictusAntiphons(m_weeklyAntiphons[2]);
break;
case WEDNESDAY:
if (m_weeklyAntiphons[4].empty())
return BenedictusAntiphons(m_weeklyAntiphons[0]);
return BenedictusAntiphons(m_weeklyAntiphons[4]);
break;
case THURSDAY:
if (m_weeklyAntiphons[4].empty())
return BenedictusAntiphons(m_weeklyAntiphons[2]);
return BenedictusAntiphons(m_weeklyAntiphons[1]);
break;
case FRIDAY:
if (m_weeklyAntiphons[4].empty())
return BenedictusAntiphons(m_weeklyAntiphons[0]);
return BenedictusAntiphons(m_weeklyAntiphons[3]);
break;
case SATURDAY:
return BenedictusAntiphons(
"O wondrous interchange! The Creator of mankind taking upon him a living body, vouchsafed to be born of a Virgin : and begotten in no earthly wise, hath made us partakers of his Divinity, alleluia.");
break;
case SUNDAY:
throw std::runtime_error(
"Cannot use general easter lauds model for Sunday");
break;
default:
return BenedictusAntiphons(m_day);
break;
};
}
std::unique_ptr<ILaudsInfo> clone() const override
{
return std::make_unique<EasterLaudsInfo>(*this);
}
private:
std::string m_chapterText;
std::string m_chapterSrc;
std::array<std::string, 5> m_weeklyAntiphons;
};
constinit static EasterAntiphonConverter converter;
auto chapt = inStandardInfo.getLaudsInfo().generateLaudsChapter();
return std::make_unique<EasterLaudsInfo>(
m_psalter, m_name, m_day, m_hoursInfo, chapt.getChapterText(),
chapt.getChapterSrc(), converter);
}
else
{
class RogationLaudsInfo : public CoreEasterLaudsInfo
{
public:
RogationLaudsInfo(const IPsalter &inPsalter,
const std::string &inName, Days inDay,
const IHoursInfo &inHoursInfo):
CoreEasterLaudsInfo(inPsalter, inName, inDay,
inHoursInfo.grade())
{ }
~RogationLaudsInfo() override {}
LaudsChapter generateLaudsChapter() const override
{
if ((m_day == Days::MONDAY) || (m_day == Days::TUESDAY))
return assembleLaudsChapter(
"Be ye doers of the word, and not hearers only, deceiving your own selves. For if any be a hearer of the word, and not a doer, he is like unto a man beholding his natural face in a glass.",
"James i");
else
return assembleLaudsChapter(
"And the multitude of them that believed were of one heart and of one soul : neither said any of them that ought of the things which he possessed was his own ; but they had all things common.",
"Acts iv");
}
BenedictusAntiphons generateBenedictusAntiphons() const override
{
if (m_day == Days::MONDAY)
return BenedictusAntiphons(
"Ask, and it shall be given you; seek, and ye shall find : knock, and it shall be opened unto you. Alleluia.");
else if (m_day == Days::TUESDAY)
return BenedictusAntiphons(
"I came forth from the Father, and am come into the world: again, I leave the world, and go to the Father, alleluia.");
else
return BenedictusAntiphons(
"And now, O Father, glorify Thou me with Thine own self: with the glory which I had with Thee before the world was. Alleluia.");
}
std::unique_ptr<ILaudsInfo> clone() const override
{
return std::make_unique<RogationLaudsInfo>(*this);
}
};
return std::make_unique<RogationLaudsInfo>(m_psalter, m_name, m_day,
m_hoursInfo);
}
}
else
{
return inStandardInfo.getLaudsInfo().clone();
}
}
The complexity of the model has changed, so we now have three additional "leaf" classes (plus one intermediate class to factor common functionality out.
The two-input model doesn't have to concern itself with the special cases, which all relate to situations where a single input is expected. It therefore has no additional models but has to work to deal with the permutations of what might be set in a partial instance and what might not:
std::unique_ptr<ILaudsInfo>
LaudsBuilder::generateLaudsInfo(const IPartialOfficeInfo &inPartialInfo,
const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason,
const std::string &inOverrideBenedictus)
{
const IPartialOfficeInfo::HourInfo &partialLaudsInfo
= inPartialInfo.getHourInfo(OfficeNames::LAUDS);
std::vector<std::string> invitation(partialLaudsInfo.invitation);
if (invitation.empty())
{
auto inv = inStandardInfo.getLaudsInfo()
.generateLaudsInvocation()
.getSarumResponses();
invitation.push_back(inv.first);
invitation.push_back(inv.second);
}
std::unique_ptr<LaudsAntiphons> antiphons;
if (partialLaudsInfo.majorHourAntiphons.empty())
antiphons = std::make_unique<LaudsAntiphons>(
inStandardInfo.getLaudsInfo().generateLaudsAntiphons());
else
antiphons = std::make_unique<LaudsAntiphons>(
m_name,
const_cast<std::vector<std::string> &>(
partialLaudsInfo.majorHourAntiphons),
(partialLaudsInfo.majorHourAntiphons.size() == 5));
std::vector<std::string> chapterResponses;
std::string chapter;
std::string chapterSrc;
std::string hymn(partialLaudsInfo.hymnName);
if (partialLaudsInfo.chapterResponses.empty()
|| partialLaudsInfo.chapter.empty()
|| partialLaudsInfo.chapterSrc.empty() || hymn.empty())
{
auto chapt = inStandardInfo.getLaudsInfo().generateLaudsChapter();
if (partialLaudsInfo.chapterResponses.empty()
|| partialLaudsInfo.chapter.empty()
|| partialLaudsInfo.chapterSrc.empty())
{
std::ranges::copy(chapt.getResponses(),
std::back_inserter(chapterResponses));
chapter = chapt.getChapterText();
chapterSrc = chapt.getChapterSrc();
}
if (hymn.empty())
hymn = chapt.getHymnName();
}
else
{
chapterResponses = partialLaudsInfo.chapterResponses;
chapter = partialLaudsInfo.chapter;
chapterSrc = partialLaudsInfo.chapterSrc;
}
if (!inOverrideBenedictus.empty())
return std::make_unique<DetailedLaudsInfo>(
m_psalter, m_name, m_day, m_hoursInfo, chapter, chapterSrc, hymn,
BenedictusAntiphons(inOverrideBenedictus),
std::make_pair(invitation[0], invitation[1]), chapterResponses,
*antiphons);
else if (partialLaudsInfo.antiphon.empty())
return std::make_unique<DetailedLaudsInfo>(
m_psalter, m_name, m_day, m_hoursInfo, chapter, chapterSrc, hymn,
inStandardInfo.getLaudsInfo().generateBenedictusAntiphons(),
std::make_pair(invitation[0], invitation[1]), chapterResponses,
*antiphons);
else
return std::make_unique<DetailedLaudsInfo>(
m_psalter, m_name, m_day, m_hoursInfo, chapter, chapterSrc, hymn,
BenedictusAntiphons(partialLaudsInfo.antiphon),
std::make_pair(invitation[0], invitation[1]), chapterResponses,
*antiphons);
}
Vespers is more complicated still, because there is different logic for first and second vespers.
Vespers declares an abstract strategy:
class IVariantVespersHandler
{
public:
virtual ~IVariantVespersHandler();
virtual std::unique_ptr<IVespersInfo>
getStandardInfo(const IOfficeInfoHolder &inStandardInfo) const = 0;
virtual std::unique_ptr<IVespersInfo>
getEasterInfo(const IOfficeInfoHolder &inStandardInfo) const = 0;
virtual const IPartialOfficeInfo::HourInfo &
getPartialInfo(const IPartialOfficeInfo &inPartialInfo) const = 0;
virtual VespersAntiphons
getStandardAntiphons(const IOfficeInfoHolder &inStandardInfo) const = 0;
virtual VespersChapter
getStandardChapter(const IOfficeInfoHolder &inStandardInfo) const = 0;
virtual VespersPsalmSpec
getPsalmSpec(const IOfficeInfoHolder &inStandardInfo) const = 0;
virtual std::unique_ptr<IVespersInfo>
getOAntiphonInfo(const MagnificatAntiphons &inAnt,
const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons,
VespersPsalmSpec &&inPsalmSpec) const = 0;
virtual std::unique_ptr<IVespersInfo>
getDetailedInfoFromStandardInfo(const IOfficeInfoHolder &inStandardInfo,
const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons,
VespersPsalmSpec &&inPsalmSpec) const = 0;
};
which has two implementations:
class FirstVespersHandler : public IVariantVespersHandler
{
public:
FirstVespersHandler(const Days inDay, const std::string &inName):
m_day(inDay), m_name(inName)
{ }
~FirstVespersHandler() override;
std::unique_ptr<IVespersInfo>
getStandardInfo(const IOfficeInfoHolder &inStandardInfo) const override;
std::unique_ptr<IVespersInfo>
getEasterInfo(const IOfficeInfoHolder &inStandardInfo) const override;
const IPartialOfficeInfo::HourInfo &
getPartialInfo(const IPartialOfficeInfo &inPartialInfo) const override;
VespersAntiphons getStandardAntiphons(
const IOfficeInfoHolder &inStandardInfo) const override;
VespersChapter
getStandardChapter(const IOfficeInfoHolder &inStandardInfo) const override;
VespersPsalmSpec
getPsalmSpec(const IOfficeInfoHolder &inStandardInfo) const override;
std::unique_ptr<IVespersInfo>
getOAntiphonInfo(const MagnificatAntiphons &inAnt,
const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons,
VespersPsalmSpec &&inPsalmSpec) const override;
std::unique_ptr<IVespersInfo> getDetailedInfoFromStandardInfo(
const IOfficeInfoHolder &inStandardInfo,
const VespersChapter &inChapter, const VespersAntiphons &inAntiphons,
VespersPsalmSpec &&inPsalmSpec) const override;
private:
Days m_day;
std::string m_name;
};
class SecondVespersHandler : public IVariantVespersHandler
{
public:
SecondVespersHandler(const Days inDay, const std::string &inName):
m_day(inDay), m_name(inName)
{ }
~SecondVespersHandler() override;
std::unique_ptr<IVespersInfo>
getStandardInfo(const IOfficeInfoHolder &inStandardInfo) const override;
std::unique_ptr<IVespersInfo>
getEasterInfo(const IOfficeInfoHolder &inStandardInfo) const override;
const IPartialOfficeInfo::HourInfo &
getPartialInfo(const IPartialOfficeInfo &inPartialInfo) const override;
VespersAntiphons getStandardAntiphons(
const IOfficeInfoHolder &inStandardInfo) const override;
VespersChapter
getStandardChapter(const IOfficeInfoHolder &inStandardInfo) const override;
VespersPsalmSpec
getPsalmSpec(const IOfficeInfoHolder &inStandardInfo) const override;
std::unique_ptr<IVespersInfo>
getOAntiphonInfo(const MagnificatAntiphons &inAnt,
const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons,
VespersPsalmSpec &&inPsalmSpec) const override;
std::unique_ptr<IVespersInfo> getDetailedInfoFromStandardInfo(
const IOfficeInfoHolder &inStandardInfo,
const VespersChapter &inChapter, const VespersAntiphons &inAntiphons,
VespersPsalmSpec &&inPsalmSpec) const override;
private:
Days m_day;
std::string m_name;
};
std::unique_ptr<IVespersInfo>
VespersBuilder::FirstVespersHandler::getStandardInfo(
const IOfficeInfoHolder &inStandardInfo) const
{
return inStandardInfo.getFirstVespersInfo().clone();
}
std::unique_ptr<IVespersInfo>
VespersBuilder::FirstVespersHandler::getEasterInfo(
const IOfficeInfoHolder &inStandardInfo) const
{
constinit static EasterAntiphonConverter converter;
auto chapt = inStandardInfo.getFirstVespersInfo().generateChapter();
return std::make_unique<EasterVespersInfo>(m_name, m_day, true,
chapt.getChapterText(),
chapt.getChapterSrc(), converter);
}
const IPartialOfficeInfo::HourInfo &
VespersBuilder::FirstVespersHandler::getPartialInfo(
const IPartialOfficeInfo &inPartialInfo) const
{
return inPartialInfo.getHourInfo(OfficeNames::VESPERS, true);
}
VespersAntiphons VespersBuilder::FirstVespersHandler::getStandardAntiphons(
const IOfficeInfoHolder &inStandardInfo) const
{
return inStandardInfo.getFirstVespersInfo().generateAntiphons();
}
VespersChapter VespersBuilder::FirstVespersHandler::getStandardChapter(
const IOfficeInfoHolder &inStandardInfo) const
{
return inStandardInfo.getFirstVespersInfo().generateChapter();
}
VespersPsalmSpec VespersBuilder::FirstVespersHandler::getPsalmSpec(
const IOfficeInfoHolder &inStandardInfo) const
{
if ((m_name == "Corpus Christi"s)
|| ((m_name == "Christmas") || (m_name == "Epiphany")))
return VespersPsalmSpec(m_name);
else
return VespersPsalmSpec(m_day);
}
std::unique_ptr<IVespersInfo>
VespersBuilder::FirstVespersHandler::getOAntiphonInfo(
const MagnificatAntiphons &inAnt, const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons, VespersPsalmSpec &&inPsalmSpec) const
{
if (m_day != Days::SUNDAY)
{
std::array<std::string, 2> resps{
"Come, Lord, tarry not, do away the offences of Thy people Israel."s,
"Make haste, Lord, tarry not: * and deliver Thy people."s
};
VespersChapter chapt(inChapter, resps);
return std::make_unique<DetailedVespersInfo>(inAnt, chapt, inAntiphons,
std::move(inPsalmSpec));
}
else
return std::make_unique<DetailedVespersInfo>(inAnt, inChapter, inAntiphons,
std::move(inPsalmSpec));
}
std::unique_ptr<IVespersInfo>
VespersBuilder::FirstVespersHandler::getDetailedInfoFromStandardInfo(
const IOfficeInfoHolder &inStandardInfo, const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons, VespersPsalmSpec &&inPsalmSpec) const
{
return std::make_unique<DetailedVespersInfo>(
inStandardInfo.getFirstVespersInfo().generateMagnificatAntiphons(),
inChapter, inAntiphons, std::move(inPsalmSpec));
}
and
std::unique_ptr<IVespersInfo>
VespersBuilder::SecondVespersHandler::getStandardInfo(
const IOfficeInfoHolder &inStandardInfo) const
{
return inStandardInfo.getSecondVespersInfo().clone();
}
std::unique_ptr<IVespersInfo>
VespersBuilder::SecondVespersHandler::getEasterInfo(
const IOfficeInfoHolder &inStandardInfo) const
{
constinit static EasterAntiphonConverter converter;
auto chapt = inStandardInfo.getSecondVespersInfo().generateChapter();
return std::make_unique<EasterVespersInfo>(m_name, m_day, false,
chapt.getChapterText(),
chapt.getChapterSrc(), converter);
}
const IPartialOfficeInfo::HourInfo &
VespersBuilder::SecondVespersHandler::getPartialInfo(
const IPartialOfficeInfo &inPartialInfo) const
{
return inPartialInfo.getHourInfo(OfficeNames::VESPERS, false);
}
VespersAntiphons VespersBuilder::SecondVespersHandler::getStandardAntiphons(
const IOfficeInfoHolder &inStandardInfo) const
{
return inStandardInfo.getSecondVespersInfo().generateAntiphons();
}
VespersChapter VespersBuilder::SecondVespersHandler::getStandardChapter(
const IOfficeInfoHolder &inStandardInfo) const
{
return inStandardInfo.getSecondVespersInfo().generateChapter();
}
VespersPsalmSpec VespersBuilder::SecondVespersHandler::getPsalmSpec(
const IOfficeInfoHolder &inStandardInfo) const
{
if (m_name == "Corpus Christi"s)
return VespersPsalmSpec(m_name);
else if ((m_name == "Christmas") || (m_name == "Epiphany"))
{
static std::vector<PsalmSpec> spec{
PsalmSpec{ "110" }, PsalmSpec{ "111" }, PsalmSpec{ "112" },
PsalmSpec{ "130" }, PsalmSpec{ "132" }
};
return VespersPsalmSpec(spec);
}
else if ((inStandardInfo.getName().find(" Ap."s) != std::string::npos)
|| inStandardInfo.getName().ends_with(" App."s))
{
return VespersPsalmSpec("Apostles Second Vespers");
}
else
return VespersPsalmSpec(m_day);
}
std::unique_ptr<IVespersInfo>
VespersBuilder::SecondVespersHandler::getOAntiphonInfo(
const MagnificatAntiphons &inAnt, const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons, VespersPsalmSpec &&inPsalmSpec) const
{
std::array<std::string, 2> resps{
"Come, Lord, tarry not, do away the offences of Thy people Israel."s,
"Make haste, Lord, tarry not: * and deliver Thy people."s
};
VespersChapter chapt(inChapter, resps);
return std::make_unique<DetailedVespersInfo>(inAnt, inChapter, inAntiphons,
std::move(inPsalmSpec));
}
std::unique_ptr<IVespersInfo>
VespersBuilder::SecondVespersHandler::getDetailedInfoFromStandardInfo(
const IOfficeInfoHolder &inStandardInfo, const VespersChapter &inChapter,
const VespersAntiphons &inAntiphons, VespersPsalmSpec &&inPsalmSpec) const
{
return std::make_unique<DetailedVespersInfo>(
inStandardInfo.getSecondVespersInfo().generateMagnificatAntiphons(),
inChapter, inAntiphons, std::move(inPsalmSpec));
}
Because the two strategies use IVespersInfo implementations similar to those in the Lauds case, they are not local here, but rather declared in their own headers, e.g.
class EasterVespersInfo : public CoreEasterVespersInfo
{
public:
EasterVespersInfo(const std::string &inName, const Days inDay,
const bool inFirstVespers,
const std::string &inChapterText,
const std::string &inChapterSrc,
const EasterAntiphonConverter &inConverter);
~EasterVespersInfo() override;
VespersChapter generateChapter() const override
{
return assembleVespersChapter(m_chapterText, m_chapterSrc);
}
MagnificatAntiphons generateMagnificatAntiphons() const override;
std::unique_ptr<IVespersInfo> clone() const override
{
return std::make_unique<EasterVespersInfo>(*this);
}
private:
std::string m_chapterText;
std::string m_chapterSrc;
std::array<std::string, 5> m_weeklyAntiphons;
};
corresponds to the EasterLaudsInfo, above.
The exception to this is SimpleVespersInfo, which is used only for types of days which do not have a first Vespers:
std::unique_ptr<IVespersInfo>
VespersBuilder::generateVespersInfo(const IPsalter &inPsalter,
const IOfficeInfoHolder &inStandardInfo,
const HymnSeasons inSeason)
{
class SimpleVespersInfo : public IVespersInfo
{
public:
SimpleVespersInfo(const IHoursInfo &inHoursInfo, const Days inDay):
m_hoursInfo(inHoursInfo), m_day(inDay)
{ }
~SimpleVespersInfo() override {}
VespersPsalmSpec generatePsalmSpec() const override
{
return VespersPsalmSpec{ m_day };
}
VespersAntiphons generateAntiphons() const override
{
return VespersAntiphons{ m_day };
}
VespersChapter generateChapter() const override
{
return VespersChapter{ m_day, m_hoursInfo };
}
MagnificatAntiphons generateMagnificatAntiphons() const override
{
return MagnificatAntiphons{ m_day };
}
std::unique_ptr<IVespersInfo> clone() const override
{
return std::make_unique<SimpleVespersInfo>(*this);
}
void reduceToOneAntiphon() override {}
void clearChapterResponses() override {}
void resetMagnificatAntiphon(const std::string &inVal) override {}
private:
const IHoursInfo &m_hoursInfo;
Days m_day;
};
if (((inSeason == HymnSeasons::NORMAL)
|| (inSeason == HymnSeasons::SEPTUAGESIMA))
&& ((inStandardInfo.getGrade() == Grades::FERIA)
|| (inStandardInfo.getGrade() == Grades::DOMINICA)
|| (inStandardInfo.getGrade() == Grades::DOMINICA_SECOND_CLASS)
|| (inStandardInfo.getGrade() == Grades::DOMINICA_FIRST_CLASS)))
{
if ((m_day == Days::SATURDAY)
&& (m_name.find("on Saturday") != std::string::npos))
{
return m_handler->getStandardInfo(inStandardInfo);
}
return std::make_unique<SimpleVespersInfo>(m_hoursInfo, m_day);
}
else if ((m_day != Days::SUNDAY)
&& (inStandardInfo.getGrade() == Grades::FERIA)
&& ((m_hoursInfo.getSeason() == HymnSeasons::LENT)
|| (m_hoursInfo.getSeason() == HymnSeasons::MID_LENT)
|| (m_hoursInfo.getSeason() == HymnSeasons::PASSIONTIDE)))
{
return std::make_unique<SimpleVespersInfo>(m_hoursInfo, m_day);
}
else if ((m_hoursInfo.getSeason() == HymnSeasons::EASTER)
&& (inStandardInfo.getGrade() == Grades::FERIA))
{
if (m_name.find("Rogation") == std::string::npos)
{
return m_handler->getEasterInfo(inStandardInfo);
}
else
{
return std::make_unique<RogationVespersInfo>(m_day);
}
}
else
{
return m_handler->getStandardInfo(inStandardInfo);
}
}
The double input version has to handle the special cases of O Antiphons and the use of Annue, Christe for Apostles:
std::unique_ptr<IVespersInfo> VespersBuilder::generateVespersInfo(
const IPsalter &inPsalter, const IPartialOfficeInfo &inPartialInfo,
const IOfficeInfoHolder &inStandardInfo, const HymnSeasons inSeason,
const unsigned int inDay, const std::string &inOverrideMagnificat)
{
const IPartialOfficeInfo::HourInfo &partialVespersInfo
= inPartialInfo.getHourInfo(OfficeNames::VESPERS, m_firstVespers);
std::unique_ptr<VespersAntiphons> antiphons;
if (partialVespersInfo.majorHourAntiphons.empty())
{
antiphons = std::make_unique<VespersAntiphons>(
m_handler->getStandardAntiphons(inStandardInfo));
}
else
antiphons = std::make_unique<VespersAntiphons>(
const_cast<std::vector<std::string> &>(
partialVespersInfo.majorHourAntiphons),
"VespersBuilder::generateVespersInfo()");
std::vector<std::string> chapterResponses;
std::vector<std::string> hymnResponses;
std::string chapterText;
std::string chapterSrc;
std::string hymn(partialVespersInfo.hymnName);
VespersChapter chapter = m_handler->getStandardChapter(inStandardInfo);
if (!partialVespersInfo.chapterResponses.empty()
|| !partialVespersInfo.invitation.empty()
|| !partialVespersInfo.chapter.empty() || !hymn.empty())
{
if (!partialVespersInfo.chapterResponses.empty())
std::ranges::copy(partialVespersInfo.chapterResponses,
std::back_inserter(chapterResponses));
else if (!chapter.getChapterResponses().empty())
std::ranges::copy(chapter.getChapterResponses(),
std::back_inserter(chapterResponses));
if (!partialVespersInfo.invitation.empty())
std::ranges::copy(partialVespersInfo.invitation,
std::back_inserter(hymnResponses));
else
std::ranges::copy(chapter.getHymnResponses().getResponses(),
std::back_inserter(hymnResponses));
if (!partialVespersInfo.chapter.empty())
{
chapterText = partialVespersInfo.chapter;
chapterSrc = partialVespersInfo.chapterSrc;
}
else
{
chapterText = chapter.getChapterText();
chapterSrc = chapter.getChapterSrc();
}
if (hymn.empty())
hymn = chapter.getHymnName();
chapter = VespersChapter(chapterText, chapterSrc, hymnResponses, hymn,
chapterResponses);
if ((hymn == "Annue, Christe") && !partialVespersInfo.hymnVerse.empty())
chapter.setAnnueChristeVerse(
partialVespersInfo.hymnVerse,
((m_name.find("Peter") != std::string::npos)
|| (m_name.find("Simon") != std::string::npos)
|| (m_name.find("Philip") != std::string::npos)));
}
// O Antiphons
if (inSeason == HymnSeasons::ADVENT)
{
MagnificatAntiphons ant(inSeason, inDay);
if (ant.antiphons() == 1)
{
return m_handler->getOAntiphonInfo(
ant, chapter, *antiphons,
m_handler->getPsalmSpec(inStandardInfo));
}
}
if (!inOverrideMagnificat.empty())
{
return std::make_unique<DetailedVespersInfo>(
MagnificatAntiphons(inOverrideMagnificat), chapter, *antiphons,
m_handler->getPsalmSpec(inStandardInfo));
}
else if (partialVespersInfo.antiphon.empty())
{
return m_handler->getDetailedInfoFromStandardInfo(
inStandardInfo, chapter, *antiphons,
m_handler->getPsalmSpec(inStandardInfo));
}
else
return std::make_unique<DetailedVespersInfo>(
MagnificatAntiphons(partialVespersInfo.antiphon), chapter, *antiphons,
m_handler->getPsalmSpec(inStandardInfo));
}
FullOfficeGenerator
The FullOfficeGenerator is implemented in:
class FullOfficeGenerator : public IFullOfficeGenerator
{
public:
FullOfficeGenerator(const std::string &inName, const Days inDay,
const HymnSeasons inSeason, const Use inUse,
const IEncapsulatedOfficeFormatter &inFormatter,
const IPsalter &inPsalter, const IHymnSource &inHymns,
const bool inIsPriest):
m_name(inName),
m_day(inDay), m_season(inSeason), m_romanUse(inUse != Use::Anglican),
m_formatter(inFormatter), m_psalter(inPsalter), m_hymns(inHymns),
m_isPriest(inIsPriest)
{ }
void
generateFromSingleSource(const IOfficeInfoSource &inSource,
const IHoursInfo &inGeneralInfo) const override;
void generateFromCombinedSources(const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo,
const std::string &inCollect,
const int inDayOfMonth) const override;
void generateFromCombinedSourcesWithVariantAntiphons(
const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo, const std::string &inCollect,
const int inDayOfMonth,
const std::pair<std::string, std::string> &inAntiphons) const override;
void generateForSpecialDay(const SpecialDays inSpecialDay) const override;
void generateOctaveDay(const IOfficeInfoSource &inSource,
const IHoursInfo &inGeneralInfo) const override;
void generateDayInGreaterOctave(const IOfficeInfoSource &inSource,
const IHoursInfo &inGeneralInfo,
const int inDaysAfterFeast) const override;
void
generateOctaveDayForApostles(const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo) const override;
private:
std::string m_name;
Days m_day;
HymnSeasons m_season;
bool m_romanUse;
const IEncapsulatedOfficeFormatter &m_formatter;
const IPsalter &m_psalter;
const IHymnSource &m_hymns;
bool m_isPriest;
void generateTerce(const IHoursInfo &inInfo,
const ITerceInfo &inTerceInfo) const;
void generateSext(const IHoursInfo &inInfo,
const ISextInfo &inSextInfo) const;
void generateNone(const IHoursInfo &inInfo,
const INoneInfo &inNoneInfo) const;
void generatePrime(const IHoursInfo &inInfo,
const IPrimeInfo &inPrimeInfo) const;
void generateVespers(const IHoursInfo &inInfo,
const IVespersInfo &inVespersInfo) const;
void generateLauds(const IHoursInfo &inInfo,
const ILaudsInfo &inLaudsInfo) const;
void generateCompline(const IHoursInfo &inInfo,
const IComplineInfo &inComplineInfo) const;
};
This has the general information available after the startup of a program is complete -- season, use, name of the day, plus the resources parsed in from disk -- and deals with the details of handling the output. Thus, most simply:
void FullOfficeGenerator::generateFromSingleSource(
const IOfficeInfoSource &inSource, const IHoursInfo &inGeneralInfo) const
{
if (inSource.isPartial())
throw OfficeGenerationException("generateFromSingleSource", "partial");
const IOfficeInfoHolder &info = inSource.getStandardInfo();
if (inSource.hasFirstVespers())
{
VespersBuilder vb(inGeneralInfo, m_day, m_name, true);
auto vi = vb.generateVespersInfo(m_psalter, info, m_season);
generateVespers(inGeneralInfo, *vi);
}
{
LaudsBuilder lb(m_psalter, inGeneralInfo, m_day, m_name);
auto li = lb.generateLaudsInfo(info, m_season);
generateLauds(inGeneralInfo, *li);
}
{
PrimeBuilder pb;
auto pi = pb.generatePrimeInfo(m_psalter, info, m_season);
generatePrime(inGeneralInfo, *pi);
}
{
MinorHourBuilder mhb;
{
auto ti = mhb.generateTerceInfo(info, m_season);
generateTerce(inGeneralInfo, *ti);
}
{
auto si = mhb.generateSextInfo(info, m_season);
generateSext(inGeneralInfo, *si);
}
{
auto ni = mhb.generateNoneInfo(info, m_season);
generateNone(inGeneralInfo, *ni);
}
}
{
VespersBuilder vb(inGeneralInfo, m_day, m_name, false);
generateVespers(inGeneralInfo,
*vb.generateVespersInfo(m_psalter, info, m_season));
}
{
if (!m_romanUse)
{
auto l = [&](const Compline::SpecialAntiphonDays inDays) {
Compline c(inDays);
c.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
};
if ((m_name == "Christmas Eve") && !m_romanUse)
{
l(Compline::SpecialAntiphonDays::CHRISTMAS_EVE);
return;
}
else if ((m_name == "Epiphany") && !m_romanUse)
{
l(Compline::SpecialAntiphonDays::EPIPHANY);
return;
}
else if ((m_name == "Purification of the Virgin") && !m_romanUse)
{
l(Compline::SpecialAntiphonDays::CANDLEMAS);
return;
}
else if ((m_name == "Holy Name of Jesus") && !m_romanUse)
{
l(Compline::SpecialAntiphonDays::HOLY_NAME);
return;
}
}
generateCompline(inGeneralInfo, *ComplineBuilder().generateComplineInfo(
info, m_season, m_romanUse));
}
}
There are a number of particular cases which are handled:
A general two-source case:
void FullOfficeGenerator::generateFromCombinedSources(
const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource, const IHoursInfo &inGeneralInfo,
const std::string &inCollect, const int inDayOfMonth) const
{
if (inBaseSource.isPartial())
throw OfficeGenerationException("generateFromCombinedSources", "partial");
if (!inDerivedSource.isPartial())
throw OfficeGenerationException("generateFromCombinedSources", "general");
const IHoursInfo *hiptr = &inGeneralInfo;
std::unique_ptr<IHoursInfo> tempHoursInfo;
if (!inCollect.empty() && inGeneralInfo.getCollect().empty())
{
tempHoursInfo = std::make_unique<DelegatedHoursInfoWithCollect>(
inGeneralInfo, inCollect);
hiptr = tempHoursInfo.get();
}
const IOfficeInfoHolder &info = inBaseSource.getStandardInfo();
const IPartialOfficeInfo &partialInfo = inDerivedSource.getPartialInfo();
if (inDerivedSource.hasFirstVespers())
{
VespersBuilder vb(*hiptr, m_day, m_name, true);
auto vi = vb.generateVespersInfo(m_psalter, partialInfo, info, m_season,
inDayOfMonth);
generateVespers(*hiptr, *vi);
}
{
LaudsBuilder lb(m_psalter, *hiptr, m_day, m_name);
auto li = lb.generateLaudsInfo(partialInfo, info, m_season);
generateLauds(*hiptr, *li);
}
{
PrimeBuilder pb;
auto pi
= pb.generatePrimeInfo(m_psalter, partialInfo, info, m_season, m_day);
generatePrime(*hiptr, *pi);
}
{
MinorHourBuilder mhb;
{
auto ti = mhb.generateTerceInfo(partialInfo, info, m_season);
generateTerce(*hiptr, *ti);
}
{
auto si = mhb.generateSextInfo(partialInfo, info, m_season);
generateSext(*hiptr, *si);
}
{
auto ni = mhb.generateNoneInfo(partialInfo, info, m_season);
generateNone(*hiptr, *ni);
}
}
{
VespersBuilder vb(*hiptr, m_day, m_name, false);
auto vi = vb.generateVespersInfo(m_psalter, partialInfo, info, m_season,
inDayOfMonth);
generateVespers(*hiptr, *vi);
}
{
ComplineBuilder cb;
auto ci = cb.generateComplineInfo(info, m_season, m_romanUse);
generateCompline(*hiptr, *ci);
}
}
A two-source version with an override of the Collect and/or antiphons
void FullOfficeGenerator::generateFromCombinedSourcesWithVariantAntiphons(
const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource, const IHoursInfo &inGeneralInfo,
const std::string &inCollect, const int inDayOfMonth,
const std::pair<std::string, std::string> &inAntiphons) const
{
if (inBaseSource.isPartial())
throw OfficeGenerationException(
"generateFromCombinedSourcesWithVariantAntiphons", "partial");
if (!inDerivedSource.isPartial())
throw OfficeGenerationException(
"generateFromCombinedSourcesWithVariantAntiphons", "general");
const IHoursInfo *hiptr = &inGeneralInfo;
std::unique_ptr<IHoursInfo> tempHoursInfo;
if (!inCollect.empty() && inGeneralInfo.getCollect().empty())
{
tempHoursInfo = std::make_unique<DelegatedHoursInfoWithCollect>(
inGeneralInfo, inCollect);
hiptr = tempHoursInfo.get();
}
const IOfficeInfoHolder &info = inBaseSource.getStandardInfo();
const IPartialOfficeInfo &partialInfo = inDerivedSource.getPartialInfo();
if (inDerivedSource.hasFirstVespers())
{
VespersBuilder vb(*hiptr, m_day, m_name, true);
auto vi = vb.generateVespersInfo(m_psalter, partialInfo, info, m_season,
inDayOfMonth);
generateVespers(*hiptr, *vi);
}
{
LaudsBuilder lb(m_psalter, *hiptr, m_day, m_name);
auto li
= lb.generateLaudsInfo(partialInfo, info, m_season, inAntiphons.first);
generateLauds(*hiptr, *li);
}
{
PrimeBuilder pb;
auto pi
= pb.generatePrimeInfo(m_psalter, partialInfo, info, m_season, m_day);
generatePrime(*hiptr, *pi);
}
{
MinorHourBuilder mhb;
{
auto ti = mhb.generateTerceInfo(partialInfo, info, m_season);
generateTerce(*hiptr, *ti);
}
{
auto si = mhb.generateSextInfo(partialInfo, info, m_season);
generateSext(*hiptr, *si);
}
{
auto ni = mhb.generateNoneInfo(partialInfo, info, m_season);
generateNone(*hiptr, *ni);
}
}
{
VespersBuilder vb(*hiptr, m_day, m_name, false);
auto vi = vb.generateVespersInfo(m_psalter, partialInfo, info, m_season,
inDayOfMonth, inAntiphons.second);
generateVespers(*hiptr, *vi);
}
{
ComplineBuilder cb;
auto ci = cb.generateComplineInfo(info, m_season, m_romanUse);
generateCompline(*hiptr, *ci);
}
}
A version for Holy Week and Easter Week:
void FullOfficeGenerator::generateForSpecialDay(
const SpecialDays inSpecialDay) const
{
{
Lauds l(m_psalter, inSpecialDay, m_day);
l.formatOffice(m_formatter, m_isPriest, m_hymns);
}
{
Prime p(m_psalter, inSpecialDay);
p.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
{
Terce t(m_psalter, inSpecialDay);
t.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
{
Sext s(m_psalter, inSpecialDay);
s.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
{
None n(m_psalter, inSpecialDay);
n.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
{
Vespers v(inSpecialDay, m_day);
v.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
{
Compline c(inSpecialDay, m_psalter);
c.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
}
A version for Octave Days, which requires adjusting the psalm antiphons:
void FullOfficeGenerator::generateOctaveDay(
const IOfficeInfoSource &inSource, const IHoursInfo &inGeneralInfo) const
{
if (inSource.isPartial())
throw OfficeGenerationException("generateOctaveDay", "partial");
DelegatedHoursInfoWithGrade hi(inGeneralInfo, Grades::OCTAVE_DAY);
const IOfficeInfoHolder &info = inSource.getStandardInfo();
if (inSource.hasFirstVespers())
{
VespersBuilder vb(hi, m_day, m_name, true);
auto vi = vb.generateVespersInfo(m_psalter, info, m_season);
vi->reduceToOneAntiphon();
generateVespers(hi, *vi);
}
{
LaudsBuilder lb(m_psalter, hi, m_day, m_name);
auto li = lb.generateLaudsInfo(info, m_season);
generateLauds(hi, *li);
}
{
PrimeBuilder pb;
auto pi = pb.generatePrimeInfo(m_psalter, info, m_season);
generatePrime(hi, *pi);
}
{
MinorHourBuilder mhb;
{
auto ti = mhb.generateTerceInfo(info, m_season);
generateTerce(hi, *ti);
}
{
auto si = mhb.generateSextInfo(info, m_season);
generateSext(hi, *si);
}
{
auto ni = mhb.generateNoneInfo(info, m_season);
generateNone(hi, *ni);
}
}
{
VespersBuilder vb(hi, m_day, m_name, false);
auto vi = vb.generateVespersInfo(m_psalter, info, m_season);
vi->clearChapterResponses();
generateVespers(hi, *vi);
}
if (!m_romanUse && (m_name.find("Epiphany") != std::string::npos))
{
Compline c(Compline::SpecialAntiphonDays::EPIPHANY);
c.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
else
{
ComplineBuilder cb;
auto ci = cb.generateComplineInfo(info, m_season, m_romanUse);
generateCompline(hi, *ci);
}
}
A version for Apostles' octave days, following Sarum use (Roman use did not have lesser octaves for apostles):
void FullOfficeGenerator::generateOctaveDayForApostles(
const IOfficeInfoSource &inBaseSource,
const IOfficeInfoSource &inDerivedSource,
const IHoursInfo &inGeneralInfo) const
{
if (inBaseSource.isPartial())
throw OfficeGenerationException("generateOctaveDayForApostles", "partial");
if (!inDerivedSource.isPartial())
throw OfficeGenerationException("generateOctaveDayForApostles", "full");
const IOfficeInfoHolder &info = inBaseSource.getStandardInfo();
const IPartialOfficeInfo &partialInfo = inDerivedSource.getPartialInfo();
DelegatedHoursInfoWithGrade hi(inGeneralInfo, Grades::OCTAVE_DAY);
if (inDerivedSource.hasFirstVespers())
{
VespersBuilder vb(hi, m_day, m_name, true);
auto vi
= vb.generateVespersInfo(m_psalter, partialInfo, info, m_season,
1); // Not advent, can put in dummy value
vi->reduceToOneAntiphon();
generateVespers(hi, *vi);
}
{
LaudsBuilder lb(m_psalter, hi, m_day, m_name);
auto li = lb.generateLaudsInfo(partialInfo, info, m_season);
generateLauds(hi, *li);
}
{
PrimeBuilder pb;
auto pi
= pb.generatePrimeInfo(m_psalter, partialInfo, info, m_season, m_day);
generatePrime(hi, *pi);
}
{
MinorHourBuilder mhb;
{
auto ti = mhb.generateTerceInfo(partialInfo, info, m_season);
generateTerce(hi, *ti);
}
{
auto si = mhb.generateSextInfo(partialInfo, info, m_season);
generateSext(hi, *si);
}
{
auto ni = mhb.generateNoneInfo(partialInfo, info, m_season);
generateNone(hi, *ni);
}
}
{
VespersBuilder vb(hi, m_day, m_name, false);
auto vi
= vb.generateVespersInfo(m_psalter, partialInfo, info, m_season, 1);
vi->clearChapterResponses();
generateVespers(hi, *vi);
}
{
ComplineBuilder cb;
auto ci = cb.generateComplineInfo(info, m_season, m_romanUse);
generateCompline(hi, *ci);
}
}
A version for generating a feria in a greater octave, which not overrides the canticle antiphons as well:
void FullOfficeGenerator::generateDayInGreaterOctave(
const IOfficeInfoSource &inSource, const IHoursInfo &inGeneralInfo,
const int inDaysAfterFeast) const
{
if (inDaysAfterFeast == 7)
generateOctaveDay(inSource, inGeneralInfo);
else if (inDaysAfterFeast < 1)
return;
else
{
if (inSource.isPartial())
throw OfficeGenerationException("generateDayInGreaterOctave",
"partial");
const IOfficeInfoHolder &info = inSource.getStandardInfo();
DelegatedHoursInfoWithGrade hi(inGeneralInfo, Grades::GREATER_OCTAVE);
std::string benAnt;
std::string magAnt;
{
VespersBuilder vb(hi, m_day, m_name, true);
auto vi = vb.generateVespersInfo(m_psalter, info, m_season);
VespersAntiphons va = vi->generateAntiphons();
std::vector<std::string> vec;
va.getAntiphons(vec);
auto l = [&](const int inBen, const int inMag) {
benAnt = vec[inBen];
magAnt = vec[inMag];
};
if (vec.size() == 5)
{
switch (inDaysAfterFeast)
{
case 1:
l(0, 1);
break;
case 2:
l(2, 3);
break;
case 3:
l(4, 0);
break;
case 4:
l(1, 2);
break;
case 5:
l(3, 4);
break;
case 6:
l(0, 1);
break;
default:
break;
}
}
}
{
LaudsBuilder lb(m_psalter, hi, m_day, m_name);
auto li = lb.generateLaudsInfo(info, m_season);
if (!benAnt.empty())
li->resetBenedictusAntiphon(benAnt);
generateLauds(hi, *li);
}
{
PrimeBuilder pb;
auto pi = pb.generatePrimeInfo(m_psalter, info, m_season);
generatePrime(hi, *pi);
}
{
MinorHourBuilder mhb;
{
auto ti = mhb.generateTerceInfo(info, m_season);
generateTerce(hi, *ti);
}
{
auto si = mhb.generateSextInfo(info, m_season);
generateSext(hi, *si);
}
{
auto ni = mhb.generateNoneInfo(info, m_season);
generateNone(hi, *ni);
}
}
{
VespersBuilder vb(hi, m_day, m_name, false);
auto vi = vb.generateVespersInfo(m_psalter, info, m_season);
if (!magAnt.empty())
vi->resetMagnificatAntiphon(magAnt);
vi->clearChapterResponses();
generateVespers(hi, *vi);
}
if (!m_romanUse && (m_name.find("Epiphany") != std::string::npos))
{
Compline c(Compline::SpecialAntiphonDays::EPIPHANY);
c.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
else
{
ComplineBuilder cb;
auto ci = cb.generateComplineInfo(info, m_season, m_romanUse);
generateCompline(hi, *ci);
}
}
}
All of these functions, after all the commonalities have been factored out (and even local lambdas, in some cases, have been used to encapsulate small-scale repetitions) are just big bundles of special-case logic. There's not much that can be done to reduce that type of complexity by any form of design.
The other private functions are short definitions which clean up the syntax of related small blocks of logic, e.g.:
void FullOfficeGenerator::generateTerce(const IHoursInfo &inInfo,
const ITerceInfo &inTerceInfo) const
{
Terce t(m_psalter, inInfo, inTerceInfo);
t.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
void FullOfficeGenerator::generateCompline(
const IHoursInfo &inInfo, const IComplineInfo &inComplineInfo) const
{
Compline c(inInfo, inComplineInfo);
c.formatOffice(m_formatter, m_psalter, m_isPriest, m_hymns);
}
Comments
Post a Comment