diff options
Diffstat (limited to 'web/xhtml/fragment.cxx')
-rw-r--r-- | web/xhtml/fragment.cxx | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/web/xhtml/fragment.cxx b/web/xhtml/fragment.cxx new file mode 100644 index 0000000..843db82 --- /dev/null +++ b/web/xhtml/fragment.cxx @@ -0,0 +1,143 @@ +// file : web/xhtml/fragment.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include <web/xhtml/fragment.hxx> + +#include <string> +#include <cassert> + +#include <libstudxml/parser.hxx> +#include <libstudxml/serializer.hxx> + +#include <web/xhtml/serialization.hxx> + +using namespace std; +using namespace xml; + +namespace web +{ + namespace xhtml + { + fragment:: + fragment (const string& text, const string& name, size_t length) + { + // To parse the fragment make it a valid xml document, wrapping with the + // root element. If requested, truncate the fragment before the + // first-level element when the content length limit is exceeded. + // + string doc ("<d>" + text + "</d>"); + + parser p (doc.c_str (), + doc.size (), + name, + parser::receive_elements | + parser::receive_characters | + parser::receive_attributes_event); + + size_t len (0); + size_t level (0); + + for (parser::event_type e: p) + { + switch (e) + { + case parser::start_element: + { + truncated = length != 0 && level == 1 && len >= length; + + if (truncated) + break; + + ++level; + } + // Fall through. + case parser::start_attribute: + { + const auto& n (p.qname ()); + + if (!n.namespace_ ().empty ()) + throw parsing ( + name, p.line (), p.column (), "namespace is not allowed"); + + events_.emplace_back (e, n.name ()); + break; + } + case parser::end_element: + { + --level; + } + // Fall through. + case parser::end_attribute: + { + events_.emplace_back (e, ""); + break; + } + case parser::characters: + { + string& s (p.value ()); + + assert (!events_.empty ()); // Contains root element start. + + if (events_.back ().first != parser::start_attribute) + len += s.size (); + + events_.emplace_back (e, move (s)); + break; + } + default: + assert (false); + } + + if (truncated) + { + events_.emplace_back (parser::end_element, ""); // Close root. + break; + } + } + + // Unwrap the fragment removing the root element events. + // + assert (events_.size () >= 2); + events_.erase (events_.begin ()); + events_.pop_back (); + } + + void fragment:: + operator() (serializer& s) const + { + for (const auto& e: events_) + { + switch (e.first) + { + case parser::start_element: + { + s.start_element (xmlns, e.second); + break; + } + case parser::start_attribute: + { + s.start_attribute (e.second); + break; + } + case parser::end_element: + { + s.end_element (); + break; + } + case parser::end_attribute: + { + s.end_attribute (); + break; + } + case parser::characters: + { + s.characters (e.second); + break; + } + default: + assert (false); + } + } + } + } +} |