// file : web/xhtml-fragment.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include 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 ("" + text + ""); 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); } } } } }