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