From 2ac86dfdafb9bf4d4e9252260a431755b56d8ebe Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 8 Mar 2013 17:41:00 +0200 Subject: Add support for XML parsing and serialization --- cutl/xml/serializer.cxx | 245 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 cutl/xml/serializer.cxx (limited to 'cutl/xml/serializer.cxx') diff --git a/cutl/xml/serializer.cxx b/cutl/xml/serializer.cxx new file mode 100644 index 0000000..66c78a4 --- /dev/null +++ b/cutl/xml/serializer.cxx @@ -0,0 +1,245 @@ +// file : cutl/xml/serializer.cxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include // std::bad_alloc +#include // std::strlen + +#include + +using namespace std; + +namespace cutl +{ + namespace xml + { + // serialization + // + serialization:: + ~serialization () throw () {} + + serialization:: + serialization (const string& n, const string& d) + : name_ (n), description_ (d) + { + if (!n.empty ()) + { + what_ += n; + what_ += ": "; + } + + what_ += "error: "; + what_ += d; + } + + char const* serialization:: + what () const throw () + { + return what_.c_str (); + } + + // serializer + // + extern "C" genxStatus + genx_write (void* p, constUtf8 us) + { + // It would have been easier to throw the exception directly, + // however, the Genx code is most likely not exception safe. + // + ostream* os (static_cast (p)); + const char* s (reinterpret_cast (us)); + os->write (s, static_cast (strlen (s))); + return os->good () ? GENX_SUCCESS : GENX_IO_ERROR; + } + + extern "C" genxStatus + genx_write_bound (void* p, constUtf8 start, constUtf8 end) + { + ostream* os (static_cast (p)); + const char* s (reinterpret_cast (start)); + streamsize n (static_cast (end - start)); + os->write (s, n); + return os->good () ? GENX_SUCCESS : GENX_IO_ERROR; + } + + extern "C" genxStatus + genx_flush (void* p) + { + ostream* os (static_cast (p)); + os->flush (); + return os->good () ? GENX_SUCCESS : GENX_IO_ERROR; + } + + serializer:: + ~serializer () + { + if (s_ != 0) + genxDispose (s_); + } + + serializer:: + serializer (ostream& os, const string& name, unsigned short ind) + : os_ (os), os_state_ (os.exceptions ()), name_ (name), depth_ (0) + { + // Temporarily disable exceptions on the stream. + // + os_.exceptions (ostream::goodbit); + + // Allocate the serializer. Make sure nothing else can throw after + // this call since otherwise we will leak it. + // + s_ = genxNew (0, 0, 0); + + if (s_ == 0) + throw bad_alloc (); + + genxSetUserData (s_, &os_); + + if (ind != 0) + genxSetPrettyPrint (s_, ind); + + sender_.send = &genx_write; + sender_.sendBounded = &genx_write_bound; + sender_.flush = &genx_flush; + + if (genxStatus e = genxStartDocSender (s_, &sender_)) + { + string m (genxGetErrorMessage (s_, e)); + genxDispose (s_); + throw serialization (name, m); + } + } + + void serializer:: + handle_error (genxStatus e) + { + switch (e) + { + case GENX_ALLOC_FAILED: + throw bad_alloc (); + case GENX_IO_ERROR: + // Restoring the original exception state should trigger the + // exception. If it doesn't (e.g., because the user didn't + // configure the stream to throw), then fall back to the + // serialiation exception. + // + os_.exceptions (os_state_); + // Fall through. + default: + throw serialization (name_, genxGetErrorMessage (s_, e)); + } + } + + void serializer:: + start_element (const string& ns, const string& name) + { + if (genxStatus e = genxStartElementLiteral ( + s_, + reinterpret_cast (ns.empty () ? 0 : ns.c_str ()), + reinterpret_cast (name.c_str ()))) + handle_error (e); + + depth_++; + } + + void serializer:: + end_element () + { + if (genxStatus e = genxEndElement (s_)) + handle_error (e); + + // Call EndDocument() if we are past the root element. + // + if (--depth_ == 0) + { + if (genxStatus e = genxEndDocument (s_)) + handle_error (e); + + // Also restore the original exception state on the stream. + // + os_.exceptions (os_state_); + } + } + + void serializer:: + start_attribute (const string& ns, const string& name) + { + if (genxStatus e = genxStartAttributeLiteral ( + s_, + reinterpret_cast (ns.empty () ? 0 : ns.c_str ()), + reinterpret_cast (name.c_str ()))) + handle_error (e); + } + + void serializer:: + end_attribute () + { + if (genxStatus e = genxEndAttribute (s_)) + handle_error (e); + } + + void serializer:: + attribute (const string& ns, + const string& name, + const string& value) + { + if (genxStatus e = genxAddAttributeLiteral ( + s_, + reinterpret_cast (ns.empty () ? 0 : ns.c_str ()), + reinterpret_cast (name.c_str ()), + reinterpret_cast (value.c_str ()))) + handle_error (e); + } + + void serializer:: + characters (const string& value) + { + if (genxStatus e = genxAddCountedText ( + s_, + reinterpret_cast (value.c_str ()), + static_cast (value.size ()))) + handle_error (e); + } + + void serializer:: + namespace_decl (const string& ns, const string& p) + { + if (genxStatus e = ns.empty () && p.empty () + ? genxUnsetDefaultNamespace (s_) + : genxAddNamespaceLiteral ( + s_, + reinterpret_cast (ns.c_str ()), + reinterpret_cast (p.c_str ()))) + handle_error (e); + } + + void serializer:: + xml_decl (const string& ver, const string& enc, const string& stl) + { + if (genxStatus e = genxXmlDeclaration ( + s_, + reinterpret_cast (ver.c_str ()), + (enc.empty () ? 0 : reinterpret_cast (enc.c_str ())), + (stl.empty () ? 0 : reinterpret_cast (stl.c_str ())))) + handle_error (e); + } + + bool serializer:: + lookup_namespace_prefix (const string& ns, string& p) + { + // Currently Genx will create a namespace mapping if one doesn't + // already exist. + // + genxStatus e; + genxNamespace gns ( + genxDeclareNamespace ( + s_, reinterpret_cast (ns.c_str ()), 0, &e)); + + if (e != GENX_SUCCESS) + handle_error (e); + + p = reinterpret_cast (genxGetNamespacePrefix (gns)); + return true; + } + } +} -- cgit v1.1