1 /* This file is part of the KDE project 2 Copyright (C) 2004 David Faure <faure@kde.org> 3 Copyright (C) 2007 Thomas Zander <zander@kde.org> 4 Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com> 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Library General Public 8 License as published by the Free Software Foundation; either 9 version 2 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Library General Public License for more details. 15 16 You should have received a copy of the GNU Library General Public License 17 along with this library; see the file COPYING.LIB. If not, write to 18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #ifndef XMLWRITER_H 23 #define XMLWRITER_H 24 25 #include <QMap> 26 #include <QIODevice> 27 #include "kritastore_export.h" 28 29 /** 30 * A class for writing out XML (to any QIODevice), with a special attention on performance. 31 * The XML is being written out along the way, which avoids requiring the entire 32 * document in memory (like QDom does). 33 */ 34 class KRITASTORE_EXPORT KoXmlWriter 35 { 36 public: 37 /** 38 * Create a KoXmlWriter instance to write out an XML document into 39 * the given QIODevice. 40 */ 41 explicit KoXmlWriter(QIODevice* dev, int indentLevel = 0); 42 43 /// Destructor 44 ~KoXmlWriter(); 45 46 QIODevice *device() const; 47 48 /** 49 * Start the XML document. 50 * This writes out the \<?xml?\> tag with utf8 encoding, and the DOCTYPE. 51 * @param rootElemName the name of the root element, used in the DOCTYPE tag. 52 * @param publicId the public identifier, e.g. "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" 53 * @param systemId the system identifier, e.g. "office.dtd" or a full URL to it. 54 */ 55 void startDocument(const char* rootElemName, const char* publicId = 0, const char* systemId = 0); 56 57 /// Call this to terminate an XML document. 58 void endDocument(); 59 60 /** 61 * Start a new element, as a child of the current element. 62 * @param tagName the name of the tag. Warning: this string must 63 * remain alive until endElement, no copy is internally made. 64 * Usually tagName is a string constant so this is no problem anyway. 65 * @param indentInside if set to false, there will be no indentation inside 66 * this tag. This is useful for elements where whitespace matters. 67 */ 68 void startElement(const char* tagName, bool indentInside = true); 69 70 /** 71 * Overloaded version of addAttribute( const char*, const char* ), 72 * which is a bit slower because it needs to convert @p value to utf8 first. 73 */ addAttribute(const char * attrName,const QString & value)74 inline void addAttribute(const char* attrName, const QString& value) { 75 addAttribute(attrName, value.toUtf8()); 76 } 77 /** 78 * Add an attribute whose value is an integer 79 */ addAttribute(const char * attrName,int value)80 inline void addAttribute(const char* attrName, int value) { 81 addAttribute(attrName, QByteArray::number(value)); 82 } 83 /** 84 * Add an attribute whose value is an unsigned integer 85 */ addAttribute(const char * attrName,uint value)86 inline void addAttribute(const char* attrName, uint value) { 87 addAttribute(attrName, QByteArray::number(value)); 88 } 89 /** 90 * Add an attribute whose value is an bool 91 * It is written as "true" or "false" based on value 92 */ addAttribute(const char * attrName,bool value)93 inline void addAttribute(const char* attrName, bool value) { 94 addAttribute(attrName, value ? "true" : "false"); 95 } 96 /** 97 * Add an attribute whose value is a floating point number 98 * The number is written out with the highest possible precision 99 * (unlike QString::number and setNum, which default to 6 digits) 100 */ 101 void addAttribute(const char* attrName, double value); 102 /** 103 * Add an attribute whose value is a floating point number 104 * The number is written out with the highest possible precision 105 * (unlike QString::number and setNum, which default to 6 digits) 106 */ 107 void addAttribute(const char* attrName, float value); 108 109 /// Overloaded version of the one taking a const char* argument, for convenience 110 void addAttribute(const char* attrName, const QByteArray& value); 111 112 /** 113 * Add an attribute to the current element. 114 */ 115 void addAttribute(const char* attrName, const char* value); 116 /** 117 * Terminate the current element. After this you should start a new one (sibling), 118 * add a sibling text node, or close another one (end of siblings). 119 */ 120 void endElement(); 121 /** 122 * Overloaded version of addTextNode( const char* ), 123 * which is a bit slower because it needs to convert @p str to utf8 first. 124 */ addTextNode(const QString & str)125 inline void addTextNode(const QString& str) { 126 addTextNode(str.toUtf8()); 127 } 128 /// Overloaded version of the one taking a const char* argument 129 void addTextNode(const QByteArray& cstr); 130 /** 131 * @brief Adds a text node as a child of the current element. 132 * 133 * This is appends the literal content of @p str to the contents of the element. 134 * E.g. addTextNode( "foo" ) inside a \<p\> element gives \<p\>foo\</p\>, 135 * and startElement( "b" ); endElement( "b" ); addTextNode( "foo" ) gives \<p\>\<b/\>foo\</p\> 136 */ 137 void addTextNode(const char* cstr); 138 139 /** 140 * @brief Adds a processing instruction 141 * 142 * This writes a processing instruction, like <?foo bar blah?>, where foo 143 * is the target, and the rest is the data. 144 * 145 * Processing instructions are used in XML to keep processor-specific 146 * information in the text of the document. 147 */ 148 void addProcessingInstruction(const char* cstr); 149 150 /** 151 * This is quite a special-purpose method, not for everyday use. 152 * It adds a complete element (with its attributes and child elements) 153 * as a child of the current element. The string is supposed to be escaped 154 * for XML already, so it will usually come from another KoXmlWriter. 155 */ 156 void addCompleteElement(const char* cstr); 157 158 /** 159 * This is quite a special-purpose method, not for everyday use. 160 * It adds a complete element (with its attributes and child elements) 161 * as a child of the current element. The iodevice is supposed to be escaped 162 * for XML already, so it will usually come from another KoXmlWriter. 163 * This is usually used with KTempFile. 164 */ 165 void addCompleteElement(QIODevice* dev); 166 167 // #### Maybe we want to subclass KoXmlWriter for manifest files. 168 /** 169 * Special helper for writing "manifest" files 170 * This is equivalent to startElement/2*addAttribute/endElement 171 * This API will probably have to change (or not be used anymore) 172 * when we add support for encrypting/signing. 173 * @note OASIS-specific 174 */ 175 void addManifestEntry(const QString& fullPath, const QString& mediaType); 176 177 /** 178 * Special helper for writing config item into settings.xml 179 * @note OASIS-specific 180 */ 181 void addConfigItem(const QString & configName, const QString& value); 182 /// @note OASIS-specific 183 void addConfigItem(const QString & configName, bool value); 184 /// @note OASIS-specific 185 void addConfigItem(const QString & configName, int value); 186 /// @note OASIS-specific 187 void addConfigItem(const QString & configName, double value); 188 /// @note OASIS-specific 189 void addConfigItem(const QString & configName, float value); 190 /// @note OASIS-specific 191 void addConfigItem(const QString & configName, long value); 192 /// @note OASIS-specific 193 void addConfigItem(const QString & configName, short value); 194 195 // TODO addConfigItem for datetime and base64Binary 196 197 /** 198 * @brief Adds a text span as nodes of the current element. 199 * 200 * Unlike KoXmlWriter::addTextNode it handles tabulations, linebreaks, 201 * and multiple spaces by using the appropriate OASIS tags. 202 * 203 * @param text the text to write 204 * 205 * @note OASIS-specific 206 */ 207 void addTextSpan(const QString& text); 208 /** 209 * Overloaded version of addTextSpan which takes an additional tabCache map. 210 * @param text the text to write 211 * @param tabCache optional map allowing to find a tab for a given character index 212 * @note OASIS-specific 213 */ 214 void addTextSpan(const QString& text, const QMap<int, int>& tabCache); 215 216 /** 217 * @return the current indentation level. 218 * Useful when creating a sub-KoXmlWriter (see addCompleteElement) 219 */ 220 int indentLevel() const; 221 222 /** 223 * Return all the open tags at this time, root element first. 224 */ 225 QList<const char*> tagHierarchy() const; 226 227 /** 228 * Return the so far written XML as string for debugging purposes. 229 */ 230 QString toString() const; 231 232 private: 233 struct Tag { 234 Tag(const char* t = 0, bool ind = true) tagNameTag235 : tagName(t), hasChildren(false), lastChildIsText(false), 236 openingTagClosed(false), indentInside(ind) {} TagTag237 Tag(const Tag &original) 238 { 239 tagName = original.tagName; 240 hasChildren = original.hasChildren; 241 lastChildIsText = original.lastChildIsText; 242 openingTagClosed = original.openingTagClosed; 243 indentInside = original.indentInside; 244 } 245 const char* tagName; 246 bool hasChildren : 1; ///< element or text children 247 bool lastChildIsText : 1; ///< last child is a text node 248 bool openingTagClosed : 1; ///< true once the '\>' in \<tag a="b"\> is written out 249 bool indentInside : 1; ///< whether to indent the contents of this tag 250 }; 251 252 /// Write out \n followed by the number of spaces required. 253 void writeIndent(); 254 255 // writeCString is much faster than writeString. 256 // Try to use it as much as possible, especially with constants. 257 void writeString(const QString& str); 258 259 // TODO check return value!!! writeCString(const char * cstr)260 inline void writeCString(const char* cstr) { 261 device()->write(cstr, qstrlen(cstr)); 262 } writeChar(char c)263 inline void writeChar(char c) { 264 device()->putChar(c); 265 } closeStartElement(Tag & tag)266 inline void closeStartElement(Tag& tag) { 267 if (!tag.openingTagClosed) { 268 tag.openingTagClosed = true; 269 writeChar('>'); 270 } 271 } 272 char* escapeForXML(const char* source, int length) const; 273 bool prepareForChild(bool indentInside = true); 274 void prepareForTextNode(); 275 void init(); 276 277 class Private; 278 Private * const d; 279 280 KoXmlWriter(const KoXmlWriter &); // forbidden 281 KoXmlWriter& operator=(const KoXmlWriter &); // forbidden 282 }; 283 284 #endif /* XMLWRITER_H */ 285 286