File indexing completed on 2024-05-12 16:00:00

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
0003    SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0004    SPDX-FileCopyrightText: 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef XMLWRITER_H
0010 #define XMLWRITER_H
0011 
0012 #include <QDebug>
0013 #include <QMap>
0014 #include <QIODevice>
0015 
0016 #include "kritastore_export.h"
0017 
0018 /**
0019  * A class for writing out XML (to any QIODevice), with a special attention on performance.
0020  * The XML is being written out along the way, which avoids requiring the entire
0021  * document in memory (like QDom does).
0022  */
0023 class KRITASTORE_EXPORT KoXmlWriter
0024 {
0025 public:
0026     /**
0027      * Create a KoXmlWriter instance to write out an XML document into
0028      * the given QIODevice.
0029      */
0030     explicit KoXmlWriter(QIODevice* dev, int indentLevel = 0);
0031 
0032     /// Destructor
0033     ~KoXmlWriter();
0034 
0035     /**
0036      * Start the XML document.
0037      * This writes out the \<?xml?\> tag with utf8 encoding, and the DOCTYPE.
0038      *
0039      * @param rootElemName the name of the root element, used in the DOCTYPE tag.
0040      * @param publicId the public identifier, e.g. "-//OpenOffice.org//DTD OfficeDocument 1.0//EN"
0041      * @param systemId the system identifier, e.g. "office.dtd" or a full URL to it.
0042      */
0043     void startDocument(const char* rootElemName, const char* publicId = 0, const char* systemId = 0);
0044 
0045     /// Call this to terminate an XML document.
0046     void endDocument();
0047 
0048     /**
0049      * Start a new element, as a child of the current element.
0050      *
0051      * @param tagName the name of the tag.
0052      * @param indentInside if set to false, there will be no indentation inside
0053      * this tag. This is useful for elements where whitespace matters.
0054      */
0055     void startElement(const char* tagName, bool indentInside = true);
0056 
0057     /**
0058      * Overloaded version of addAttribute( const char*, const char* ),
0059      * which is a bit slower because it needs to convert @p value to utf8 first.
0060      */
0061     inline void addAttribute(const char* attrName, const QString& value) {
0062         addAttribute(attrName, value.toUtf8());
0063     }
0064     /**
0065      * Add an attribute whose value is an integer
0066      */
0067     inline void addAttribute(const char* attrName, int value) {
0068         addAttribute(attrName, QByteArray::number(value));
0069     }
0070     /**
0071      * Add an attribute whose value is an unsigned integer
0072      */
0073     inline void addAttribute(const char* attrName, uint value) {
0074         addAttribute(attrName, QByteArray::number(value));
0075     }
0076     /**
0077      * Add an attribute whose value is an bool
0078      * It is written as "true" or "false" based on value
0079      */
0080     inline void addAttribute(const char* attrName, bool value) {
0081         addAttribute(attrName, value ? "true" : "false");
0082     }
0083     /**
0084      * Add an attribute whose value is a floating point number
0085      * The number is written out with the highest possible precision
0086      * (unlike QString::number and setNum, which default to 6 digits)
0087      */
0088     void addAttribute(const char* attrName, double value);
0089 
0090     /**
0091      * Add an attribute whose value is a floating point number
0092      * The number is written out with the highest possible precision
0093      * (unlike QString::number and setNum, which default to 6 digits)
0094      */
0095     void addAttribute(const char* attrName, float value);
0096 
0097     /// Overloaded version of the one taking a const char* argument, for convenience
0098     void addAttribute(const char* attrName, const QByteArray& value);
0099 
0100     /**
0101      * Add an attribute to the current element.
0102      */
0103     void addAttribute(const char* attrName, const char* value);
0104 
0105     /**
0106      * Terminate the current element. After this you should start a new one (sibling),
0107      * add a sibling text node, or close another one (end of siblings).
0108      */
0109     void endElement();
0110 
0111     /**
0112      * Overloaded version of addTextNode( const char* ),
0113      * which is a bit slower because it needs to convert @p str to utf8 first.
0114      */
0115     inline void addTextNode(const QString& str) {
0116         addTextNode(str.toUtf8());
0117     }
0118 
0119     /// Overloaded version of the one taking a const char* argument
0120     void addTextNode(const QByteArray& cstr);
0121 
0122     /**
0123      * @brief Adds a text node as a child of the current element.
0124      *
0125      * This is appends the literal content of @p str to the contents of the element.
0126      * E.g. addTextNode( "foo" ) inside a \<p\> element gives \<p\>foo\</p\>,
0127      * and startElement( "b" ); endElement( "b" ); addTextNode( "foo" ) gives \<p\>\<b/\>foo\</p\>
0128      */
0129     void addTextNode(const char* cstr);
0130 
0131     /**
0132      * This is quite a special-purpose method, not for everyday use.
0133      * It adds a complete element (with its attributes and child elements)
0134      * as a child of the current element. The iodevice is supposed to be escaped
0135      * for XML already, so it will usually come from another KoXmlWriter.
0136      * This is usually used with KTempFile.
0137      */
0138     void addCompleteElement(QIODevice* dev);
0139 
0140     // #### Maybe we want to subclass KoXmlWriter for manifest files.
0141     /**
0142      * Special helper for writing "manifest" files
0143      * This is equivalent to startElement/2*addAttribute/endElement
0144      * This API will probably have to change (or not be used anymore)
0145      * when we add support for encrypting/signing.
0146      * @note OASIS-specific
0147      */
0148     void addManifestEntry(const QString& fullPath, const QString& mediaType);
0149 
0150 private:
0151     struct Tag {
0152         Tag(const char *t = 0, bool ind = true)
0153             : hasChildren(false)
0154             , lastChildIsText(false)
0155             , openingTagClosed(false)
0156             , indentInside(ind)
0157         {
0158             tagName = new char[qstrlen(t) + 1];
0159             qstrcpy(tagName, t);
0160         }
0161 
0162         ~Tag() {
0163             delete[] tagName;
0164         }
0165 
0166         Tag(const Tag &original)
0167         {
0168             tagName = new char[qstrlen(original.tagName) + 1];
0169             qstrcpy(tagName, original.tagName);
0170 
0171             hasChildren = original.hasChildren;
0172             lastChildIsText = original.lastChildIsText;
0173             openingTagClosed = original.openingTagClosed;
0174             indentInside = original.indentInside;
0175         }
0176 
0177         char *tagName {0};
0178         bool hasChildren : 1; ///< element or text children
0179         bool lastChildIsText : 1; ///< last child is a text node
0180         bool openingTagClosed : 1; ///< true once the '\>' in \<tag a="b"\> is written out
0181         bool indentInside : 1; ///< whether to indent the contents of this tag
0182     };
0183 
0184     /// Write out \n followed by the number of spaces required.
0185     void writeIndent();
0186 
0187     void writeCString(const char* cstr);
0188 
0189     void writeChar(char c);
0190 
0191 
0192     inline void closeStartElement(Tag& tag) {
0193         if (!tag.openingTagClosed) {
0194             tag.openingTagClosed = true;
0195             writeChar('>');
0196         }
0197     }
0198     char *escapeForXML(const char* source, int length) const;
0199     bool prepareForChild(bool indentInside = true);
0200     void prepareForTextNode();
0201 
0202     class Private;
0203     Private * const d;
0204 
0205     KoXmlWriter(const KoXmlWriter &);   // forbidden
0206     KoXmlWriter& operator=(const KoXmlWriter &);   // forbidden
0207 };
0208 
0209 #endif /* XMLWRITER_H */
0210