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
Post a Comment