// // XMLWriter.h // // Library: XML // Package: XML // Module: XMLWriter // // Definition of the XMLWriter class. // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #ifndef XML_XMLWriter_INCLUDED #define XML_XMLWriter_INCLUDED #include "Poco/XML/XML.h" #include "Poco/SAX/ContentHandler.h" #include "Poco/SAX/LexicalHandler.h" #include "Poco/SAX/DTDHandler.h" #include "Poco/SAX/NamespaceSupport.h" #include "Poco/XML/XMLString.h" #include "Poco/XML/XMLStream.h" #include "Poco/XML/Name.h" #include "Poco/TextEncoding.h" #include "Poco/StreamConverter.h" #include #include namespace Poco { namespace XML { class Locator; class XML_API XMLWriter: public ContentHandler, public LexicalHandler, public DTDHandler /// This class serializes SAX2 ContentHandler, LexicalHandler and /// DTDHandler events back into a stream. /// /// Various consistency checks are performed on the written data /// (i.e. there must be exactly one root element and every startElement() /// must have a matching endElement()). /// /// The XMLWriter supports optional pretty-printing of the serialized XML. /// Note, however, that pretty-printing XML data alters the /// information set of the document being written, since in /// XML all whitespace is potentially relevant to an application. /// /// The writer contains extensive support for XML Namespaces, so that a client /// application does not have to keep track of prefixes and supply xmlns attributes. /// /// If the client does not provide namespace prefixes (either by specifying them /// as part of the qualified name given to startElement(), or by calling /// startPrefixMapping()), the XMLWriter automatically generates namespace /// prefixes in the form ns1, ns2, etc. { public: enum Options { CANONICAL = 0x00, /// Do not write an XML declaration (default). CANONICAL_XML = 0x01, /// Enables basic support for Canonical XML: /// - do not write an XML declaration /// - do not use special empty element syntax /// - set the New Line character to NEWLINE_LF /// - write namespace declarations and attributes /// in canonical order /// - use default namespace as much as possible WRITE_XML_DECLARATION = 0x02, /// Write an XML declaration. PRETTY_PRINT = 0x04, /// Pretty-print XML markup. PRETTY_PRINT_ATTRIBUTES = 0x08 /// Write each attribute on a separate line. /// PRETTY_PRINT must be specified as well. }; XMLWriter(XMLByteOutputStream& str, int options); /// Creates the XMLWriter and sets the specified options. /// /// The resulting stream will be UTF-8 encoded. XMLWriter(XMLByteOutputStream& str, int options, const std::string& encodingName, Poco::TextEncoding& textEncoding); /// Creates the XMLWriter and sets the specified options. /// /// The encoding is reflected in the XML declaration. /// The caller is responsible for that the given encodingName matches with /// the given textEncoding. XMLWriter(XMLByteOutputStream& str, int options, const std::string& encodingName, Poco::TextEncoding* pTextEncoding); /// Creates the XMLWriter and sets the specified options. /// /// The encoding is reflected in the XML declaration. /// The caller is responsible for that the given encodingName matches with /// the given textEncoding. /// If pTextEncoding is null, the given encodingName is ignored and the /// default UTF-8 encoding is used. ~XMLWriter(); /// Destroys the XMLWriter. void setNewLine(const std::string& newLineCharacters); /// Sets the line ending for the resulting XML file. /// /// Possible values are: /// * NEWLINE_DEFAULT (whatever is appropriate for the current platform) /// * NEWLINE_CRLF (Windows), /// * NEWLINE_LF (Unix), /// * NEWLINE_CR (Macintosh) const std::string& getNewLine() const; /// Returns the line ending currently in use. void setIndent(const std::string& indent); /// Sets the string used for one indentation step. /// /// The default is a single TAB character. /// The given string should only contain TAB or SPACE /// characters (e.g., a single TAB character, or /// two to four SPACE characters). const std::string& getIndent() const; /// Returns the string used for one indentation step. // ContentHandler void setDocumentLocator(const Locator* loc); /// Currently unused. void startDocument(); /// Writes a generic XML declaration to the stream. /// If a document type has been set (see SetDocumentType), /// a DOCTYPE declaration is also written. void endDocument(); /// Checks that all elements are closed and prints a final newline. void startFragment(); /// Use this instead of StartDocument() if you want to write /// a fragment rather than a document (no XML declaration and /// more than one "root" element allowed). void endFragment(); /// Checks that all elements are closed and prints a final newline. void startElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes); /// Writes an XML start element tag. /// /// Namespaces are handled as follows. /// 1. If a qname, but no namespaceURI and localName are given, the qname is taken as element name. /// 2. If a namespaceURI and a localName, but no qname is given, and the given namespaceURI has been /// declared earlier, the namespace prefix for the given namespaceURI together with the localName /// is taken as element name. If the namespace has not been declared, a prefix in the form /// "ns1", "ns2", etc. is generated and the namespace is declared with the generated prefix. /// 3. If all three are given, and the namespace given in namespaceURI has not been declared, it is declared now. /// Otherwise, see 2. void startElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname); /// Writes an XML start element tag with no attributes. /// See the other startElement() method for more information. void endElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname); /// Writes an XML end element tag. /// /// Throws an exception if the name of doesn't match the /// one of the most recent startElement(). void emptyElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname); /// Writes an empty XML element tag (). void emptyElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes); /// Writes an empty XML element tag with the given attributes (). void characters(const XMLChar ch[], int start, int length); /// Writes XML character data. Quotes, ampersand's, less-than and /// greater-than signs are escaped, unless a CDATA section /// has been opened by calling startCDATA(). /// /// The characters must be encoded in UTF-8 (if XMLChar is char) or /// UTF-16 (if XMLChar is wchar_t). void characters(const XMLString& str); /// Writes XML character data. Quotes, ampersand's, less-than and /// greater-than signs are escaped, unless a CDATA section /// has been opened by calling startCDATA(). /// /// The characters must be encoded in UTF-8 (if XMLChar is char) or /// UTF-16 (if XMLChar is wchar_t). void rawCharacters(const XMLString& str); /// Writes the characters in the given string as they are. /// The caller is responsible for escaping characters as /// necessary to produce valid XML. /// /// The characters must be encoded in UTF-8 (if XMLChar is char) or /// UTF-16 (if XMLChar is wchar_t). void ignorableWhitespace(const XMLChar ch[], int start, int length); /// Writes whitespace characters by simply passing them to /// characters(). void processingInstruction(const XMLString& target, const XMLString& data); /// Writes a processing instruction. void startPrefixMapping(const XMLString& prefix, const XMLString& namespaceURI); /// Begin the scope of a prefix-URI Namespace mapping. /// A namespace declaration is written with the next element. void endPrefixMapping(const XMLString& prefix); /// End the scope of a prefix-URI mapping. void skippedEntity(const XMLString& name); /// Does nothing. void dataElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const XMLString& data, const XMLString& attr1 = XMLString(), const XMLString& value1 = XMLString(), const XMLString& attr2 = XMLString(), const XMLString& value2 = XMLString(), const XMLString& attr3 = XMLString(), const XMLString& value3 = XMLString()); /// Writes a data element in the form data. // LexicalHandler void startCDATA(); /// Writes the string that ends a CDATA section. void comment(const XMLChar ch[], int start, int length); /// Writes a comment. void startDTD(const XMLString& name, const XMLString& publicId, const XMLString& systemId); /// Writes a DTD declaration. void endDTD(); /// Writes the closing characters of a DTD declaration. void startEntity(const XMLString& name); /// Does nothing. void endEntity(const XMLString& name); /// Does nothing. // DTDHandler void notationDecl(const XMLString& name, const XMLString* publicId, const XMLString* systemId); void unparsedEntityDecl(const XMLString& name, const XMLString* publicId, const XMLString& systemId, const XMLString& notationName); static const std::string NEWLINE_DEFAULT; static const std::string NEWLINE_CR; static const std::string NEWLINE_CRLF; static const std::string NEWLINE_LF; // Namespace support. XMLString uniquePrefix(); /// Creates and returns a unique namespace prefix that /// can be used with startPrefixMapping(). bool isNamespaceMapped(const XMLString& namespc) const; /// Returns true if the given namespace has been mapped /// to a prefix in the current element or its ancestors. // Misc. int depth() const; /// Return the number of nested XML elements. /// /// Will be -1 if no document or fragment has been started, /// 0 if the document or fragment has been started, /// 1 if the document element has been written and /// > 1 for every element nested within the document element. protected: typedef std::map AttributeMap; typedef std::map> CanonicalAttributeMap; void writeStartElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes); void writeCanonicalStartElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes); void writeEndElement(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname); void writeMarkup(const std::string& str) const; void writeXML(const XMLString& str) const; void writeXML(XMLChar ch) const; void writeNewLine() const; void writeIndent() const; void writeIndent(int indent) const; void writeName(const XMLString& prefix, const XMLString& localName); void writeXMLDeclaration(); void closeStartTag(); void declareNamespaces(const XMLString& namespaceURI, const XMLString& localName, const XMLString& qname, const Attributes& attributes); void declareAttributeNamespaces(const Attributes& attributes); void addNamespaceAttributes(AttributeMap& attributeMap); void addNamespaceAttributes(CanonicalAttributeMap& attributeMap); void addAttributes(AttributeMap& attributeMap, const Attributes& attributes, const XMLString& elementNamespaceURI); void addAttributes(CanonicalAttributeMap& attributeMap, const Attributes& attributes, const XMLString& elementNamespaceURI); void writeAttributes(const AttributeMap& attributeMap); void writeAttributes(const CanonicalAttributeMap& attributeMap); void prettyPrint() const; static std::string nameToString(const XMLString& localName, const XMLString& qname); private: struct Namespace { Namespace(const XMLString& thePrefix, const XMLString& theNamespaceURI): prefix(thePrefix), namespaceURI(theNamespaceURI) { } XMLString prefix; XMLString namespaceURI; }; typedef std::vector ElementStack; Poco::OutputStreamConverter* _pTextConverter; Poco::TextEncoding* _pInEncoding; Poco::TextEncoding* _pOutEncoding; int _options; std::string _encoding; std::string _newLine; int _depth; int _elementCount; bool _inFragment; bool _inCDATA; bool _inDTD; bool _inInternalDTD; bool _contentWritten; bool _unclosedStartTag; ElementStack _elementStack; NamespaceSupport _namespaces; int _prefix; bool _nsContextPushed; std::string _indent; static const std::string MARKUP_QUOTENC; static const std::string MARKUP_AMPENC; static const std::string MARKUP_LTENC; static const std::string MARKUP_GTENC; static const std::string MARKUP_TABENC; static const std::string MARKUP_CRENC; static const std::string MARKUP_LFENC; static const std::string MARKUP_LT; static const std::string MARKUP_GT; static const std::string MARKUP_SLASHGT; static const std::string MARKUP_LTSLASH; static const std::string MARKUP_COLON; static const std::string MARKUP_EQQUOT; static const std::string MARKUP_QUOT; static const std::string MARKUP_SPACE; static const std::string MARKUP_TAB; static const std::string MARKUP_BEGIN_CDATA; static const std::string MARKUP_END_CDATA; }; // // inlines // inline int XMLWriter::depth() const { return _depth; } } } // namespace Poco::XML #endif // XML_XMLWriter_INCLUDED