Formatting and output

 I've been passing around a generic formatting interface but haven't said anything about it yet.

Here's the full interface:

class IEncapsulatedOfficeFormatter

{

public:

  virtual ~IEncapsulatedOfficeFormatter();

  virtual void formatAntiphon(const std::string& inAntiphon) const = 0;

  virtual void formatHeading(const std::string& inHeader,

                             const bool inItalic = false) const = 0;

  virtual void formatResponsory(const std::string &inVersicle,

                                const std::string &inResponse,

                                const bool inEasterResponsory = false,

                                const bool inPassiontideResponsory

                                = false) const = 0;

  virtual void formatCanticleVerse(const std::string &inVerse) const = 0;

  virtual void formatPsalmVerse(uint16_t inNumber,

                                std::string_view inVerse) const = 0;

  virtual void formatChapter(const std::string &inText,

                             const std::string &inSource) const = 0;

  virtual void formatCollect(const std::string &inText) const = 0;

  virtual void formatHymn(const std::string &inText,

                          const std::string &inTag) const = 0;

  virtual void formatGloriaPatri() const = 0;

  virtual void formatKyrie(const bool inThreefold = true) const = 0;

  virtual void formatPaterNoster() const = 0;

  virtual void formatDismissal(const bool inIsPriest) const = 0;

  virtual void formatCreed() const = 0;

  virtual void formatConfiteor() const = 0;

  virtual void formatInvocation(const bool inUseRomanUse,

const bool inIsAfterSeptuagesima) const = 0;

  virtual void formatGreeting(const bool inIsPriest) const = 0;

};

In some cases this will normally just provide some form of formatting fot text provided; in other cases (the Creed, greeting, the confiteor) it also generates the text in a standard way.  Also note that although the names of the methods reflect the substantive domain, it is not technically dependent on any other objects in the overall domain.

The simplest implementation is a text formatter, which writes to a std::ostream, providing simple line wrapping and centring capabilities:

class TextEncapsulatedOfficeFormatter : public IEncapsulatedOfficeFormatter

{

public:

  TextEncapsulatedOfficeFormatter(std::ostream &inStream,

                                  const std::size_t inLineLength)

      : m_stream(inStream), m_lineLength(inLineLength)

  { }

  ~TextEncapsulatedOfficeFormatter() override;

  void formatAntiphon(const std::string& inAntiphon) const override;

  void formatHeading(const std::string& inHeader,

                     const bool inItalic = false) const override;

  void formatResponsory(const std::string &inVersicle,

                        const std::string &inResponse,

                        const bool inEasterResponsory = false,

                        const bool inPassiontideResponsory

                        = false) const override;

  void formatCanticleVerse(const std::string &inVerse) const override;

  void formatPsalmVerse(uint16_t inNumber,

                        std::string_view inVerse) const override;

  void formatChapter(const std::string &inText,

                     const std::string &inSource) const override;

  void formatCollect(const std::string &inText) const override;

  void formatHymn(const std::string &inText,

                  const std::string &inTag) const override;

  void formatGloriaPatri() const override;

  void formatKyrie(const bool inThreefold = true) const override;

  void formatPaterNoster() const override;

  void formatDismissal(const bool inIsPriest) const override;

  void formatCreed() const override;

  void formatConfiteor() const override;

  void formatInvocation(const bool inUseRomanUse,

                        const bool inIsAfterSeptuagesima) const override;

  void formatGreeting(const bool inIsPriest) const override;

private:

  std::ostream &m_stream;

  void wrap(std::ostream &outStream, const std::string_view inString) const;

  void center(std::ostream &outStream, std::string_view inString) const;

  std::size_t m_lineLength = 80;

};


The implementations  are the bvase on which other implemenattions (e.g. for HTML) would be based as far as content goes:

void TextEncapsulatedOfficeFormatter::formatAntiphon(

    const std::string& inAntiphon) const

{

  std::string val = "Ant. "s + inAntiphon;

  if (val[val.length() - 1] != '.')

    val.append(".");

  wrap(m_stream, val);

}

void TextEncapsulatedOfficeFormatter::formatHeading(const std::string& inHeader,

                                               const bool inItalic) const

{

  std::size_t hlen = inHeader.length();

  if (hlen >= (m_lineLength - 1))

    m_stream << inHeader << "\n\n";

  center(m_stream, inHeader);

}


void TextEncapsulatedOfficeFormatter::formatResponsory(

    const std::string &inVersicle, const std::string &inResponse,

    const bool inEasterResponsory, const bool inPassiontideResponsory) const

{

  std::size_t div = inResponse.find('*');

  if (div == std::string::npos)

    {

      wrap(m_stream, "V. " + inVersicle);

      wrap(m_stream, "R. " + inResponse);

      m_stream << std::endl;

    }

  else

    {

      std::string shortResp = inResponse.substr(div + 1);

      if (shortResp[shortResp.length() - 1] != '.')

        shortResp += ".";

      std::string converted = inResponse.substr(0, div) + shortResp;

      std::size_t div2 = converted.find('%');

      boost::algorithm::trim(shortResp);

      shortResp

          = std::string(1, std::toupper(shortResp[0])) + shortResp.substr(1);

      std::string lastResp;

      if (!inEasterResponsory && (div2 != std::string::npos))

        {

          lastResp = converted.substr(div2 + 1);

          converted = converted.substr(0, div2) + lastResp;

          boost::algorithm::trim(lastResp);

          lastResp

              = std::string(1, std::toupper(lastResp[0])) + lastResp.substr(1);

        }

      else if (inEasterResponsory)

        lastResp = "Alleluia";

      else

        lastResp = converted;

      std::string versicle;

      if (inVersicle[inVersicle.length() - 1] != '.')

        versicle = inVersicle + ".";

      else

        versicle = inVersicle;

      std::string gp;

      if (!inPassiontideResponsory)

        gp = " V. Glory be to the Father, and to the Son and to the Holy Ghost.\n"s;

      wrap(m_stream, "R. " + converted);

      wrap(m_stream, "V. " + versicle);

      wrap(m_stream, "R. " + shortResp);

      m_stream << gp;

      if (lastResp[lastResp.length() - 1] != '.')

        lastResp.append(".");

      wrap(m_stream, "R. " + lastResp);

    }

}


void TextEncapsulatedOfficeFormatter::formatCanticleVerse(

    const std::string &inVerse) const

{

  wrap(m_stream, inVerse);

}


void TextEncapsulatedOfficeFormatter::formatPsalmVerse(

    uint16_t inNumber, std::string_view inVerse) const

{

  std::string base = boost::lexical_cast<std::string>(inNumber) + " "

                     + std::string(inVerse);

  wrap(m_stream, base);

  m_stream << "\n";

}


void TextEncapsulatedOfficeFormatter::formatChapter(

    const std::string &inText, const std::string &inSource) const

{

  std::string h = "CHAPTER. " + inSource;

  if (!inSource.ends_with("."))

    h.append(".");

  center(m_stream, h);

  m_stream << "\n";

  if (!inText.ends_with("."))

    wrap(m_stream, inText + ".");

  else

    wrap(m_stream, inText);

  m_stream << "R. Thanks be to God.\n\n";

}


void TextEncapsulatedOfficeFormatter::formatCollect(const std::string &inText) const

{

  m_stream << "Let us pray.\n";

  center(m_stream, "Collect.");

  m_stream << "\n";

  wrap(m_stream, inText + " Amen.");

  m_stream << "\n";

}


void TextEncapsulatedOfficeFormatter::formatHymn(const std::string &inText,

                                            const std::string &inTag) const

{

  center(m_stream, "HYMN. " + inTag + ".");

  m_stream << inText << " Amen.\n\n";

}


void TextEncapsulatedOfficeFormatter::formatGloriaPatri() const

{

  formatCanticleVerse(

      "Glory be to the Father, and to the Son, and to the Holy Ghost:");

  formatCanticleVerse("As it was in the beginning, is now, and ever shall be, "

                      "world without end. Amen.");

}


void TextEncapsulatedOfficeFormatter::formatKyrie(const bool inThreefold) const

{

  if (inThreefold)

    m_stream

      << R"(Lord, have mercy upon us.

Christ, have mercy upon us.

Lord, Have mercy upon us.


)";

  else

    m_stream

      << R"(Lord, have mercy upon us.

Lord, have mercy upon us.

Lord, have mercy upon us.


Christ, have mercy upon us.

Christ, have mercy upon us.

Christ, have mercy upon us.


Lord, Have mercy upon us.

Lord, Have mercy upon us.

Lord, Have mercy upon us.


)";

}


void TextEncapsulatedOfficeFormatter::formatPaterNoster() const

{

  m_stream << "Our Father, said privately.\n\n";

  formatResponsory("And lead us not into temptation.",

                   "But deliver us from evil.");

}


void TextEncapsulatedOfficeFormatter::formatCreed() const {

  m_stream << "I believe in God, said privately.\n\n";

  formatResponsory("The resurrection of the body.",

                   "And the life everlasting.");

}


void TextEncapsulatedOfficeFormatter::formatConfiteor() const  {

  m_stream << "The Officiant:\n";

  wrap(m_stream, 

       "I CONFESS to God Almighty, to Blessed Mary Ever-Virgin, to Blessed Michael the Archangel, to Blessed John Baptist, to the Holy Apostles Peter and Paul, to all the Saints, and to you, my brethren, that I have sinned exceedingly in thought, word, and deed, by my fault, my own fault, my own most grievous fault; therefore I pray Blessed Mary Ever-Virgin, Blessed Michael the Archangel, Blessed John Baptist, the Holy Apostles Peter and Paul, all the Saints, and you, brethren, to pray to the Lord our God for me.");

  m_stream << "The Choir replies :\n\n";

  wrap(m_stream, "May Almighty God have mercy upon thee, pardon and deliver thee from all thy sins, confirm and strengthen thee in all goodness, and bring thee to everlasting life.");

  m_stream << "Amen.\n\nThe Choir:"; 

  wrap(m_stream, 

       "I CONFESS to God Almighty, to Blessed Mary Ever-Virgin, to Blessed Michael the Archangel, to Blessed John Baptist, to the Holy Apostles Peter and Paul, to all the Saints, and to thee, father, that I have sinned exceedingly in thought, word, and deed, by my fault, my own fault, my own most grievous fault; therefore I pray Blessed Mary Ever-Virgin, Blessed Michael the Archangel, Blessed John Baptist, the Holy Apostles Peter and Paul, all the Saints, and thee, father, to pray to the Lord our God for me.");

  m_stream << "The Officiant:\n";

  wrap(m_stream, "May Almighty God have mercy upon you, pardon and deliver you from all your sins, confirm and strengthen you in all goodness, and bring you to everlasting life.");

  m_stream << "The Officiantnt, if a Priest, adds:\n"; 

  wrap(m_stream, "The Almighty and merciful Lord grant you pardon, absolution and remission of all your sins.");

  m_stream << "Amen.\n\n";  

}

void TextEncapsulatedOfficeFormatter::formatGreeting(const bool inIsPriest) const 

{

  if (inIsPriest)

    formatResponsory("The Lord be with you.", "And with thy spirit.");

  else

    formatResponsory("O Lord, hear my prayer.",

                     "And let my cry come unto thee.");

}


void TextEncapsulatedOfficeFormatter::formatDismissal(const bool inIsPriest) const

{

  formatGreeting(inIsPriest);

  formatResponsory("Let us bless the Lord.", "Thanks be to God.");

  wrap(m_stream, "May the souls of the faithful, through the mercy of God, "

                 "rest in peace.  Amen.");

  m_stream << "\n";

}


void TextEncapsulatedOfficeFormatter::center(std::ostream &outStream,

                                        std::string_view inString) const

{

  if (inString.length() >= m_lineLength)

    {

      outStream << inString << std::endl;

      return;

    }

  int toAdd = (m_lineLength - inString.length()) / 2;

  outStream << std::string(toAdd, ' ');

  outStream << inString << std::endl;

}


void TextEncapsulatedOfficeFormatter::wrap(std::ostream &outStream,

                                      std::string_view inString) const

{

  if (inString.length() <= m_lineLength)

    {

      outStream << inString << std::endl;

      return;

    }

  else

    {

      int offset = m_lineLength;

      while ((offset > 0) && !std::isspace(inString[offset]))

        --offset;

      if (offset == 0)

        {

          outStream << inString;

          return;

        }

      std::string_view s = inString.substr(offset + 1);

      outStream << inString.substr(0, offset) << '\n';

      while (s.length() > m_lineLength)

        {

          offset = m_lineLength;

          while ((offset > 0) && !std::isspace(s[offset]))

            --offset;

          if (offset == 0)

            {

              outStream << s;

              return;

            }

          outStream << s.substr(0, offset) << '\n';

          s = s.substr(offset + 1);

        }

      if (!s.empty())

        outStream << s;

      outStream << std::endl;

    }

}


void TextEncapsulatedOfficeFormatter::formatInvocation(const bool inUseRomanUse,

       const bool inIsAfterSeptuagesima) const

{

  if (inUseRomanUse)

    formatResponsory("O God, make speed to save me.",

     "O Lord, make haste to help me.");

  else

    formatResponsory("O God, make speed to save us.",

     "O Lord, make haste to help us.");

  formatGloriaPatri();

  if (inIsAfterSeptuagesima)

    formatCanticleVerse(

"Praise be to thee, O Lord, King of eternal glory.");

  else

    formatCanticleVerse("Alleluia.");

}

The stream interface is hidden so that the formatter can be more generic: a formatter for GTK, for example, would not generally format to stream, but use calls to place formatted text into a text area, e.g. with gtk_text_buffer_insert().

Test Formatter

In testing, I usually don't want the entire text dumped out to my screen or even checked in the course of the unit test.  So there's a test version of the formatter which gets heavily used:

class TestOfficeFormatter : public BreviaryLib::IEncapsulatedOfficeFormatter,

                            public TestSupport::RecordedActions

{

public:

  ~TestOfficeFormatter() override {}


  void

  formatAntiphon(const std::string& inAntiphon) const override

  {

    add("formatAntiphon", inAntiphon);

  }


  void

  formatHeading(const std::string& inHeader,

                const bool inItalic = false) const override

  {

    add("formatHeading", inHeader);

  }


  void

  formatResponsory(const std::string &inVersicle,

                   const std::string &inResponse,

                   const bool inEasterResponsory = false,

                   const bool inPassiontideResponsory = false) const override

  {

    add("formatResponsory", inVersicle);

  }


  void

  formatCanticleVerse(const std::string &inVerse) const override

  {

    add("formatCanticleVerse", inVerse);

  }


  void

  formatPsalmVerse(uint16_t inNumber, std::string_view inVerse) const override

  {

    add("formatPsalmVerse", boost::lexical_cast<std::string>(inNumber));

  }


  void

  formatChapter(const std::string &inText,

                const std::string &inSource) const override

  {

    add("formatChapter", inText);

  }


  void

  formatCollect(const std::string &inText) const override

  {

    add("formatCollect", inText);

  }


  void

  formatHymn(const std::string &inText,

             const std::string &inTag) const override

  {

    add("formatHymn", inTag);

  }


  void

  formatGloriaPatri() const override

  {

    add("formatGloriaPatri");

  }


  void

  formatKyrie(const bool inThreefold = true) const override

  {

    add("formatKyrie");

  }


  void

  formatPaterNoster() const override

  {

    add("formatPaterNoster");

  }


  void

  formatDismissal(const bool inIsPriest) const override

  {

    add("formatDismissal");

  }


  void

  formatCreed() const override

  {

    add("formatCreed");

  }

  void

  formatConfiteor() const override

  {

    add("formatConfiteor");

  }


  void

  formatInvocation(const bool inUseRomanUse,

                   const bool inIsAfterSeptuagesima) const override

  {

    add("formatInvocation");

  }


  void

  formatGreeting(const bool inIsPriest) const override

  {

    add("formatGreeting");

  }

};


I'm not generally worried about capturing everything, so some parameters such as inIsPriest don't get echoed -- I usually want to be able to determine that a call was made.  Likewise psalm and hymn texts are suppressed.


Comments

Popular posts from this blog

Boundaries

State Machines

Considerations on an Optimization