summaryrefslogtreecommitdiff
path: root/examples/cxx/tree/streaming/serializer.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-07-04 11:15:44 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-07-04 11:15:44 +0200
commit57fe13de5c01bc8e5bbeb39acd131c9329245261 (patch)
tree1196307d17664b97af750778488e4798d0dbf8dc /examples/cxx/tree/streaming/serializer.cxx
parenta1bc52f9ae499a672b05b3264f82a24637a16a02 (diff)
Update streaming example with new implementation
Diffstat (limited to 'examples/cxx/tree/streaming/serializer.cxx')
-rw-r--r--examples/cxx/tree/streaming/serializer.cxx492
1 files changed, 457 insertions, 35 deletions
diff --git a/examples/cxx/tree/streaming/serializer.cxx b/examples/cxx/tree/streaming/serializer.cxx
index 04d2db4..0ce8156 100644
--- a/examples/cxx/tree/streaming/serializer.cxx
+++ b/examples/cxx/tree/streaming/serializer.cxx
@@ -1,9 +1,11 @@
-// file : examples/cxx/tree/streaming/serializer.cxx
-// copyright : not copyrighted - public domain
+#include <vector>
+#include <cassert>
+#include <cstddef>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/dom/DOM.hpp>
+#include <xercesc/dom/impl/DOMLSSerializerImpl.hpp>
#include <xsd/cxx/xml/string.hxx>
#include <xsd/cxx/xml/dom/bits/error-handler-proxy.hxx>
@@ -20,6 +22,319 @@ using namespace xercesc;
namespace xml = xsd::cxx::xml;
namespace tree = xsd::cxx::tree;
+static const XMLCh gEOLSeq[] =
+{
+ chLF, chNull
+};
+
+static const XMLCh gUTF8[] =
+{
+ chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull
+};
+
+static const XMLCh gEndElement[] =
+{
+ chOpenAngle, chForwardSlash, chNull
+};
+
+static const int DISCARD_DEFAULT_CONTENT_ID = 0x1;
+static const int ENTITIES_ID = 0x2;
+static const int FORMAT_PRETTY_PRINT_1ST_LEVEL_ID = 0xA;
+
+class StreamingDOMSerializer: public DOMLSSerializerImpl
+{
+public:
+ StreamingDOMSerializer (MemoryManager* manager)
+ : DOMLSSerializerImpl (manager)
+ {
+ }
+
+ bool
+ startOpen (const DOMElement* e, DOMLSOutput* const destination)
+ {
+ const DOMDocument* docu (e->getOwnerDocument ());
+ assert (docu != 0);
+
+ // Code adapted from DOMLSSerializerImpl::write().
+ //
+ target_ = destination->getByteStream();
+
+ fEncodingUsed = gUTF8;
+
+ const XMLCh* lsEncoding=destination->getEncoding();
+ if (lsEncoding && *lsEncoding)
+ {
+ fEncodingUsed = lsEncoding;
+ }
+ else if (docu)
+ {
+ const XMLCh* tmpEncoding = docu->getInputEncoding();
+
+ if ( tmpEncoding && *tmpEncoding)
+ {
+ fEncodingUsed = tmpEncoding;
+ }
+ else
+ {
+ tmpEncoding = docu->getXmlEncoding();
+
+ if ( tmpEncoding && *tmpEncoding)
+ {
+ fEncodingUsed = tmpEncoding;
+ }
+ }
+ }
+
+ fNewLineUsed = (fNewLine && *fNewLine)? fNewLine : gEOLSeq;
+
+ fDocumentVersion = (docu->getXmlVersion() && *(docu->getXmlVersion()))
+ ? docu->getXmlVersion()
+ : XMLUni::fgVersion1_0;
+
+ fErrorCount = 0;
+
+ fLineFeedInTextNodePrinted = false;
+ fLastWhiteSpaceInTextNode = 0;
+
+ level_ = 0;
+ namespace_map_.clear ();
+
+ fFormatter = new (fMemoryManager) XMLFormatter( fEncodingUsed
+ ,fDocumentVersion
+ ,target_
+ ,XMLFormatter::NoEscapes
+ ,XMLFormatter::UnRep_CharRef
+ ,fMemoryManager);
+ formatter_.reset (fFormatter);
+
+ // Write out the XML declaration, etc. Here we assume that the document
+ // has no children (i.e., no root element).
+ //
+ processNode (docu, 0);
+ fLineFeedInTextNodePrinted = true;
+
+ return writeOpen (e);
+ }
+
+ bool
+ writeOpen (const DOMElement* e)
+ {
+ // Code adapted from the first part of ELEMENT_NODE case in
+ // DOMLSSerializerImpl::processNode().
+ //
+
+ if (!fLineFeedInTextNodePrinted)
+ {
+ if(level_ == 1 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
+ printNewLine();
+
+ printNewLine();
+ }
+ else
+ {
+ fLineFeedInTextNodePrinted = false;
+ }
+
+ printIndent(level_);
+
+ RefHashTableOf<XMLCh>* namespaceMap = NULL;
+
+ *fFormatter << XMLFormatter::NoEscapes << chOpenAngle <<
+ e->getNodeName ();
+
+ setURCharRef();
+ DOMNamedNodeMap *attributes = e->getAttributes();
+ XMLSize_t attrCount = attributes->getLength();
+
+ const XMLCh* prefix = e->getPrefix();
+ const XMLCh* uri = e->getNamespaceURI();
+ if((uri && uri[0]) ||
+ ((prefix==0 || prefix[0]==0) && isDefaultNamespacePrefixDeclared()))
+ {
+ if(prefix==0 || prefix[0]==0)
+ prefix=XMLUni::fgZeroLenString;
+ if(!isNamespaceBindingActive(prefix, uri))
+ {
+ if(namespaceMap==NULL)
+ {
+ namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
+ fNamespaceStack->addElement(namespaceMap);
+ }
+ namespaceMap->put((void*)prefix,(XMLCh*)uri);
+ *fFormatter << XMLFormatter::NoEscapes
+ << chSpace << XMLUni::fgXMLNSString;
+
+ if(!XMLString::equals(prefix,XMLUni::fgZeroLenString))
+ *fFormatter << chColon << prefix;
+
+ *fFormatter << chEqual << chDoubleQuote
+ << XMLFormatter::AttrEscapes
+ << uri
+ << XMLFormatter::NoEscapes
+ << chDoubleQuote;
+ }
+ }
+
+ bool discard = getFeature(DISCARD_DEFAULT_CONTENT_ID);
+ for (XMLSize_t i = 0; i < attrCount; i++)
+ {
+ DOMAttr* attribute = (DOMAttr*)attributes->item(i);
+
+ if (discard && !((DOMAttr*)attribute )->getSpecified())
+ continue;
+
+ // if this attribute is a namespace declaration, add it to the namespace map for the current level
+ const XMLCh* ns = attribute->getNamespaceURI();
+ if (ns != 0 )
+ {
+ if(XMLString::equals(ns, XMLUni::fgXMLNSURIName))
+ {
+ if(namespaceMap==NULL)
+ {
+ namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
+ fNamespaceStack->addElement(namespaceMap);
+ }
+ const XMLCh* nsPrefix = attribute->getLocalName();
+ if(XMLString::equals(attribute->getNodeName(),XMLUni::fgXMLNSString))
+ nsPrefix = XMLUni::fgZeroLenString;
+ if(namespaceMap->containsKey((void*)nsPrefix))
+ continue;
+ namespaceMap->put((void*)attribute->getLocalName(),(XMLCh*)attribute->getNodeValue());
+ }
+ else if(!XMLString::equals(ns, XMLUni::fgXMLURIName))
+ {
+ // check if the namespace for the current node is already defined
+ const XMLCh* prefix = attribute->getPrefix();
+ if(prefix && prefix[0])
+ {
+ const XMLCh* uri = attribute->getNamespaceURI();
+ if(!isNamespaceBindingActive(prefix, uri))
+ {
+ if(namespaceMap==NULL)
+ {
+ namespaceMap=new (fMemoryManager) RefHashTableOf<XMLCh>(12, false, fMemoryManager);
+ fNamespaceStack->addElement(namespaceMap);
+ }
+ namespaceMap->put((void*)prefix,(XMLCh*)uri);
+ *fFormatter << XMLFormatter::NoEscapes
+ << chSpace << XMLUni::fgXMLNSString << chColon << prefix
+ << chEqual << chDoubleQuote
+ << XMLFormatter::AttrEscapes
+ << uri
+ << XMLFormatter::NoEscapes
+ << chDoubleQuote;
+ }
+ }
+ }
+ }
+
+ if (XMLString::equals(ns, XMLUni::fgXMLNSURIName) || checkFilter(attribute) == DOMNodeFilter::FILTER_ACCEPT)
+ {
+ *fFormatter << XMLFormatter::NoEscapes
+ << chSpace << attribute->getNodeName()
+ << chEqual << chDoubleQuote
+ << XMLFormatter::AttrEscapes;
+
+ if (getFeature(ENTITIES_ID))
+ {
+ DOMNode* child = attribute->getFirstChild();
+ while( child != 0)
+ {
+ if(child->getNodeType()==DOMNode::TEXT_NODE)
+ *fFormatter << child->getNodeValue();
+ else if(child->getNodeType()==DOMNode::ENTITY_REFERENCE_NODE)
+ *fFormatter << XMLFormatter::NoEscapes
+ << chAmpersand << child->getNodeName() << chSemiColon
+ << XMLFormatter::AttrEscapes;
+ child = child->getNextSibling();
+ }
+ }
+ else
+ *fFormatter << attribute->getNodeValue();
+
+ *fFormatter << XMLFormatter::NoEscapes << chDoubleQuote;
+ }
+ }
+
+ *fFormatter << XMLFormatter::NoEscapes << chCloseAngle;
+
+ // Keep track of whether we have added a namespace map for this
+ // element. Used to pop it in writeClose().
+ //
+ namespace_map_.push_back (namespaceMap != 0);
+
+ level_++;
+
+ DOMNode* child = e->getFirstChild();
+ while (child != 0)
+ {
+ processNode (child, level_);
+ child = child->getNextSibling();
+ }
+
+ return fErrorCount == 0;
+ }
+
+ bool
+ writeClose (const XMLCh* name)
+ {
+ // Code adapted from the second part of ELEMENT_NODE case in
+ // DOMLSSerializerImpl::processNode().
+ //
+ level_--;
+
+ // Assume we are not on the same line (nodeLine != fCurrentLine).
+ //
+ {
+ if (!fLineFeedInTextNodePrinted)
+ {
+ printNewLine();
+ }
+ else
+ {
+ fLineFeedInTextNodePrinted = false;
+ }
+
+ if(level_ == 0 && getFeature(FORMAT_PRETTY_PRINT_1ST_LEVEL_ID))
+ printNewLine();
+
+ printIndent(level_);
+ }
+
+ *fFormatter << XMLFormatter::NoEscapes << gEndElement <<
+ name << chCloseAngle;
+
+ if (namespace_map_.back ())
+ fNamespaceStack->removeLastElement();
+
+ namespace_map_.pop_back ();
+
+ if (level_ == 0)
+ {
+ printNewLine();
+ target_->flush ();
+ }
+
+ return fErrorCount == 0;
+ }
+
+ bool
+ write (const DOMElement* e)
+ {
+ processNode (e, level_);
+ return fErrorCount == 0;
+ }
+
+ using DOMLSSerializerImpl::write; // Whole document.
+
+private:
+ XMLFormatTarget* target_;
+ std::auto_ptr<XMLFormatter> formatter_;
+ int level_;
+
+ std::vector<bool> namespace_map_;
+};
+
class serializer_impl
{
public:
@@ -37,17 +352,25 @@ public:
create (const string& ns, const string& qname, const namespace_infomap&);
void
- serialize (DOMElement& e);
+ serialize (xml::dom::auto_ptr<DOMElement>);
+
+ void
+ serialize_open (xml::dom::auto_ptr<DOMElement>);
+
+ void
+ serialize_close (const string&);
private:
void
- add_namespaces (xercesc::DOMElement*, const namespace_infomap&);
+ clear_document ();
private:
+ bool start_;
+
// Serializer.
//
xml::dom::auto_ptr<DOMLSOutput> out_;
- xml::dom::auto_ptr<DOMLSSerializer> serializer_;
+ xml::dom::auto_ptr<StreamingDOMSerializer> serializer_;
auto_ptr<xml::dom::ostream_format_target> oft_;
@@ -58,6 +381,10 @@ private:
//
DOMImplementation& dom_impl_;
xml::dom::auto_ptr<DOMDocument> doc_;
+ vector<DOMElement*> element_stack_;
+
+ size_t element_count_; // Number of elements serialized using current doc.
+ static const size_t element_count_limit_ = 500;
};
const XMLCh ls[] = {chLatin_L, chLatin_S, chNull};
@@ -65,27 +392,35 @@ const XMLCh ls[] = {chLatin_L, chLatin_S, chNull};
serializer_impl::
serializer_impl ()
: error_proxy_ (error_handler_),
- dom_impl_ (*DOMImplementationRegistry::getDOMImplementation (ls)),
- doc_ (dom_impl_.createDocument ())
+ dom_impl_ (*DOMImplementationRegistry::getDOMImplementation (ls))
{
- serializer_.reset (dom_impl_.createLSSerializer ());
- DOMConfiguration* conf (serializer_->getDomConfig ());
+ serializer_.reset (
+ new (XMLPlatformUtils::fgMemoryManager)
+ StreamingDOMSerializer (XMLPlatformUtils::fgMemoryManager));
+ DOMConfiguration* conf (serializer_->getDomConfig ());
conf->setParameter (XMLUni::fgDOMErrorHandler, &error_proxy_);
+ conf->setParameter (XMLUni::fgDOMXMLDeclaration, true);
conf->setParameter (XMLUni::fgDOMWRTDiscardDefaultContent, true);
conf->setParameter (XMLUni::fgDOMWRTFormatPrettyPrint, true);
- conf->setParameter (XMLUni::fgDOMXMLDeclaration, false);
+ conf->setParameter (XMLUni::fgDOMWRTXercesPrettyPrint, false);
}
void serializer_impl::
start (ostream& os, const string& encoding)
{
+ element_stack_.clear ();
+ doc_.reset (dom_impl_.createDocument ());
+ element_count_ = 0;
+
error_handler_.reset ();
oft_.reset (new xml::dom::ostream_format_target (os));
out_.reset (dom_impl_.createLSOutput ());
out_->setEncoding (xml::string (encoding).c_str ());
out_->setByteStream (oft_.get ());
+
+ start_ = true;
}
DOMElement* serializer_impl::
@@ -94,7 +429,13 @@ create (const string& name, const namespace_infomap& map)
DOMElement* r (doc_->createElement (xml::string (name).c_str ()));
if (!map.empty ())
- add_namespaces (r, map);
+ xml::dom::add_namespaces<char> (*r, map);
+
+ // Add the element as the child of the stack "tip" so that it
+ // "sees" all the namespace declarations active from this point.
+ //
+ if (!element_stack_.empty ())
+ element_stack_.back ()->appendChild (r);
return r;
}
@@ -107,42 +448,111 @@ create (const string& ns, const string& qname, const namespace_infomap& map)
xml::string (ns).c_str (), xml::string (qname).c_str ()));
if (!map.empty ())
- add_namespaces (r, map);
+ xml::dom::add_namespaces<char> (*r, map);
+
+ // Add the element as the child of the stack "tip" so that it
+ // "sees" all the namespace declarations active from this point.
+ //
+ if (!element_stack_.empty ())
+ element_stack_.back ()->appendChild (r);
return r;
}
void serializer_impl::
-add_namespaces (DOMElement* e, const namespace_infomap& map)
+serialize (xml::dom::auto_ptr<DOMElement> p)
{
- for (namespace_infomap::const_iterator i (map.begin ()), end (map.end ());
- i != end; ++i)
+ DOMElement* e (p.get ());
+
+ if (start_)
{
- if (i->first.empty ())
- {
- // Empty prefix.
- //
- if (!i->second.name.empty ())
- e->setAttributeNS (
- XMLUni::fgXMLNSURIName,
- xml::string ("xmlns").c_str (),
- xml::string (i->second.name).c_str ());
- }
- else
- {
- e->setAttributeNS (
- XMLUni::fgXMLNSURIName,
- xml::string ("xmlns:" + i->first).c_str (),
- xml::string (i->second.name).c_str ());
- }
+ serializer_->write (e, out_.get ());
+ start_ = false;
}
+ else
+ serializer_->write (e);
+
+ error_handler_.throw_if_failed<tree::serialization<char> > ();
+
+ // Remove this element from its parent before we release.
+ //
+ if (!element_stack_.empty ())
+ element_stack_.back ()->removeChild (e);
+
+ p.reset (); // Release it before we may clear the document below.
+
+ if (element_count_++ > element_count_limit_)
+ clear_document ();
}
void serializer_impl::
-serialize (DOMElement& e)
+serialize_open (xml::dom::auto_ptr<DOMElement> p)
{
- serializer_->write (&e, out_.get ());
+ DOMElement* e (p.get ());
+
+ if (start_)
+ {
+ serializer_->startOpen (e, out_.get ());
+ start_ = false;
+ }
+ else
+ serializer_->writeOpen (e);
+
+ error_handler_.throw_if_failed<tree::serialization<char> > ();
+
+ // Add this element to the element stack. serialize_close() is
+ // responsible for its removal and releasing.
+ //
+ element_stack_.push_back (e);
+ p.release ();
+}
+
+void serializer_impl::
+serialize_close (const string& name)
+{
+ serializer_->writeClose (xml::string (name).c_str ());
error_handler_.throw_if_failed<tree::serialization<char> > ();
+
+ // Release the element.
+ //
+ DOMElement* e (element_stack_.back ());
+ element_stack_.pop_back ();
+
+ if (!element_stack_.empty ())
+ element_stack_.back ()->removeChild (e);
+
+ e->release ();
+
+ if (element_count_++ > element_count_limit_)
+ clear_document ();
+}
+
+void serializer_impl::
+clear_document ()
+{
+ // Re-create the document in order to force deallocation of its
+ // internal heap. While Xerces-C++ DOM tries to re-use memory,
+ // it still accumulates no longer used memory blocks.
+ //
+ xml::dom::auto_ptr<DOMDocument> doc (dom_impl_.createDocument ());
+
+ if (!element_stack_.empty ())
+ {
+ DOMElement* e (
+ static_cast<DOMElement*> (
+ doc->importNode (element_stack_.front (), true)));
+
+ for (vector<DOMElement*>::iterator i (element_stack_.begin ());
+ i != element_stack_.end ();
+ ++i)
+ {
+ *i = e;
+ e = static_cast<DOMElement*> (e->getFirstChild ());
+ }
+ }
+
+ doc_ = doc;
+ element_count_ = 0;
}
//
@@ -179,7 +589,19 @@ create (const string& ns, const string& qname, const namespace_infomap& map)
}
void serializer::
-serialize (DOMElement& e)
+serialize (xml::dom::auto_ptr<DOMElement> e)
{
impl_->serialize (e);
}
+
+void serializer::
+serialize_open (xml::dom::auto_ptr<DOMElement> e)
+{
+ impl_->serialize_open (e);
+}
+
+void serializer::
+serialize_close (const string& name)
+{
+ impl_->serialize_close (name);
+}