Vespers Element
There are two types of Vespers: Second Vespers, on the day itself, and First Vespers, which can occur on the evening before a feast (and would displace the previous day's Second Vespers):
<Vespers type="First">
<VespersAntiphons ref="SUNDAY"/>
<psalms ref="SUNDAY"/>
<chapter src="Isa. ii">it shall come to pass in the last days, that the mountain of the Lord's house shall be established in the top of the mountains, and shall be exalted above the hills; and all nations shall flow unto it.</chapter>
<ChapterResponses>
<versicle>In His days Judah shall be saved, and Israel shall dwell safely,</versicle>
<response>Behold, the days come, saith the Lord, that I will raise unto David a righteous branch, and a King shall reign and prosper, and shall execute judgment and justice in the earth * And this is his Name whereby He shall be called : The Lord our Righteousness</response>
</ChapterResponses>
<hymns>
<hymn name="Conditor alme siderum"/>
</hymns>
<HymnResponses>
<versicle>Drop down, ye heavens, from above.</versicle>
<response>And let the skies pour down righteousness : let the earth open, and let them bring forth a Saviour.</response>
</HymnResponses>
<MagnificatAntiphon>Behold, the Name of the Lord cometh from far : and his glory filleth the whole earth.</MagnificatAntiphon>
</Vespers>
...
<Vespers type="Second">
<VespersAntiphons ref="Lauds"/>
<psalms ref="SUNDAY"/>
<chapter ref="Lauds"/>
<ChapterResponses>
<versicle>For it is time that Thou have mercy upon her, yea, the time is come.</versicle>
<response>Thou shalt arise, O Lord * And have mercy upon Sion.</response>
</ChapterResponses>
<hymns>
<hymn name="Conditor alme siderum"/>
</hymns>
<HymnResponses>
<versicle>Drop down, ye heavens, from above.</versicle>
<response>And let the skies pour down righteousness : let the earth open, and let them bring forth a Saviour.</response>
</HymnResponses>
<MagnificatAntiphon>Fear not, Mary: thou hast found favour with God : behold, thou shalt conceive, and bring forth a Son, alleluia.</MagnificatAntiphon>
</Vespers>
class VespersElement : public MultiElementElement
{
public:
// First Vespers
VespersElement(const Use inUse, const Days inDays, std::string_view inText,
const HymnSeasons inSeason);
// Second Vespers
VespersElement(const Use inUse, const ILaudsChapter &inLaudsChapter,
const ILaudsAntiphons &inLaudsAntiphons, const Days inDays,
std::string_view inText, const HymnSeasons inSeason);
~VespersElement() override;
PartialOfficeHourInfo getPartialOfficeInfo() const;
std::unique_ptr<IVespersInfo> generateVespersInfo() const;
bool hasChapter() const { return m_hasChapter; }
bool hasPsalmAntiphons() const { return m_hasPsalmAntiphons; }
bool hasMagnificatAntiphon() const { return m_hasMagnificatAntiphon; }
private:
const std::string &getStartTagName() const override
{
return m_tag.getName();
}
VespersTag m_tag;
std::string m_chapterText;
std::string m_chapterSrc;
std::string m_magnificatAntiphon;
std::string m_hymn;
mutable std::vector<std::string> m_antiphons;
mutable std::vector<std::string> m_chapterResponsory;
mutable std::vector<std::string> m_hymnResponsory;
std::unique_ptr<VespersPsalmSpec> m_psalmSpec;
bool m_hasChapter = false;
bool m_hasPsalmAntiphons = false;
bool m_hasMagnificatAntiphon = false;
std::string_view::size_type extractPsalms(std::string_view inText,
const Days inDays,
const bool inRomanUse);
std::string_view::size_type extractHymns(std::string_view inText,
const Use inUse);
std::string_view::size_type extractHymnResponses(std::string_view inText);
std::string_view::size_type
extractHymnResponsesWithLauds(std::string_view inText,
const ILaudsChapter &inLaudsChapter);
std::string_view::size_type extractChapterResponses(std::string_view inText);
std::string_view::size_type
extractMagnificatAntiphon(std::string_view inText,
const HymnSeasons inSeason);
void handleEnd(std::string_view inText);
void doParse(std::string_view inText,
std::function<std::string_view(std::string_view)> inParseLogic,
const HymnSeasons inSeason);
void checkForRequiredChapterElement(const std::string &inName,
std::string_view inText) const;
void doBasicChapterParse(std::string_view &inText);
};
The VespersTag has a very simple model, with just one attribute:
class VespersTag : public BreviaryTag
{
public:
enum class Type {
First,
Second
};
VespersTag(): BreviaryTag(GetTagName()) { }
~VespersTag() override;
Type getType() const { return m_type; }
static std::string GetTagName() { return "Vespers"; }
private:
int
allowedAttributeCount() const override
{
return 1;
}
std::span<std::string> getAllowedAttributes() const override;
bool validate(std::string_view inAttribute,
std::string_view inValue) const override;
void setValue(std::string_view inAttribute,
std::string_view inValue) override;
bool checkMandatoryAttributes() const override { return hasAttribute("type"); }
Type m_type = Type::Second;
};
std::span<std::string>
VespersTag::getAllowedAttributes() const
{
static std::array<std::string, 1> rval{ "type" };
return rval;
}
bool VespersTag::validate(std::string_view inAttribute,
std::string_view inValue) const
{
return (inValue == "First") || (inValue == "Second");
}
void VespersTag::setValue(std::string_view inAttribute, std::string_view inValue)
{
m_type = (inValue == "First") ? Type::First : Type::Second;
}
generateVespersInfo() and getPartialOfficeInfo() are very similar to the Lauds code:
std::unique_ptr<IVespersInfo> VespersElement::generateVespersInfo() const
{
class VespersInfo : public IVespersInfo
{
public:
VespersInfo(const VespersPsalmSpec &inPsalmSpec,
std::span<std::string> inAntiphons,
std::span<std::string> inChapterResponses,
std::span<std::string> inHymnResponses,
const std::string &inChapterText,
const std::string &inChapterSrc, const std::string &inHymn,
const std::string &inMagnificatAntiphon):
m_psalmSpec(inPsalmSpec),
m_antiphons(inAntiphons.begin(), inAntiphons.end()),
m_chapterResponses(inChapterResponses.begin(),
inChapterResponses.end()),
m_hymnResponses(inHymnResponses.begin(), inHymnResponses.end()),
m_chapterText(inChapterText), m_chapterSrc(inChapterSrc),
m_hymn(inHymn), m_magnificatAntiphon(inMagnificatAntiphon)
{
}
~VespersInfo() override {}
VespersPsalmSpec generatePsalmSpec() const override { return m_psalmSpec; }
VespersAntiphons generateAntiphons() const override
{
return VespersAntiphons{ m_antiphons,
"VespersElement::generateAntiphons()" };
}
VespersChapter generateChapter() const override
{
return VespersChapter{ m_chapterText, m_chapterSrc, m_hymnResponses,
m_hymn, m_chapterResponses };
}
MagnificatAntiphons generateMagnificatAntiphons() const override
{
return MagnificatAntiphons{ m_magnificatAntiphon };
}
std::unique_ptr<IVespersInfo> clone() const override
{
return std::make_unique<VespersInfo>(*this);
}
void reduceToOneAntiphon() override
{
if (m_antiphons.size() > 1)
{
m_antiphons.resize(1);
}
}
void clearChapterResponses() override { m_chapterResponses.clear(); }
void resetMagnificatAntiphon(const std::string &inVal) override
{
m_magnificatAntiphon = inVal;
}
private:
VespersPsalmSpec m_psalmSpec;
mutable std::vector<std::string> m_antiphons;
mutable std::vector<std::string> m_chapterResponses;
mutable std::vector<std::string> m_hymnResponses;
std::string m_chapterText;
std::string m_chapterSrc;
std::string m_hymn;
std::string m_magnificatAntiphon;
};
return std::make_unique<VespersInfo>(
*m_psalmSpec, m_antiphons, m_chapterResponsory, m_hymnResponsory,
m_chapterText, m_chapterSrc, m_hymn, m_magnificatAntiphon);
}
PartialOfficeHourInfo VespersElement::getPartialOfficeInfo() const
{
return PartialOfficeHourInfo{
m_chapterText, m_chapterSrc, m_hymn, ""s,
m_magnificatAntiphon, m_chapterResponsory, m_hymnResponsory, m_antiphons
};
}
The two constructors encapsulate their variant logic in a lambda which captures the differing parameters for the two constructors.
VespersElement::VespersElement(const Use inUse, const Days inDays,
std::string_view inText,
const HymnSeasons inSeason):
MultiElementElement(inText, VespersTag::GetTagName()),
m_psalmSpec(
std::make_unique<VespersPsalmSpec>(inDays, (inUse != Use::Anglican)))
{
auto l = [&](std::string_view inRest) -> std::string_view {
if (inRest.starts_with("<VespersAnt"))
{
VespersAntiphonsElement element(inRest, inDays);
inRest.remove_prefix(incrementLength(element.getLength()));
m_antiphons = element.getAntiphons();
adjustBetweenTags(inRest);
m_hasPsalmAntiphons = true;
}
if (inRest.starts_with("<psalms"))
{
inRest.remove_prefix(
extractPsalms(inRest, inDays, (inUse != Use::Anglican)));
adjustBetweenTags(inRest);
}
if (inRest.starts_with("<chapter")) // chapter, possible chapter responses,
// hymns, HymnResponses
{
{
doBasicChapterParse(inRest);
adjustBetweenTags(inRest);
}
if (inRest.starts_with("<ChapterResponses"))
{
inRest.remove_prefix(extractChapterResponses(inRest));
adjustBetweenTags(inRest);
}
m_hasChapter = true;
}
if (inRest.starts_with("<hymns"))
{
inRest.remove_prefix(extractHymns(inRest, inUse));
adjustBetweenTags(inRest);
if (inRest.starts_with("<HymnR"))
{
inRest.remove_prefix(extractHymnResponses(inRest));
adjustBetweenTags(inRest);
}
}
return inRest;
};
doParse(inText, l, inSeason);
}
VespersElement::VespersElement(const Use inUse,
const ILaudsChapter &inLaudsChapter,
const ILaudsAntiphons &inLaudsAntiphons,
const Days inDays, std::string_view inText,
const HymnSeasons inSeason):
MultiElementElement(inText, VespersTag::GetTagName()),
m_psalmSpec(
std::make_unique<VespersPsalmSpec>(inDays, (inUse != Use::Anglican)))
{
auto l = [&](std::string_view inRest) -> std::string_view {
if (inRest.starts_with("<VespersAnt"))
{
VespersAntiphonsElement element(inLaudsAntiphons, inRest, inDays);
inRest.remove_prefix(incrementLength(element.getLength()));
m_antiphons = element.getAntiphons();
adjustBetweenTags(inRest);
m_hasPsalmAntiphons = true;
}
if (inRest.starts_with("<psalms"))
{
inRest.remove_prefix(
extractPsalms(inRest, inDays, (inUse != Use::Anglican)));
adjustBetweenTags(inRest);
}
if (inRest.starts_with("<chapter")) // chapter, possible chapter responses,
// hymns, HymnResponses
{
if (inLaudsChapter.getChapterText().empty())
{
doBasicChapterParse(inRest);
}
else
{
ChapterElement element(inLaudsChapter, inRest,
OfficeNames::VESPERS);
inRest.remove_prefix(incrementLength(element.getLength()));
m_chapterSrc = element.getSrc();
m_chapterText = element.getText();
}
adjustBetweenTags(inRest);
if (inRest.starts_with("<ChapterResponses"))
{
inRest.remove_prefix(extractChapterResponses(inRest));
adjustBetweenTags(inRest);
}
m_hasChapter = true;
}
if (inRest.starts_with("<hymns"))
{
inRest.remove_prefix(extractHymns(inRest, inUse));
adjustBetweenTags(inRest);
if (inRest.starts_with("<HymnR"))
{
inRest.remove_prefix(
extractHymnResponsesWithLauds(inRest, inLaudsChapter));
adjustBetweenTags(inRest);
}
}
return inRest;
};
doParse(inText, l, inSeason);
}
This lambda is then called from the doParse() function, which aloo executes common logic at the beginning and end of the parsing:
void VespersElement::doParse(
std::string_view inText,
std::function<std::string_view(std::string_view)> inParseLogic,
const HymnSeasons inSeason)
{
auto l = [&](const VespersTag &inVal, const int inAttributes) {
if (inVal.isClosed())
throw OfficeParseException("Cannot have a closed " + inVal.getName()
+ " element");
std::string_view rest(inText.substr(getLength()));
adjustBetweenTags(rest);
rest = inParseLogic(rest);
if (rest.starts_with("<Magnificat"))
{
rest.remove_prefix(extractMagnificatAntiphon(rest, inSeason));
adjustBetweenTags(rest);
}
handleEnd(rest);
};
TagSetter().set<VespersTag>(m_tag, l, inText.substr(0, getLength()));
}
This is the sort of solution to common code which would be impractical in C++03. (As always, you could do the same thing with abstract interfaces and local classes, but the syntactic overhead would bury the logic in implementation details. With lambdas, there is very little to detract from the logic itself.)
The other functions are simple encapsulations of a kind that by now should be familiar, e.g.:
std::string_view::size_type
VespersElement::extractPsalms(std::string_view inText, const Days inDays,
const bool inRomanUse)
{
PsalmsElement element(inText, inDays, inRomanUse);
if (const std::string &namedException = element.getNamedException(); !namedException.empty())
m_psalmSpec = std::make_unique<VespersPsalmSpec>(namedException);
else
{
const auto &spec = element.getPsalms();
if (spec.size() == 5)
{
m_psalmSpec = std::make_unique<VespersPsalmSpec>(spec);
}
}
return incrementLength(element.getLength());
}
This is where we ensure that the named exception version of the psalm spec always takes precedence over anything else.
A couple of other examples, one calling the other:
void VespersElement::checkForRequiredChapterElement(
const std::string &inName, std::string_view inText) const
{
if (!inText.starts_with("<"s + inName))
throw OfficeParseException("Expected "s + inName
+ " element in a chapter sequence in a "s
+ m_tag.getName() + " element"s,
inText);
}
std::string_view::size_type
VespersElement::extractHymns(std::string_view inText, const Use inUse)
{
checkForRequiredChapterElement("hymns", inText);
HymnsElement element(inUse, inText);
m_hymn = element.getHymn();
return incrementLength(element.getLength());
}
checkForRequiredChapterElement() is called in other functions as well, but they replicate the general pattern of creating an element object and then capturing the values in the VespersElement members and will not be dragged out here.
Comments
Post a Comment