diff --git a/XML/include/Poco/XML/XMLStreamSerializer.h b/XML/include/Poco/XML/XMLStreamSerializer.h index 9a93f157f..6d4a94cd8 100644 --- a/XML/include/Poco/XML/XMLStreamSerializer.h +++ b/XML/include/Poco/XML/XMLStreamSerializer.h @@ -55,8 +55,16 @@ public: void startElement(const QName& qname); void startElement(const std::string& name); void startElement(const std::string& ns, const std::string& name); + void endElement(); + // "Checked" end element. That is, it checks that the element + // you think you are ending matches the current one. + // + void endElement (const QName& qname); + void endElement (const std::string& name); + void endElement (const std::string& ns, const std::string& name); + // Helpers for serializing elements with simple content. The first two // functions assume that startElement() has already been called. The // other two serialize the complete element, from start to end. @@ -79,7 +87,17 @@ public: void startAttribute(const QName& qname); void startAttribute(const std::string& name); void startAttribute(const std::string& ns, const std::string& name); + void endAttribute(); + // "Checked" end attribute. That is, it checks that the attribute + // you think you are ending matches the current one. + // + void endAttribute (const QName& qname); + + void endAttribute (const std::string& name); + + void endAttribute (const std::string& ns, const std::string& name); + void attribute(const QName& qname, const std::string& value); template void attribute(const QName& qname, const T& value); @@ -100,23 +118,61 @@ public: /// Namespaces declaration. If prefix is empty, then the default /// namespace is declared. If both prefix and namespace are empty, /// then the default namespace declaration is cleared (xmlns=""). + /// This function should be called after start_element(). void xmlDecl(const std::string& version = "1.0", const std::string& encoding = "UTF-8", const std::string& standalone = ""); /// XML declaration. If encoding or standalone are not specified, /// then these attributes are omitted from the output. + // DOCTYPE declaration. If encoding or standalone are not specified, + // then these attributes are omitted from the output. + // + void doctypeDecl (const std::string& root_element, + const std::string& public_id = "", + const std::string& system_id = "", + const std::string& internal_subset = ""); + // Utility functions. // - bool lookupNamespacePrefix(const std::string& ns, std::string& prefix); + bool lookupNamespacePrefix(const std::string& ns, std::string& prefix) const; /// Return true if there is a mapping. In this case, prefix contains /// the mapped prefix. + // Return the current element, that is, the latest element for which + // start_element() but not endElement() have been called. + // + QName currentElement () const; + + // Return the current attribute, that is, the latest attribute for + // which start_attribute() but not endAttribute() have been called. + // + QName currentAttribute () const; + + // Suspend/resume indentation. + // + // Indentation can be suspended only inside an element and, unless + // explicitly resumed, it will remain suspended until the end of + // that element. You should only explicitly resume indentation at + // the element nesting level of suspension. If indentation is already + // suspended at an outer nesting level, then subsequent calls to + // suspend/resume are ignored. The indentation_suspended() function + // can be used to check if indentation is currently suspended. If it + // is not, then this function returns 0. Otherwise, it returns the + // level at which pretty-printing was suspended, with root element + // being level 1. + // + void suspendIndentation (); + + void resumeIndentation (); + + std::size_t indentationSuspended () const; + private: XMLStreamSerializer(const XMLStreamSerializer&); XMLStreamSerializer& operator=(const XMLStreamSerializer&); - void handleError(genxStatus); + void handleError(genxStatus) const; std::ostream& _outputStream; std::ostream::iostate _lastStreamState;// Original exception state. @@ -127,6 +183,18 @@ private: std::size_t _depth; }; +XMLStreamSerializer& operator<< (XMLStreamSerializer&, void (*func) (XMLStreamSerializer&)); + /// Stream-like interface for serializer. If the passed argument + /// is a function with the void f(serializer&) signature or is a + /// function object with the void operator() (serializer&) const + /// operator, then this function (object) is called with the passed + /// serializer. Otherwise, the argument is passed to the serializer's + // characters() function. + + +template +XMLStreamSerializer& operator<< (XMLStreamSerializer&, const T& value); + inline void XMLStreamSerializer::startElement(const QName& qname) { startElement(qname.namespace_(), qname.name()); @@ -137,6 +205,19 @@ inline void XMLStreamSerializer::startElement(const std::string& name) startElement(std::string(), name); } + +inline void XMLStreamSerializer::endElement (const QName& qname) +{ + endElement (qname.namespace_ (), qname.name ()); +} + + +inline void XMLStreamSerializer::endElement (const std::string& name) +{ + endElement (std::string (), name); +} + + inline void XMLStreamSerializer::element(const std::string& v) { if (!v.empty()) @@ -193,6 +274,18 @@ inline void XMLStreamSerializer::startAttribute(const std::string& name) } +inline void XMLStreamSerializer::endAttribute (const QName& qname) +{ + endAttribute (qname.namespace_ (), qname.name ()); +} + + +inline void XMLStreamSerializer::endAttribute (const std::string& name) +{ + endAttribute (std::string (), name); +} + + inline void XMLStreamSerializer::attribute(const QName& qname, const std::string& value) { attribute(qname.namespace_(), qname.name(), value); @@ -233,6 +326,62 @@ inline void XMLStreamSerializer::characters(const T& value) } +// operator<< +// + +inline XMLStreamSerializer& operator<< (XMLStreamSerializer& s, void (*func) (XMLStreamSerializer&)) +{ + func (s); + return s; +} + + +namespace details +{ + // Detect whether T defines void operator(A) const. + // + template + struct is_functor + { + typedef char no[1]; + typedef char yes[2]; + + template struct check; + + template + static no& test (...); + + template + static yes& test (check*); + + static const bool value = sizeof (test (0)) == sizeof (yes); + }; + + template ::value> + struct inserter; + + template + struct inserter + { + static void insert (XMLStreamSerializer& s, const T& f) {f (s);} + }; + + template + struct inserter + { + static void insert (XMLStreamSerializer& s, const T& v) {s.characters (v);} + }; +} + + +template +inline XMLStreamSerializer& operator<< (XMLStreamSerializer& s, const T& value) +{ + details::inserter::insert (s, value); + return s; +} + + } } diff --git a/XML/include/Poco/XML/genx.h b/XML/include/Poco/XML/genx.h index bdaeee1f6..c727cd9f3 100644 --- a/XML/include/Poco/XML/genx.h +++ b/XML/include/Poco/XML/genx.h @@ -117,6 +117,24 @@ void * genxGetUserData(genxWriter w); genxStatus genxSetPrettyPrint(genxWriter w, int indentation); int genxGetPrettyPrint(genxWriter w); +/* + * Suspend/resume pretty-printing. Pretty-printing can be suspended + * only inside an element and, unless explicitly resumed, it will + * remain suspended until the end of that element. You should only + * explicitly resume pretty-printing at the element nesting level + * of suspension. If pretty-printing is already suspended at an + * outer nesting level, then subsequent calls to suspend/resume + * are ignored. The PrettyPrintSuspended() function can be used + * to check if pretty-printing is currently suspended. If it is + * not, then this function returns 0. Otherwise, it returns the + * level at which pretty-printing was suspended, with root element + * being level 1. + */ +genxStatus genxSuspendPrettyPrint(genxWriter w); +genxStatus genxResumePrettyPrint(genxWriter w); +int genxPrettyPrintSuspended(genxWriter w); + + /* * Set/get canonicalization. If true, then output explicit closing * tags and sort attributes. Default is false. @@ -162,7 +180,7 @@ genxNamespace genxDeclareNamespace(genxWriter w, * If something failed, returns NULL and sets the status code via statusP */ genxElement genxDeclareElement(genxWriter w, - genxNamespace ns, constUtf8 type, + genxNamespace ns, constUtf8 name, genxStatus * statusP); /* @@ -192,7 +210,7 @@ typedef struct genxStatus genxStartDocSender(genxWriter w, genxSender * sender); /* - * End a document. Calls "flush" + * End a document. Calls "flush". */ genxStatus genxEndDocument(genxWriter w); @@ -204,6 +222,19 @@ genxStatus genxXmlDeclaration(genxWriter w, constUtf8 version, constUtf8 encoding, constUtf8 standalone); +/* + * Write DOCTYPE declaration. If public_id is not NULL, then this is + * a PUBLIC DOCTYPE declaration, otherwise, if system_id is not NULL, + * then this is a SYSTEM DOCTYPE. If both are NULL, then a DOCTYPE + * that only contains the root element and, if not NULL, internal + * subset is written. + */ +genxStatus genxDoctypeDeclaration(genxWriter w, + constUtf8 root_element, + constUtf8 public_id, + constUtf8 system_id, + constUtf8 internal_subset); + /* * Write a comment */ @@ -218,7 +249,7 @@ genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text); * Start an element */ genxStatus genxStartElementLiteral(genxWriter w, - constUtf8 xmlns, constUtf8 type); + constUtf8 xmlns, constUtf8 name); /* * Start a predeclared element @@ -226,28 +257,44 @@ genxStatus genxStartElementLiteral(genxWriter w, */ genxStatus genxStartElement(genxElement e); +/* + * Get current element. The returned values are valid until this + * element ceases to be current (i.e., EndElement() is called). + * If the element is unqualified, then xmlns is set to NULL. + */ +genxStatus genxGetCurrentElement (genxWriter w, + constUtf8* xmlns, constUtf8* name); + /* * Write an attribute */ genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns, constUtf8 name, constUtf8 value); +/* + * Write a predeclared attribute + */ +genxStatus genxAddAttribute(genxAttribute a, constUtf8 value); + /* * Start an attribute */ genxStatus genxStartAttributeLiteral(genxWriter w, constUtf8 xmlns, constUtf8 name); -/* - * Write a predeclared attribute - */ -genxStatus genxAddAttribute(genxAttribute a, constUtf8 value); - /* * Start a predeclared attribute */ genxStatus genxStartAttribute(genxAttribute a); +/* + * Get current attribute. The returned values are valid until this + * attribute ceases to be current (i.e., EndAttribute() is called). + * If the attribute is unqualified, then xmlns is set to NULL. + */ +genxStatus genxGetCurrentAttribute (genxWriter w, + constUtf8* xmlns, constUtf8* name); + /* * End an attribute */ diff --git a/XML/samples/CMakeLists.txt b/XML/samples/CMakeLists.txt index 659591ff6..196ba8f81 100644 --- a/XML/samples/CMakeLists.txt +++ b/XML/samples/CMakeLists.txt @@ -2,4 +2,5 @@ add_subdirectory(DOMParser) add_subdirectory(DOMWriter) add_subdirectory(PrettyPrint) add_subdirectory(SAXParser) +add_subdirectory(RoundTrip) diff --git a/XML/samples/RoundTrip/CMakeLists.txt b/XML/samples/RoundTrip/CMakeLists.txt new file mode 100644 index 000000000..2da54f203 --- /dev/null +++ b/XML/samples/RoundTrip/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SAMPLE_NAME "RoundTrip") + +set(LOCAL_SRCS "") +aux_source_directory(src LOCAL_SRCS) + +add_executable( ${SAMPLE_NAME} ${LOCAL_SRCS} ) +target_link_libraries( ${SAMPLE_NAME} PocoXML PocoFoundation ) diff --git a/XML/samples/RoundTrip/Makefile b/XML/samples/RoundTrip/Makefile new file mode 100644 index 000000000..a85b3944a --- /dev/null +++ b/XML/samples/RoundTrip/Makefile @@ -0,0 +1,21 @@ +# +# Makefile +# +# $Id: //poco/1.4/XML/samples/SAXParser/Makefile#1 $ +# +# Makefile for Poco SAXParser +# + +include $(POCO_BASE)/build/rules/global + +objects = RoundTrip + +target = RoundTrip +target_version = 1 +target_libs = PocoXML PocoFoundation + +include $(POCO_BASE)/build/rules/exec + +ifdef POCO_UNBUNDLED + SYSLIBS += -lexpat +endif diff --git a/XML/samples/RoundTrip/src/RoundTrip.cpp b/XML/samples/RoundTrip/src/RoundTrip.cpp new file mode 100644 index 000000000..f0be3188a --- /dev/null +++ b/XML/samples/RoundTrip/src/RoundTrip.cpp @@ -0,0 +1,101 @@ +// file : examples/roundtrip/driver.cxx +// copyright : not copyrighted - public domain + +#include +#include +#include + +#include "Poco/XML/XMLStreamParser.h" +#include "Poco/XML/XMLStreamSerializer.h" + +using namespace std; +using namespace Poco::XML; + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " " << endl; + return 1; + } + + try + { + // Enable stream exceptions so that io failures are reported + // as stream rather than as parsing exceptions. + // + ifstream ifs; + ifs.exceptions(ifstream::badbit | ifstream::failbit); + ifs.open(argv[1], ifstream::in | ifstream::binary); + + // Configure the parser to receive attributes as events as well + // as to receive prefix-namespace mappings (namespace declarations + // in XML terminology). + // + XMLStreamParser p(ifs, argv[1], + XMLStreamParser::RECEIVE_DEFAULT | XMLStreamParser::RECEIVE_ATTRIBUTES_EVENT | XMLStreamParser::RECEIVE_NAMESPACE_DECLS); + + // Configure serializer not to perform indentation. Existing + // indentation, if any, will be preserved. + // + XMLStreamSerializer s(cout, "out", 0); + + for (XMLStreamParser::EventType e(p.next()); e != XMLStreamParser::Eof; e = p.next()) + { + switch (e) + { + case XMLStreamParser::StartElement: + { + s.startElement(p.getQName()); + break; + } + case XMLStreamParser::EndElement: + { + s.endElement(); + break; + } + case XMLStreamParser::StartNamespaceDecl: + { + s.namespaceDecl(p.namespace_(), p.prefix()); + break; + } + case XMLStreamParser::EndNamespaceDecl: + { + // There is nothing in XML that indicates the end of namespace + // declaration since it is scope-based. + // + break; + } + case XMLStreamParser::StartAttribute: + { + s.startAttribute(p.getQName()); + break; + } + case XMLStreamParser::EndAttribute: + { + s.endAttribute(); + break; + } + case XMLStreamParser::Characters: + { + s.characters(p.value()); + break; + } + case XMLStreamParser::Eof: + { + // Handled in the for loop. + // + break; + } + } + } + } catch (const ios_base::failure&) + { + cerr << "io failure" << endl; + return 1; + } catch (const Poco::Exception& e) + { + cerr << e.displayText() << endl; + return 1; + } +} diff --git a/XML/src/XMLStreamSerializer.cpp b/XML/src/XMLStreamSerializer.cpp index f0fe9a6db..176c249bb 100644 --- a/XML/src/XMLStreamSerializer.cpp +++ b/XML/src/XMLStreamSerializer.cpp @@ -105,7 +105,7 @@ XMLStreamSerializer::XMLStreamSerializer(ostream& os, const string& oname, unsig } -void XMLStreamSerializer::handleError(genxStatus e) +void XMLStreamSerializer::handleError(genxStatus e) const { switch (e) { @@ -153,6 +153,21 @@ void XMLStreamSerializer::endElement() } +void XMLStreamSerializer::endElement (const string& ns, const string& name) +{ + constUtf8 cns, cn; + genxStatus e; + if ((e = genxGetCurrentElement (_writer, &cns, &cn)) || + reinterpret_cast (cn) != name || + (cns == 0 ? !ns.empty () : reinterpret_cast (cns) != ns)) + { + handleError (e != GENX_SUCCESS ? e : GENX_SEQUENCE_ERROR); + } + + endElement (); +} + + void XMLStreamSerializer::element(const string& ns, const string& n, const string& v) { startElement(ns, n); @@ -180,6 +195,21 @@ void XMLStreamSerializer::endAttribute() } +void XMLStreamSerializer::endAttribute (const string& ns, const string& name) +{ + constUtf8 cns, cn; + genxStatus e; + if ((e = genxGetCurrentAttribute (_writer, &cns, &cn)) || + reinterpret_cast (cn) != name || + (cns == 0 ? !ns.empty () : reinterpret_cast (cns) != ns)) + { + handleError (e != GENX_SUCCESS ? e : GENX_SEQUENCE_ERROR); + } + + endAttribute (); +} + + void XMLStreamSerializer::attribute(const string& ns, const string& name, const string& value) { if (genxStatus e = genxAddAttributeLiteral(_writer, reinterpret_cast(ns.empty() ? 0 : ns.c_str()), reinterpret_cast(name.c_str()), @@ -213,7 +243,22 @@ void XMLStreamSerializer::xmlDecl(const string& ver, const string& enc, const st } -bool XMLStreamSerializer::lookupNamespacePrefix(const string& ns, string& p) +void XMLStreamSerializer::doctypeDecl (const string& re, + const string& pi, + const string& si, + const string& is) +{ + if (genxStatus e = genxDoctypeDeclaration ( + _writer, + 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 ())))) + handleError (e); +} + + +bool XMLStreamSerializer::lookupNamespacePrefix(const string& ns, string& p) const { // Currently Genx will create a namespace mapping if one doesn't // already exist. @@ -229,5 +274,45 @@ bool XMLStreamSerializer::lookupNamespacePrefix(const string& ns, string& p) } +QName XMLStreamSerializer::currentElement () const +{ + constUtf8 ns, n; + if (genxStatus e = genxGetCurrentElement (_writer, &ns, &n)) + handleError (e); + + return QName (ns != 0 ? reinterpret_cast (ns) : "", reinterpret_cast (n)); +} + + +QName XMLStreamSerializer::currentAttribute () const +{ + constUtf8 ns, n; + if (genxStatus e = genxGetCurrentAttribute (_writer, &ns, &n)) + handleError (e); + + return QName (ns != 0 ? reinterpret_cast (ns) : "", reinterpret_cast (n)); +} + + +void XMLStreamSerializer::suspendIndentation () +{ + if (genxStatus e = genxSuspendPrettyPrint (_writer)) + handleError (e); +} + + +void XMLStreamSerializer::resumeIndentation () +{ + if (genxStatus e = genxResumePrettyPrint (_writer)) + handleError (e); +} + + +size_t XMLStreamSerializer::indentationSuspended () const +{ + return static_cast (genxPrettyPrintSuspended (_writer)); +} + + } } diff --git a/XML/src/genx.c b/XML/src/genx.c index 55ac7a6b0..87eb7445f 100644 --- a/XML/src/genx.c +++ b/XML/src/genx.c @@ -80,7 +80,7 @@ struct genxNamespace_rec struct genxElement_rec { genxWriter writer; - utf8 type; + utf8 name; genxNamespace ns; }; @@ -131,6 +131,7 @@ struct genxWriter_rec /* Pretty-printing state */ int ppIndent; int ppDepth; + int ppSuspendDepth; /* Non-0 means we are suspended. */ Boolean ppSimple; /* Canonicalization. */ @@ -316,7 +317,7 @@ static genxNamespace findNamespace(plist * pl, constUtf8 uri) return NULL; } -static genxElement findElement(plist * pl, constUtf8 xmlns, constUtf8 type) +static genxElement findElement(plist * pl, constUtf8 xmlns, constUtf8 name) { size_t i; genxElement * ee = (genxElement *) pl->pointers; @@ -325,15 +326,15 @@ static genxElement findElement(plist * pl, constUtf8 xmlns, constUtf8 type) { if (xmlns == NULL) { - if (ee[i]->ns == NULL && strcmp((const char *) type, - (const char *) ee[i]->type) == 0) + if (ee[i]->ns == NULL && strcmp((const char *) name, + (const char *) ee[i]->name) == 0) return ee[i]; } else { if (ee[i]->ns != NULL && strcmp((const char *) xmlns, (const char *) ee[i]->ns->name) == 0 && - strcmp((const char *) type, (const char *) ee[i]->type) == 0) + strcmp((const char *) name, (const char *) ee[i]->name) == 0) return ee[i]; } } @@ -692,6 +693,62 @@ int genxGetPrettyPrint(genxWriter w) return w->ppIndent; } +/* + * Suspend/resume pretty-printing. + */ +genxStatus genxSuspendPrettyPrint(genxWriter w) +{ + int d = w->ppDepth; + + if (w->ppIndent == 0) + return w->status = GENX_SEQUENCE_ERROR; + + switch (w->sequence) + { + case SEQUENCE_START_TAG: + case SEQUENCE_ATTRIBUTES: + d++; /* No start tag written, still outer depth. */ + case SEQUENCE_CONTENT: + break; + default: + return w->status = GENX_SEQUENCE_ERROR; + } + + if (w->ppSuspendDepth == 0) /* Ignore nested suspend/resume calls. */ + w->ppSuspendDepth = d; + + return w->status; +} + +genxStatus genxResumePrettyPrint(genxWriter w) +{ + int d = w->ppDepth; + + if (w->ppIndent == 0 || w->ppSuspendDepth == 0) + return w->status = GENX_SEQUENCE_ERROR; + + switch (w->sequence) + { + case SEQUENCE_START_TAG: + case SEQUENCE_ATTRIBUTES: + d++; /* No start tag written, still outer depth. */ + case SEQUENCE_CONTENT: + break; + default: + return w->status = GENX_SEQUENCE_ERROR; + } + + if (w->ppSuspendDepth == d) /* Ignore nested suspend/resume calls. */ + w->ppSuspendDepth = 0; + + return w->status; +} + +int genxPrettyPrintSuspended(genxWriter w) +{ + return w->ppSuspendDepth; +} + /* * get/set canonicalization. */ @@ -752,7 +809,7 @@ void genxDispose(genxWriter w) for (i = 0; i < w->elements.count; i++) { - deallocate(w, ee[i]->type); + deallocate(w, ee[i]->name); deallocate(w, ee[i]); } @@ -1009,20 +1066,20 @@ utf8 genxGetNamespacePrefix(genxNamespace ns) * DeclareElement - see genx.h for details */ genxElement genxDeclareElement(genxWriter w, - genxNamespace ns, constUtf8 type, + genxNamespace ns, constUtf8 name, genxStatus * statusP) { genxElement old; genxElement el; - if ((w->status = checkNCName(w, type)) != GENX_SUCCESS) + if ((w->status = checkNCName(w, name)) != GENX_SUCCESS) { *statusP = w->status; return NULL; } /* already declared? */ - old = findElement(&w->elements, (ns == NULL) ? NULL : ns->name, type); + old = findElement(&w->elements, (ns == NULL) ? NULL : ns->name, name); if (old) return old; @@ -1034,7 +1091,7 @@ genxElement genxDeclareElement(genxWriter w, el->writer = w; el->ns = ns; - if ((el->type = copy(w, type)) == NULL) + if ((el->name = copy(w, name)) == NULL) { w->status = *statusP = GENX_ALLOC_FAILED; return NULL; @@ -1217,8 +1274,9 @@ genxStatus genxStartDocSender(genxWriter w, genxSender * sender) if (w->ppIndent) { - w->ppSimple = True; w->ppDepth = 0; + w->ppSuspendDepth = 0; + w->ppSimple = True; } return GENX_SUCCESS; @@ -1292,7 +1350,9 @@ static genxStatus writeStartTag(genxWriter w, Boolean close) if (w->ppIndent) { - if (w->ppDepth) + if (w->ppDepth && + /* Suspend depth could be at this element's depth (after ++ below). */ + (w->ppSuspendDepth == 0 || w->ppSuspendDepth > w->ppDepth)) if (writeIndentation (w) != GENX_SUCCESS) return w->status; @@ -1301,6 +1361,15 @@ static genxStatus writeStartTag(genxWriter w, Boolean close) w->ppDepth++; w->ppSimple = True; } + else + { + /* + * Conceptually we incremented/decremented the depth, so check if we + * need to resume pretty-printing. + */ + if (w->ppSuspendDepth > w->ppDepth) + w->ppSuspendDepth = 0; + } } SendCheck(w, "<"); @@ -1309,7 +1378,7 @@ static genxStatus writeStartTag(genxWriter w, Boolean close) SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON); SendCheck(w, ":"); } - SendCheck(w, e->type); + SendCheck(w, e->name); /* If we are canonicalizing, then write sorted attributes. Otherwise write them in the order specified. */ @@ -1711,6 +1780,22 @@ genxStatus genxStartAttribute(genxAttribute a) return GENX_SUCCESS; } +genxStatus genxGetCurrentAttribute (genxWriter w, + constUtf8* xmlns, constUtf8* name) +{ + genxAttribute a; + + if (w->sequence != SEQUENCE_START_ATTR) + return w->status = GENX_SEQUENCE_ERROR; + + a = w->nowStartingAttr; + + *xmlns = a->ns ? a->ns->name : NULL; + *name = a->name; + + return GENX_SUCCESS; +} + genxStatus genxEndAttribute(genxWriter w) { genxAttribute a; @@ -1744,6 +1829,36 @@ genxStatus genxEndAttribute(genxWriter w) return GENX_SUCCESS; } +genxStatus genxGetCurrentElement (genxWriter w, + constUtf8* xmlns, constUtf8* name) +{ + int i; + genxElement e; + + switch (w->sequence) + { + case SEQUENCE_START_TAG: + case SEQUENCE_ATTRIBUTES: + e = w->nowStarting; + break; + case SEQUENCE_CONTENT: + /* Find the element. The same code as in EndElement() below. */ + for (i = (int) (w->stack.count) - 1; + w->stack.pointers[i] != NULL; + i -= 2) + ; + e = (genxElement) w->stack.pointers[--i]; + break; + default: + return w->status = GENX_SEQUENCE_ERROR; + } + + *xmlns = e->ns ? e->ns->name : NULL; + *name = e->name; + + return GENX_SUCCESS; +} + genxStatus genxEndElement(genxWriter w) { int i; @@ -1789,9 +1904,12 @@ genxStatus genxEndElement(genxWriter w) { w->ppDepth--; - if (!w->ppSimple) + if (!w->ppSimple && w->ppSuspendDepth == 0) if (writeIndentation (w) != GENX_SUCCESS) return w->status; + + if (w->ppSuspendDepth > w->ppDepth) + w->ppSuspendDepth = 0; /* Resume pretty-printing. */ } SendCheck(w, "ns->declaration->name + STRLEN_XMLNS_COLON); SendCheck(w, ":"); } - SendCheck(w, e->type); + SendCheck(w, e->name); SendCheck(w, ">"); } - if (w->ppIndent) + /* If this element is written while pretty-printing is suspended, + treat it as simple. As an example, think of an XHTML element + for which we suspend pretty-printing before writing the opening + tag and resume it after the closing one. */ + if (w->ppIndent && w->ppSuspendDepth == 0) w->ppSimple = False; /* @@ -2143,6 +2265,58 @@ genxStatus genxXmlDeclaration(genxWriter w, return GENX_SUCCESS; } +genxStatus genxDoctypeDeclaration(genxWriter w, + constUtf8 re, + constUtf8 pi, + constUtf8 si, + constUtf8 is) +{ + if (w->sequence != SEQUENCE_PRE_DOC) + return w->status = GENX_SEQUENCE_ERROR; + + if ((w->status = genxCheckText(w, re)) != GENX_SUCCESS) + return w->status; + + if (pi != NULL && (w->status = genxCheckText(w, pi)) != GENX_SUCCESS) + return w->status; + + if (si != NULL && (w->status = genxCheckText(w, si)) != GENX_SUCCESS) + return w->status; + + if (is != NULL && (w->status = genxCheckText(w, is)) != GENX_SUCCESS) + return w->status; + + SendCheck (w, "\n"); + return GENX_SUCCESS; +} + genxStatus genxComment(genxWriter w, constUtf8 text) { size_t i; @@ -2251,7 +2425,7 @@ genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text) * Literal versions of the writing routines */ genxStatus genxStartElementLiteral(genxWriter w, - constUtf8 xmlns, constUtf8 type) + constUtf8 xmlns, constUtf8 name) { genxNamespace ns = NULL; genxElement e; @@ -2262,7 +2436,7 @@ genxStatus genxStartElementLiteral(genxWriter w, if (ns == NULL || w->status != GENX_SUCCESS) return w->status; } - e = genxDeclareElement(w, ns, type, &w->status); + e = genxDeclareElement(w, ns, name, &w->status); if (e == NULL || w->status != GENX_SUCCESS) return w->status;