Major Hours Sub-Elements

 Before we move on to the Lauds and Vespers elements, there are some sub-elements which they use which were not used in the minor hour elements.

Gospel Canticles

The Benedictus and Magnificat antiphons have two elements which are identical excpt in their names.

      <BenedictusAntiphon>The Angel of the Lord brought tidings unto Mary : and she conceived of the Holy Ghost, alleluia.</BenedictusAntiphon>

      <MagnificatAntiphon>O Jerusalem, look about thee toward the east : and behold, alleluia.</MagnificatAntiphon>

The only thing that makes these vary is that some feasts may occur in or out of Eastertide, and have an alleuia appended in that case:

      <MagnificatAntiphon paschalAlleluia="true">In the regeneration when the Son of Man shall sit in the throne of His glory : ye also shall sit upon twelve thrones judging the twelve tribes of Israel, saith the Lord.</MagnificatAntiphon>

As the logic for these canticles is exactly the same, it can be put in one base class:

class CanticleAntiphonElement : public TextElementBase

{

protected:

  CanticleAntiphonElement(std::string_view inText, const HymnSeasons inSeason,

                          const std::string &inName);

public:

  ~CanticleAntiphonElement() override;

  const std::string &getStartTagName() const override

  {

    return m_tag.getName();

  }

protected:

  CanticleAntiphonTag m_tag;

};

CanticleAntiphonElement::CanticleAntiphonElement(std::string_view inText,

                                                 const HymnSeasons inSeason,

                                                 const std::string &inName):

    TextElementBase(inText, inName + "Antiphon"),

    m_tag(inName)

{

  auto l = [&](const CanticleAntiphonTag &inVal, const int inAttributes) {

    std::string_view body(inText.substr(getLength()));

    setTextFromBody(body);

    body.remove_prefix(incrementLength(m_text.length()));

    if (m_tag.addAlleluiaInEaster()

        && ((inSeason == HymnSeasons::EASTER)

            || (inSeason == HymnSeasons::ASCENSION)))

      {

        if (m_text.ends_with("."))

          m_text.erase(m_text.length() - 1);

        m_text.append(", alleluia.");

      }

    incrementLength(checkEndTag(body));

  };

  TagSetter().set<CanticleAntiphonTag>(m_tag, l,

                                       inText.substr(0, getLength()));

}

The common tag is also straightforward, with no mandatory and one optional attribute:

class CanticleAntiphonTag : public BreviaryTag

{

public:

  explicit CanticleAntiphonTag(const std::string &inCanticleName):

      BreviaryTag(inCanticleName + "Antiphon")

  { }

  ~CanticleAntiphonTag() override;

  bool addAlleluiaInEaster() const { return m_addAlleluiaInEaster; }

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 true; }

  bool m_addAlleluiaInEaster = false;

};

std::span<std::string>

CanticleAntiphonTag::getAllowedAttributes() const

{

  static std::array<std::string, 1> rval{ "paschalAlleluia" };

  return rval;

}

bool CanticleAntiphonTag::validate(std::string_view inAttribute,

                              std::string_view inValue) const

{

  return (inValue == "true"sv) || (inValue == "false"sv);

}

void CanticleAntiphonTag::setValue(std::string_view inAttribute,

                              std::string_view inValue)

{

  m_addAlleluiaInEaster = (inValue == "true"sv);

}

And the two leaf elements differ only in their names, e.g.:

class BenedictusAntiphonElement : public CanticleAntiphonElement

{

public:

  BenedictusAntiphonElement(std::string_view inText,

                            const HymnSeasons inSeason):

      CanticleAntiphonElement(inText, inSeason, s_CanticleName)

  { }

  ~BenedictusAntiphonElement() override;

private:

  const inline static std::string s_CanticleName{ "Benedictus" };

};

Major Hours Psalm Antiphons

Both Lauds and Vespers have sets of (potentially) five psalm antiphons.  In both cases, though for reasons expressed differently, they can have only one.  In the Lauds case antiphons may be marked as being  used for the other hours, and in the case of Second Vespers the antiphon may be taken from Lauds; either may be specifiesd to be taken from the day of the week.

Thus, normally:

      <LaudsAntiphons usedForHours="Y">

<antiphon number="1">In that day the mountains shall drop down new wine : and the hills shall flow with milk and honey, alleluia.</antiphon>

<antiphon number="2">Rejoice greatly, O daughter of Sion : shout, O daughter of Jerusalem, alleluia.</antiphon>

<antiphon number="3">Behold, the Lord shall come, and all His Saints with Him : and there shall be in that day a great light. Alleluia,</antiphon>

<antiphon number="4">Ho, every one that thirsteth, come ye to the waters : seek ye the Lord, while He may be found, alleluia.</antiphon>

<antiphon number="5">Behold, a great Prophet cometh : and He shall renew Jerusalem, alleluia.</antiphon>

      </LaudsAntiphons>

or

      <LaudsAntiphons>

<antiphon number="1">Behold, our Lord shall come with power : and Himself shall break the yoke of our captivity.</antiphon>

<antiphon number="2">Send, Lord, the Lamb, the Ruler of the land : from the rock of the wilderness unto the mount of the daughter of Sion.</antiphon>

<antiphon number="3">That Thy way, O Lord, may be known upon earth : Thy saving health among all nations.</antiphon> 

<antiphon number="4">Reward them, O Lord, that wait for Thee : and let Thy prophets be found faithful.</antiphon>

<antiphon number="5">The law was given by Moses : but grace and truth came by Jesus Christ.</antiphon>

      </LaudsAntiphons>


      <VespersAntiphons count="5">

<antiphon number="1">Christ the Lord, a Priest for ever after the order of Melchisedec : offered bread and wine.</antiphon>

<antiphon number="2">The merciful Lord hath given meat to them that fear Him: in remembrance of His marvellous works.</antiphon>

<antiphon number="3">I will receive the cup of salvation: and offer the sacrifice of thanksgiving.</antiphon>

<antiphon number="4">May the children of the church be like the olive-branches: round about the table of the Lord.</antiphon>

<antiphon number="5">The Lord who maketh peace in the borders of the church: filleth us with the flour of wheat.</antiphon>

      </VespersAntiphons>

but also:

      <LaudsAntiphons ref="MONDAY"/>


      <VespersAntiphons ref="SUNDAY"/>


      <VespersAntiphons ref="Lauds"/>


      <VespersAntiphons count="1">

<antiphon number="1">Be ye strong in battle, and fight with the old serpent : so shall ye receive an eternal kingdom, saith the Lord.</antiphon>

      </VespersAntiphons>

These have a good deal of commonality which can be factored out into a parent class:

class MajorHoursAntiphonsElement : public MultiElementElement

{

protected:

  MajorHoursAntiphonsElement(std::string_view inText,

                             const std::string &inName):

      MultiElementElement(inText, inName)

  { }

public:

  ~MajorHoursAntiphonsElement() override;

protected:

  int extractNumberedAntiphon(std::string_view &inText, const int inVal)

  {

    checkAntiphonTagBeginning(inText);

    inText.remove_prefix(extractAntiphon(inText, inVal));

    adjustBetweenTags(inText);

    return inVal + 1;

  }

  virtual void addAntiphon(const std::string &inVal, const int inIndex) = 0;

    void checkEnd(std::string_view inText);

private:

  std::string_view::size_type extractAntiphon(std::string_view inStr,

                                              const int inExpectedNumber);

  void checkAntiphonTagBeginning(std::string_view inText) const;

};

These are essentially all helper functions for simplifying the code in the child classes.

std::string_view::size_type

MajorHoursAntiphonsElement::extractAntiphon(std::string_view inStr,

                                            const int inExpectedNumber)

{

  AntiphonElement elem(inStr);

  if (elem.getNumber() != inExpectedNumber)

    throw OfficeParseException(

        getStartTagName() + " unexpected number: "

        + boost::lexical_cast<std::string>(elem.getNumber()) + "; should be "

        + boost::lexical_cast<std::string>(inExpectedNumber));

  addAntiphon(elem.getText(), inExpectedNumber - 1);

  return incrementLength(elem.getLength());

}

void MajorHoursAntiphonsElement::checkAntiphonTagBeginning(

    std::string_view inText) const

{

  const static auto expectedLength = AntiphonTag::GetTagName().length() + 2;

  if (inText.length() < expectedLength)

    throw OfficeParseException(

        getStartTagName() + " unexpected element: " + std::string(inText));

}

void MajorHoursAntiphonsElement::checkEnd(std::string_view inText)

{

  if (!inText.starts_with(getEndTag()))

    throw OfficeParseException(

        getStartTagName() + "  with unexpected element before closing tag: "

        + std::string(inText.substr(0, 20)));

  incrementLength(getEndTag().length());

}

With this as a basis the Lauds class is as follows:

class LaudsAntiphonsElement : public MajorHoursAntiphonsElement

{

public:

  explicit LaudsAntiphonsElement(std::string_view inText, const Days inDay);

  ~LaudsAntiphonsElement() override;

  bool usedForHours() const { return m_tag.usedForHours(); }

  bool useOnlyFirstAntiphon() const { return m_tag.useOnlyFirstAntiphon(); }

  std::span<std::string> getAntiphons() { return m_antiphons; }

private:

  const std::string &getStartTagName() const override

  {

    return m_tag.getName();

  }

  void addAntiphon(const std::string& inVal, const int inIndex) override { m_antiphons[inIndex] = inVal; }

  LaudsAntiphonsTag m_tag;

  std::array<std::string, 5> m_antiphons;

};

Aside from the accessors, as is usual with the element classes, all the logic is in the single constructor:

LaudsAntiphonsElement::LaudsAntiphonsElement(std::string_view inText,

                                             const Days inDay):

    MajorHoursAntiphonsElement(inText, LaudsAntiphonsTag::GetTagName())

{

  auto l = [&](const LaudsAntiphonsTag &inVal, const int inAttributes) {

    if (inVal.isClosed())

      {

        if (inVal.isDayReference())

          {

            Days d = StringAsDay(inVal.getAttribute("ref"));

            if ((d != inDay) && (d != Days::SUNDAY))

              d = inDay;

            std::ranges::copy(LaudsAntiphonsGenerator::GenerateAntiphons(d),

                              m_antiphons.begin());

            return;

          }

        else

          throw OfficeParseException("Cannot have a reference "

                                     + getStartTagName()

                                     + " element for Lauds antiphons");

      }

    else

      {

        std::string_view rest(inText.substr(getLength()));

        adjustBetweenTags(rest);

        int count = extractNumberedAntiphon(rest, 1);

        if (!inVal.useOnlyFirstAntiphon())

          {

    count = extractNumberedAntiphon(rest, count);

    count = extractNumberedAntiphon(rest, count);

    count = extractNumberedAntiphon(rest, count);

    count = extractNumberedAntiphon(rest, count);

  }

checkEnd(rest);

      }

  };

  TagSetter().set<LaudsAntiphonsTag>(m_tag, l, inText.substr(0, getLength()));

}

The Vespers version has two constructors, one for FirstVespers (or when lauds antiphons have not beeen marked as for hours) and one for Second Vespers:

class VespersAntiphonsElement : public MajorHoursAntiphonsElement

{

public:

  VespersAntiphonsElement(std::string_view inText, const Days inDay);

  VespersAntiphonsElement(const ILaudsAntiphons &inLaudsAntiphons,

                          std::string_view inText, const Days inDay);

  const std::vector<std::string> &getAntiphons() const { return m_antiphons; }

private:

  const std::string &getStartTagName() const override

  {

    return m_tag.getName();

  }

  void extractAntiphons(std::string_view inText);

  void addAntiphon(const std::string &inVal, const int inIndex) override

  {

    m_antiphons.push_back(inVal);

  }

  void setAntiphonsForDay(const VespersAntiphonsTag &inVal, const Days inDay);

  VespersAntiphonsTag m_tag;

  std::vector<std::string> m_antiphons;

};

VespersAntiphonsElement::VespersAntiphonsElement(std::string_view inText,

                                                 const Days inDay):

    MajorHoursAntiphonsElement(inText, VespersAntiphonsTag::GetTagName())

{

  auto l = [&](const VespersAntiphonsTag &inVal, const int inAttributes) {

    if (inVal.isClosed() && !inVal.useFerialOrSundayValues())

      throw OfficeParseException("Cannot have a reference " + getStartTagName()

                                 + " element for first vespers antiphons");

    if (inVal.useFerialOrSundayValues() && inVal.isClosed())

      {

        setAntiphonsForDay(inVal, inDay);

        return;

      }

    extractAntiphons(inText);

  };

  TagSetter().set<VespersAntiphonsTag>(m_tag, l,

                                       inText.substr(0, getLength()));

}

VespersAntiphonsElement::VespersAntiphonsElement(

    const ILaudsAntiphons &inLaudsAntiphons, std::string_view inText,

    const Days inDay):

    MajorHoursAntiphonsElement(inText, VespersAntiphonsTag::GetTagName())

{

  auto l = [&](const VespersAntiphonsTag &inVal, const int inAttributes) {

    if (inVal.isClosed())

      {

        if (inVal.useFerialOrSundayValues())

          {

            setAntiphonsForDay(inVal, inDay);

          }

        else

          m_antiphons.push_back(

              inLaudsAntiphons.getAntiphon(OfficeNames::VESPERS));

        return;

      }

    extractAntiphons(inText);

  };

  TagSetter().set<VespersAntiphonsTag>(m_tag, l,

                                       inText.substr(0, getLength()));

}

Two areas of replicated logic are encapsulated in functions:

void VespersAntiphonsElement::setAntiphonsForDay(

    const VespersAntiphonsTag &inVal, const Days inDay)

{

  Days d = StringAsDay(inVal.getAttribute("ref"));

  if ((d != inDay) && (d != Days::SUNDAY))

    d = inDay;

  VespersAntiphons ant(d);

  ant.getAntiphons(m_antiphons);

}

void VespersAntiphonsElement::extractAntiphons(std::string_view inText)

{

  if ((m_tag.getCount() < 1) && !m_tag.useFerialOrSundayValues())

    throw OfficeParseException("Cannot have a " + getStartTagName()

                               + " element with no element count");

  std::string_view rest(inText.substr(getLength()));

  int count = extractNumberedAntiphon(rest, 1);

  if (m_tag.getCount() != 1) // count will be 5

    {

      count = extractNumberedAntiphon(rest, count);

      count = extractNumberedAntiphon(rest, count);

      count = extractNumberedAntiphon(rest, count);

      count = extractNumberedAntiphon(rest, count);

    }

  checkEnd(rest);

}

Comments

Popular posts from this blog

Boundaries

Code Reviews

Overview