// file : libstudxml/serializer.cxx // copyright : Copyright (c) 2013-2017 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file #include // std::bad_alloc #include // std::strlen #include using namespace std; namespace xml { // serialization // void serialization:: init () { if (!name_.empty ()) { what_ += name_; what_ += ": "; } what_ += "error: "; what_ += description_; } // 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& oname, unsigned short ind) : os_ (os), os_state_ (os.exceptions ()), oname_ (oname), 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 (oname, m); } } void serializer:: handle_error (genxStatus e) const { 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 (oname_, 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:: end_element (const string& ns, const string& name) { constUtf8 cns, cn; genxStatus e; if ((e = genxGetCurrentElement (s_, &cns, &cn)) || reinterpret_cast (cn) != name || (cns == 0 ? !ns.empty () : reinterpret_cast (cns) != ns)) { handle_error (e != GENX_SUCCESS ? e : GENX_SEQUENCE_ERROR); } end_element (); } void serializer:: element (const string& ns, const string& n, const string& v) { start_element (ns, n); element (v); } 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:: end_attribute (const string& ns, const string& name) { constUtf8 cns, cn; genxStatus e; if ((e = genxGetCurrentAttribute (s_, &cns, &cn)) || reinterpret_cast (cn) != name || (cns == 0 ? !ns.empty () : reinterpret_cast (cns) != ns)) { handle_error (e != GENX_SUCCESS ? e : GENX_SEQUENCE_ERROR); } end_attribute (); } 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 ()), 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); } void serializer:: doctype_decl (const string& re, const string& pi, const string& si, const string& is) { if (genxStatus e = genxDoctypeDeclaration ( s_, reinterpret_cast (re.c_str ()), (pi.empty () ? 0 : reinterpret_cast (pi.c_str ())), (si.empty () ? 0 : reinterpret_cast (si.c_str ())), (is.empty () ? 0 : reinterpret_cast (is.c_str ())))) handle_error (e); } bool serializer:: lookup_namespace_prefix (const string& ns, string& p) const { // 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; } qname serializer:: current_element () const { constUtf8 ns, n; if (genxStatus e = genxGetCurrentElement (s_, &ns, &n)) handle_error (e); return qname (ns != 0 ? reinterpret_cast (ns) : "", reinterpret_cast (n)); } qname serializer:: current_attribute () const { constUtf8 ns, n; if (genxStatus e = genxGetCurrentAttribute (s_, &ns, &n)) handle_error (e); return qname (ns != 0 ? reinterpret_cast (ns) : "", reinterpret_cast (n)); } void serializer:: suspend_indentation () { if (genxStatus e = genxSuspendPrettyPrint (s_)) handle_error (e); } void serializer:: resume_indentation () { if (genxStatus e = genxResumePrettyPrint (s_)) handle_error (e); } size_t serializer:: indentation_suspended () const { return static_cast (genxPrettyPrintSuspended (s_)); } }