lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master 783befa 006/156: Add separate class for repre


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 783befa 006/156: Add separate class for representing HTML contents
Date: Tue, 30 Jan 2018 17:21:50 -0500 (EST)

branch: master
commit 783befa028b9b7adde2f676bf6674dae3662e8e3
Author: Vadim Zeitlin <address@hidden>
Commit: Vadim Zeitlin <address@hidden>

    Add separate class for representing HTML contents
    
    This is the first step in making HTML generation code more type-safe,
    although right now this is not really the case yet, as html_text objects
    still have to be created from and converted to strings in many places.
---
 group_quote_pdf_gen_wx.cpp | 126 +++++-----------------------
 html_text.hpp              | 199 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 218 insertions(+), 107 deletions(-)

diff --git a/group_quote_pdf_gen_wx.cpp b/group_quote_pdf_gen_wx.cpp
index fb94998..cc056c3 100644
--- a/group_quote_pdf_gen_wx.cpp
+++ b/group_quote_pdf_gen_wx.cpp
@@ -28,6 +28,7 @@
 #include "calendar_date.hpp"            // jdn_t()
 #include "data_directory.hpp"           // AddDataDir()
 #include "force_linking.hpp"
+#include "html_text.hpp"
 #include "ledger.hpp"
 #include "ledger_invariant.hpp"
 #include "ledger_text_formats.hpp"      // ledger_format()
@@ -59,114 +60,25 @@ LMI_FORCE_LINKING_IN_SITU(group_quote_pdf_generator_wx)
 namespace
 {
 
-/// Escape special XML characters in the given string, ensuring that it appears
-/// correctly inside HTML element contents. Notice that we don't need to escape
-/// quotes here as we never use the result of this function inside an HTML
-/// attribute, only inside HTML elements.
-
-wxString escape_for_html_elem(std::string const& s)
-{
-    wxString const u = wxString::FromUTF8(s.c_str());
-
-    wxString z;
-    z.reserve(u.length());
-    for(auto const& i : u)
-        {
-        switch(i.GetValue())
-            {
-            case '<': z += "&lt;" ; break;
-            case '>': z += "&gt;" ; break;
-            case '&': z += "&amp;"; break;
-            default : z += i      ;
-            }
-        }
-    return z;
-}
-
-/// Namespace for helpers used for HTML generation.
-
-namespace html
-{
-
-/// Namespace for the support HTML tags.
-///
-/// Tags are only used as template arguments, so they don't need to be defined,
-/// just declared -- and tag_info below specialized for them.
-
-namespace tag
-{
-
-struct b;
-struct br;
-
-} // namespace tag
-
-template<typename T>
-struct tag_info;
-
-template<>
-struct tag_info<tag::b>
-{
-    static char const* get_name() { return "b"; }
-    static bool has_end() { return true; }
-};
-
-template<>
-struct tag_info<tag::br>
-{
-    static char const* get_name() { return "br"; }
-    static bool has_end() { return false; }
-};
-
-} // namespace html
-
-/// Wrap the given text in an HTML tag if it is not empty, otherwise just
-/// return an empty string.
-///
-/// For the tags without matching closing tags, such as e.g. "<br>", wrapping
-/// the text means just prepending the tag to it. This is still done only if
-/// the text is not empty.
-
-template<typename T>
-wxString wrap_if_not_empty(wxString const& html)
-{
-    wxString result;
-    if(!html.empty())
-        {
-        result << '<' << html::tag_info<T>::get_name() << '>' << html;
-        if(html::tag_info<T>::has_end())
-            {
-            result << "</" << html::tag_info<T>::get_name() << '>';
-            }
-        }
-
-    return result;
-}
-
 /// Transform 'html' -> '<br><br>html', but return empty string unchanged.
 
-wxString brbr(std::string const& html)
+html::text brbr(std::string const& html)
 {
-    return
-        wrap_if_not_empty<html::tag::br>
-            (wrap_if_not_empty<html::tag::br>
-                (escape_for_html_elem(html)
-                )
-            );
+    return html::text::escape_for_html_elem(html)
+            .wrap_if_not_empty<html::tag::br>()
+            .wrap_if_not_empty<html::tag::br>()
+            ;
 }
 
 /// Transform 'html' -> '<br><br><b>html</b>', but return empty string 
unchanged.
 
-wxString brbrb(std::string const& html)
+html::text brbrb(std::string const& html)
 {
-    return
-        wrap_if_not_empty<html::tag::br>
-            (wrap_if_not_empty<html::tag::br>
-                (wrap_if_not_empty<html::tag::b>
-                    (escape_for_html_elem(html)
-                    )
-                )
-            );
+    return html::text::escape_for_html_elem(html)
+                .wrap_if_not_empty<html::tag::b>()
+                .wrap_if_not_empty<html::tag::br>()
+                .wrap_if_not_empty<html::tag::br>()
+                ;
 }
 
 /// Generate HTML representation of a field name and value in an HTML table.
@@ -182,9 +94,9 @@ wxString name_value_as_html_table_data
     return wxString::Format
         ("<td nowrap align=\"right\"><b>%s%s&nbsp;&nbsp;</b></td>"
          "<td>%s&nbsp;&nbsp;&nbsp;&nbsp;</td>"
-        ,escape_for_html_elem(name)
+        ,html::text::escape_for_html_elem(name).as_string()
         ,(value.empty() ? "" : ":")
-        ,escape_for_html_elem(value)
+        ,html::text::escape_for_html_elem(value).as_string()
         );
 }
 
@@ -407,7 +319,7 @@ class group_quote_pdf_generator_wx
         std::string premium_mode_;
         std::string contract_state_;
         std::string effective_date_;
-        wxString    footer_html_;
+        html::text   footer_html_;
 
         // Dynamically-determined fields.
         std::string elected_riders_;
@@ -995,9 +907,9 @@ void group_quote_pdf_generator_wx::output_document_header
          "<td align=\"center\"><i>Prepared By: %s</i></td>"
          "</tr>"
          "</table>"
-        ,escape_for_html_elem(report_data_.company_)
+        ,html::text::escape_for_html_elem(report_data_.company_).as_string()
         ,wxDateTime::Today().FormatDate()
-        ,escape_for_html_elem(report_data_.prepared_by_)
+        
,html::text::escape_for_html_elem(report_data_.prepared_by_).as_string()
         );
 
     pdf_writer.output_html
@@ -1235,13 +1147,13 @@ void group_quote_pdf_generator_wx::output_footer
         *pos_y += vert_skip;
         }
 
-    wxString const footer_html = "<p>" + report_data_.footer_html_ + "</p>";
+    auto footer_html = report_data_.footer_html_.wrap<html::tag::p>();
 
     *pos_y += pdf_writer.output_html
         (pdf_writer.get_horz_margin()
         ,*pos_y
         ,pdf_writer.get_page_width()
-        ,footer_html
+        ,std::move(footer_html).as_string()
         ,output_mode
         );
 }
diff --git a/html_text.hpp b/html_text.hpp
new file mode 100644
index 0000000..3f6c3c4
--- /dev/null
+++ b/html_text.hpp
@@ -0,0 +1,199 @@
+// Text representing HTML contents.
+//
+// Copyright (C) 2017 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#ifndef html_text_hpp
+#define html_text_hpp
+
+#include "config.hpp"
+
+#include <string>
+#include <utility>                      // std::move
+
+/// Namespace for helpers used for HTML generation.
+namespace html
+{
+
+/// Namespace for the support HTML tags.
+///
+/// Tags are only used as template arguments, so they don't need to be defined,
+/// just declared -- and tag::info below specialized for them.
+
+namespace tag
+{
+
+struct b;
+struct br;
+struct p;
+
+template<typename T>
+struct info;
+
+template<>
+struct info<b>
+{
+    static char const* get_name() { return "b"; }
+    static bool has_end() { return true; }
+};
+
+template<>
+struct info<br>
+{
+    static char const* get_name() { return "br"; }
+    static bool has_end() { return false; }
+};
+
+template<>
+struct info<p>
+{
+    static char const* get_name() { return "p"; }
+    static bool has_end() { return true; }
+};
+
+} // namespace tag
+
+/// Represents a piece of text containing HTML.
+///
+/// This is a separate type for type safety, e.g. to avoid passing raw,
+/// unescaped, strings to a function expecting HTML (or, less catastrophically,
+/// but still wrongly, passing already escaped HTML to a function doing
+/// escaping internally).
+///
+/// As it still needs to be converted to a string sooner or later to be really
+/// used, it does provide a conversion -- but it can be used only once.
+class text
+{
+public:
+    // This type has value semantics.
+    text() = default;
+    text(text const&) = default;
+    text(text &&) = default;
+    text& operator=(text const&) = default;
+    text& operator=(text&&) = default;
+
+    /// Escape special XML characters in the given string, ensuring that it
+    /// appears correctly inside HTML element contents. Notice that we don't
+    /// need to escape quotes here as we never use the result of this function
+    /// inside an HTML attribute, only inside HTML elements.
+    static text escape_for_html_elem(std::string const& s)
+    {
+        std::string z;
+        z.reserve(s.length());
+        for(auto const& c : s)
+            {
+            switch(c)
+                {
+                case '<': z += "&lt;" ; break;
+                case '>': z += "&gt;" ; break;
+                case '&': z += "&amp;"; break;
+                default : z += c      ;
+                }
+            }
+
+        return text{std::move(z)};
+    }
+
+    /// Wrap contents of this HTML snippet into the given tag (or just prepend
+    /// the tag if it doesn't have the matching end tag).
+    template <typename T>
+    text wrap() const
+    {
+        return do_wrap(tag::info<T>::get_name(), tag::info<T>::has_end());
+    }
+
+    /// Wrap contents of this HTML snippet into the given tag (or just prepend
+    /// the tag if it doesn't have the matching end tag), but only if it is not
+    /// empty -- otherwise just return empty text.
+    ///
+    /// For the tags without matching closing tags, such as e.g. "<br>",
+    /// wrapping the text means just prepending the tag to it. This is still
+    /// done only if the text is not empty.
+    template <typename T>
+    text wrap_if_not_empty() const
+    {
+        return m_html.empty() ? text{} : wrap<T>();
+    }
+
+    /// Append another HTML fragment to this one.
+    ///
+    /// This method allows chained invocation for appending more than one
+    /// fragment at once.
+    text& operator+=(text t)
+    {
+        m_html += std::move(t).m_html;
+
+        return *this;
+    }
+
+    /// Return the string with UTF-8 encoded HTML text of the given object
+    /// consuming it in the process.
+    ///
+    /// Notice that this method can only be called on rvalue references, and so
+    /// it can't be used more than once.
+    std::string&& as_string() &&
+    {
+        return std::move(m_html);
+    }
+
+private:
+    // This move ctor is private and does not perform any escaping.
+    text(std::string&& html)
+        :m_html{html}
+    {
+    }
+
+    // Type-independent part implementation of public wrap(): having it as a
+    // separate function avoids template bloat.
+    text do_wrap(char const* outer_tag, bool has_end) const
+    {
+        std::string z;
+        z.reserve(m_html.length() + 10);
+
+        z = '<';
+        z += outer_tag;
+        z += '>';
+
+        z += m_html;
+
+        if(has_end)
+            {
+            z += "</";
+            z += outer_tag;
+            z += '>';
+            }
+
+        return text{std::move(z)};
+    }
+
+
+    std::string m_html;
+};
+
+inline
+text operator+(text t1, text t2)
+{
+    auto t{t1};
+    t += t2;
+    return t;
+}
+
+} // namespace html
+
+#endif // html_text_hpp



reply via email to

[Prev in Thread] Current Thread [Next in Thread]