|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the test suite of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <QCoreApplication> |
|
43 #include <QDateTime> |
|
44 #include <QIODevice> |
|
45 #include <QList> |
|
46 #include <QPair> |
|
47 #include <QStack> |
|
48 #include <QtDebug> |
|
49 |
|
50 #include "XMLWriter.h" |
|
51 |
|
52 /* Issues: |
|
53 * - Switch to Qt's d-pointer semantics, if in Qt. |
|
54 * - Remove namespace(PatternistSDK), and change name, if in Qt. |
|
55 * - Is it really necessary to pass the tag name to endElement()? |
|
56 * - Could it be of interest to let the user control the encoding? Are those cases common |
|
57 * enough to justify support in Qt? Using anything but UTF-8 or UTF-16 |
|
58 * means asking for trouble, from an interoperability perspective. |
|
59 */ |
|
60 |
|
61 /* Design rationalis, comments: |
|
62 * |
|
63 * - The class is called XMLWriter to harvest familarity by being consistent with |
|
64 * Java's XMLWriter class. If XMLWriter is moved to Qt, the name QXmlWriter is perhaps suitable. |
|
65 * - The class does not handle indentation because the "do one thing well"-principle is |
|
66 * in use. XMLWriter should be fast and not assume a certain idea of indentation. Indentation |
|
67 * should be implemented in a standalone QXmlContentHandler that performs the indentation and |
|
68 * "has a" QXmlContentHandler which it in addition calls, and by that proxying/piping another |
|
69 * QXmlContentHandler(which most likely is an XMLWriter). Thus, achieving a modularized, |
|
70 * flexibly approach to indentation. A reason is also that indentation is very subjective. |
|
71 * The indenter class should probably be called XMLIndenter/QXmlIndenter. |
|
72 * - It could be of interest to implement QXmlDTDHandler such that it would be possible to serialize |
|
73 * DTDs. Must be done before BC becomes significant. |
|
74 * - I think the most valuable of this class is its Q_ASSERT tests. Many programmers have severe problems |
|
75 * producing XML, and the tests helps them catching their mistakes. They therefore promote |
|
76 * interoperability. Do not remove them. If any are wrong, fix them instead. |
|
77 */ |
|
78 |
|
79 using namespace QPatternistSDK; |
|
80 |
|
81 /** |
|
82 * A namespace binding, prefix/namespace URI. |
|
83 */ |
|
84 typedef QPair<QString, QString> NSBinding; |
|
85 typedef QList<NSBinding> NSBindingList; |
|
86 |
|
87 #ifdef QT_NO_DEBUG |
|
88 # define DEBUG_CODE(code) |
|
89 #else |
|
90 # define DEBUG_CODE(code) code |
|
91 #endif |
|
92 |
|
93 class XMLWriter::Private |
|
94 { |
|
95 public: |
|
96 inline Private(QIODevice *devP) : insideCDATA(false), |
|
97 addModificationNote(false), |
|
98 dev(devP) |
|
99 { |
|
100 hasContentStack.push(true); |
|
101 } |
|
102 |
|
103 #ifdef QT_NO_DEBUG |
|
104 inline void validateQName(const QString &) const |
|
105 { |
|
106 } |
|
107 |
|
108 inline void verifyNS(const QString &) const |
|
109 { |
|
110 } |
|
111 #else |
|
112 /** |
|
113 * Simple test of that @p name is an acceptable QName. |
|
114 */ |
|
115 inline void validateQName(const QString &name) |
|
116 { |
|
117 Q_ASSERT_X(!name.isEmpty(), Q_FUNC_INFO, |
|
118 "An XML name cannot be empty."); |
|
119 Q_ASSERT_X(!name.endsWith(QLatin1Char(':')), Q_FUNC_INFO, |
|
120 "An XML name cannot end with a colon(QLatin1Char(':'))."); |
|
121 Q_ASSERT_X(!name.contains(QRegExp(QLatin1String("[ \t\n]"))), Q_FUNC_INFO, |
|
122 "An XML name cannot contain whitespace."); |
|
123 } |
|
124 |
|
125 /** |
|
126 * Ensures that the prefix of @p qName is declared. |
|
127 */ |
|
128 inline void verifyNS(const QString &qName) const |
|
129 { |
|
130 const QString prefix(qName.left(qName.indexOf(QLatin1Char(':')))); |
|
131 |
|
132 if(qName.contains(QLatin1Char(':')) && prefix != QLatin1String("xml")) |
|
133 { |
|
134 bool foundPrefix = false; |
|
135 const QStack<NSBindingList>::const_iterator end(namespaceTracker.constEnd()); |
|
136 QStack<NSBindingList>::const_iterator it(namespaceTracker.constBegin()); |
|
137 |
|
138 for(; it != end; ++it) |
|
139 { |
|
140 const NSBindingList::const_iterator lend((*it).constEnd()); |
|
141 NSBindingList::const_iterator lit((*it).constBegin()); |
|
142 |
|
143 for(; lit != lend; ++it) |
|
144 { |
|
145 if((*lit).first == prefix) |
|
146 { |
|
147 foundPrefix = true; |
|
148 break; |
|
149 } |
|
150 } |
|
151 if(foundPrefix) |
|
152 break; |
|
153 } |
|
154 |
|
155 Q_ASSERT_X(foundPrefix, "XMLWriter::startElement()", |
|
156 qPrintable(QString::fromLatin1("The prefix %1 is not declared. All prefixes " |
|
157 "except 'xml' must be declared.").arg(prefix))); |
|
158 } |
|
159 } |
|
160 #endif |
|
161 |
|
162 inline QString escapeElementContent(const QString &ch) |
|
163 { |
|
164 const int l = ch.length(); |
|
165 QString retval; |
|
166 |
|
167 for(int i = 0; i != l; ++i) |
|
168 { |
|
169 const QChar c(ch.at(i)); |
|
170 |
|
171 if(c == QLatin1Char(QLatin1Char('&'))) |
|
172 retval += QLatin1String("&"); |
|
173 else if(c == QLatin1Char(QLatin1Char('<'))) |
|
174 retval += QLatin1String("<"); |
|
175 else |
|
176 retval += c; |
|
177 } |
|
178 |
|
179 return retval; |
|
180 } |
|
181 |
|
182 inline QString escapeAttributeContent(const QString &ch) |
|
183 { |
|
184 const int l = ch.length(); |
|
185 QString retval; |
|
186 |
|
187 for(int i = 0; i != l; ++i) |
|
188 { |
|
189 const QChar c(ch.at(i)); |
|
190 |
|
191 /* We don't have to escape '\'' because we use '\"' as attribute delimiter. */ |
|
192 if(c == QLatin1Char('&')) |
|
193 retval += QLatin1String("&"); |
|
194 else if(c == QLatin1Char('<')) |
|
195 retval += QLatin1String("<"); |
|
196 else if(c == QLatin1Char('"')) |
|
197 retval += QLatin1String("""); |
|
198 else |
|
199 retval += c; |
|
200 } |
|
201 |
|
202 return retval; |
|
203 } |
|
204 |
|
205 inline QString escapeCDATAContent(const QString &ch) |
|
206 { |
|
207 const int l = ch.length(); |
|
208 QString retval; |
|
209 qint8 atEnd = 0; |
|
210 |
|
211 for(int i = 0; i != l; ++i) |
|
212 { |
|
213 const QChar c(ch.at(i)); |
|
214 |
|
215 /* Escape '>' if in "]]>" */ |
|
216 if(c == QLatin1Char(']')) |
|
217 { |
|
218 if(atEnd == 0 || atEnd == 1) |
|
219 ++atEnd; |
|
220 else |
|
221 atEnd = 0; |
|
222 |
|
223 retval += QLatin1Char(']'); |
|
224 } |
|
225 else if(c == QLatin1Char('>')) |
|
226 { |
|
227 if(atEnd == 2) |
|
228 retval += QLatin1String(">"); |
|
229 else |
|
230 { |
|
231 atEnd = 0; |
|
232 retval += QLatin1Char('>'); |
|
233 } |
|
234 } |
|
235 else |
|
236 retval += c; |
|
237 } |
|
238 |
|
239 return retval; |
|
240 } |
|
241 |
|
242 /** |
|
243 * We wrap dev in this function such that we can deploy the Q_ASSERT_X |
|
244 * macro in each place it's used. |
|
245 */ |
|
246 inline QIODevice *device() const |
|
247 { |
|
248 Q_ASSERT_X(dev, Q_FUNC_INFO, |
|
249 "No device specified for XMLWriter; one must be specified with " |
|
250 "setDevice() or via the constructor before XMLWriter can be used."); |
|
251 return dev; |
|
252 } |
|
253 |
|
254 /** |
|
255 * @returns true on success, otherwise false |
|
256 */ |
|
257 inline bool serialize(const QString &data) |
|
258 { |
|
259 const QByteArray utf8(data.toUtf8()); |
|
260 |
|
261 return device()->write(utf8) == utf8.size(); |
|
262 } |
|
263 |
|
264 /** |
|
265 * @returns true on success, otherwise false |
|
266 */ |
|
267 inline bool serialize(const char data) |
|
268 { |
|
269 return device()->putChar(data); |
|
270 } |
|
271 |
|
272 /** |
|
273 * @returns true on success, otherwise false |
|
274 */ |
|
275 inline bool serialize(const char *data) |
|
276 { |
|
277 return device()->write(data) == qstrlen(data); |
|
278 } |
|
279 |
|
280 inline bool hasElementContent() const |
|
281 { |
|
282 return hasContentStack.top(); |
|
283 } |
|
284 |
|
285 inline void handleElement() |
|
286 { |
|
287 if(!hasElementContent()) |
|
288 serialize('>'); |
|
289 |
|
290 /* This element is content for the parent. */ |
|
291 hasContentStack.top() = true; |
|
292 } |
|
293 |
|
294 NSBindingList namespaces; |
|
295 bool insideCDATA; |
|
296 bool addModificationNote; |
|
297 QString msg; |
|
298 QIODevice *dev; |
|
299 QStack<bool> hasContentStack; |
|
300 QString errorString; |
|
301 DEBUG_CODE(QStack<QString> tags;) |
|
302 DEBUG_CODE(QStack<NSBindingList> namespaceTracker;) |
|
303 }; |
|
304 |
|
305 /** |
|
306 * Reduces complexity. The empty else clause is for avoiding mess when macro |
|
307 * is used in the 'then' branch of an if clause, which is followed by an else clause. |
|
308 */ |
|
309 #define serialize(string) if(!d->serialize(string)) \ |
|
310 { \ |
|
311 d->errorString = d->device()->errorString(); \ |
|
312 return false; \ |
|
313 } \ |
|
314 else do {} while (false) |
|
315 |
|
316 XMLWriter::XMLWriter(QIODevice *outStream) : d(new Private(outStream)) |
|
317 { |
|
318 } |
|
319 |
|
320 XMLWriter::~XMLWriter() |
|
321 { |
|
322 delete d; |
|
323 } |
|
324 |
|
325 bool XMLWriter::startDocument() |
|
326 { |
|
327 if(!device()->isOpen() && !device()->open(QIODevice::WriteOnly)) |
|
328 return false; |
|
329 |
|
330 if(d->addModificationNote) |
|
331 { |
|
332 if(d->msg.isNull()) |
|
333 { |
|
334 d->msg = QString::fromLatin1("NOTE: This file was automatically generated " |
|
335 "by %1 at %2. All changes to this file will be lost.") |
|
336 .arg(QCoreApplication::instance()->applicationName(), |
|
337 QDateTime::currentDateTime().toString()); |
|
338 } |
|
339 if(!comment(d->msg)) |
|
340 return false; |
|
341 |
|
342 serialize('\n'); |
|
343 } |
|
344 |
|
345 serialize(QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")); |
|
346 |
|
347 return true; |
|
348 } |
|
349 |
|
350 bool XMLWriter::startElement(const QString &/*namespaceURI*/, |
|
351 const QString &/*localName*/, |
|
352 const QString &qName, |
|
353 const QXmlAttributes &atts) |
|
354 { |
|
355 return startElement(qName, atts); |
|
356 } |
|
357 |
|
358 bool XMLWriter::startElement(const QString &qName, |
|
359 const QXmlAttributes &atts) |
|
360 { |
|
361 Q_ASSERT_X(!d->insideCDATA, Q_FUNC_INFO, |
|
362 "Only characters() can be received when inside CDATA."); |
|
363 Q_ASSERT_X(!qName.startsWith(QLatin1String("xmlns")), Q_FUNC_INFO, |
|
364 "startElement should not be used for declaring prefixes, " |
|
365 "use startPrefixMapping() for that."); |
|
366 |
|
367 d->validateQName(qName); |
|
368 d->verifyNS(qName); |
|
369 |
|
370 d->handleElement(); |
|
371 |
|
372 serialize('<'); |
|
373 serialize(qName); |
|
374 |
|
375 DEBUG_CODE(d->tags.push(qName)); |
|
376 DEBUG_CODE(d->namespaceTracker.push(d->namespaces)); |
|
377 |
|
378 /* Add namespace declarations. */ |
|
379 const NSBindingList::const_iterator end(d->namespaces.constEnd()); |
|
380 NSBindingList::const_iterator it(d->namespaces.constBegin()); |
|
381 |
|
382 for(; it != end; ++it) |
|
383 { |
|
384 if((*it).first.isEmpty()) |
|
385 serialize(" xmlns="); |
|
386 else |
|
387 { |
|
388 serialize(" xmlns:"); |
|
389 serialize((*it).first); |
|
390 serialize('='); |
|
391 } |
|
392 |
|
393 serialize('"'); |
|
394 serialize(d->escapeElementContent((*it).second)); |
|
395 serialize('"'); |
|
396 } |
|
397 d->namespaces.clear(); |
|
398 |
|
399 const int c = atts.count(); |
|
400 |
|
401 /* Serialize attributes. */ |
|
402 for(int i = 0; i != c; ++i) |
|
403 { |
|
404 d->validateQName(atts.qName(i)); |
|
405 d->verifyNS(atts.qName(i)); |
|
406 |
|
407 serialize(' '); |
|
408 serialize(atts.qName(i)); |
|
409 serialize("=\""); |
|
410 serialize(d->escapeAttributeContent(atts.value(i))); |
|
411 serialize('"'); |
|
412 } |
|
413 |
|
414 d->hasContentStack.push(false); |
|
415 return true; |
|
416 } |
|
417 |
|
418 bool XMLWriter::endElement(const QString &/*namespaceURI*/, |
|
419 const QString &/*localName*/, |
|
420 const QString &qName) |
|
421 { |
|
422 return endElement(qName); |
|
423 } |
|
424 |
|
425 bool XMLWriter::endElement(const QString &qName) |
|
426 { |
|
427 Q_ASSERT_X(!d->insideCDATA, Q_FUNC_INFO, |
|
428 "Only characters() can be received when inside CDATA."); |
|
429 Q_ASSERT_X(d->tags.pop() == qName, Q_FUNC_INFO, |
|
430 "The element tags are not balanced, the produced XML is invalid."); |
|
431 |
|
432 DEBUG_CODE(d->namespaceTracker.pop()); |
|
433 |
|
434 /* "this" element is content for our parent, so ensure hasElementContent is true. */ |
|
435 |
|
436 if(d->hasElementContent()) |
|
437 { |
|
438 serialize(QLatin1String("</")); |
|
439 serialize(qName); |
|
440 serialize('>'); |
|
441 } |
|
442 else |
|
443 serialize(QLatin1String("/>")); |
|
444 |
|
445 d->hasContentStack.pop(); |
|
446 |
|
447 return true; |
|
448 } |
|
449 |
|
450 bool XMLWriter::startPrefixMapping(const QString &prefix, const QString &uri) |
|
451 { |
|
452 Q_ASSERT_X(!d->insideCDATA, Q_FUNC_INFO, |
|
453 "Only characters() can be received when inside CDATA."); |
|
454 Q_ASSERT_X(prefix.toLower() != QLatin1String("xml") || |
|
455 (prefix.toLower() == QLatin1String("xml") && |
|
456 (uri == QLatin1String("http://www.w3.org/TR/REC-xml-names/") || |
|
457 uri.isEmpty())), |
|
458 Q_FUNC_INFO, |
|
459 "The prefix 'xml' can only be bound to the namespace " |
|
460 "\"http://www.w3.org/TR/REC-xml-names/\"."); |
|
461 Q_ASSERT_X(prefix.toLower() != QLatin1String("xml") && |
|
462 uri != QLatin1String("http://www.w3.org/TR/REC-xml-names/"), |
|
463 Q_FUNC_INFO, |
|
464 "The namespace \"http://www.w3.org/TR/REC-xml-names/\" can only be bound to the " |
|
465 "\"xml\" prefix."); |
|
466 |
|
467 d->namespaces.append(qMakePair(prefix, uri)); |
|
468 return true; |
|
469 } |
|
470 |
|
471 bool XMLWriter::processingInstruction(const QString &target, |
|
472 const QString &data) |
|
473 { |
|
474 Q_ASSERT_X(target.toLower() != QLatin1String("xml"), Q_FUNC_INFO, |
|
475 "A processing instruction cannot have the name xml in any " |
|
476 "capitalization, because it is reserved."); |
|
477 Q_ASSERT_X(!data.contains(QLatin1String("?>")), Q_FUNC_INFO, |
|
478 "The content of a processing instruction cannot contain the string \"?>\"."); |
|
479 Q_ASSERT_X(!d->insideCDATA, "XMLWriter::processingInstruction()", |
|
480 "Only characters() can be received when inside CDATA."); |
|
481 |
|
482 d->handleElement(); |
|
483 |
|
484 serialize(QLatin1String("<?")); |
|
485 serialize(target); |
|
486 serialize(' '); |
|
487 serialize(data); |
|
488 serialize(QLatin1String("?>")); |
|
489 return true; |
|
490 } |
|
491 |
|
492 bool XMLWriter::characters(const QString &ch) |
|
493 { |
|
494 Q_ASSERT_X(d->tags.count() >= 1, Q_FUNC_INFO, |
|
495 "Text nodes can only appear inside elements(no elements sent)."); |
|
496 d->handleElement(); |
|
497 |
|
498 if(d->insideCDATA) |
|
499 serialize(d->escapeCDATAContent(ch)); |
|
500 else |
|
501 serialize(d->escapeElementContent(ch)); |
|
502 |
|
503 return true; |
|
504 } |
|
505 |
|
506 bool XMLWriter::comment(const QString &ch) |
|
507 { |
|
508 Q_ASSERT_X(!d->insideCDATA, Q_FUNC_INFO, |
|
509 "Only characters() can be received when inside CDATA."); |
|
510 Q_ASSERT_X(!ch.contains(QLatin1String("--")), Q_FUNC_INFO, |
|
511 "XML comments may not contain double-hyphens(\"--\")."); |
|
512 Q_ASSERT_X(!ch.endsWith(QLatin1Char('-')), Q_FUNC_INFO, |
|
513 "XML comments cannot end with a hyphen, \"-\"(add a space, for example)."); |
|
514 /* A comment starting with "<!---" is ok. */ |
|
515 |
|
516 d->handleElement(); |
|
517 |
|
518 serialize(QLatin1String("<!--")); |
|
519 serialize(ch); |
|
520 serialize(QLatin1String("-->")); |
|
521 |
|
522 return true; |
|
523 } |
|
524 |
|
525 bool XMLWriter::startCDATA() |
|
526 { |
|
527 Q_ASSERT_X(d->insideCDATA, Q_FUNC_INFO, |
|
528 "startCDATA() has already been called."); |
|
529 Q_ASSERT_X(d->tags.count() >= 1, Q_FUNC_INFO, |
|
530 "CDATA sections can only appear inside elements(no elements sent)."); |
|
531 d->insideCDATA = true; |
|
532 serialize(QLatin1String("<![CDATA[")); |
|
533 return true; |
|
534 } |
|
535 |
|
536 bool XMLWriter::endCDATA() |
|
537 { |
|
538 d->insideCDATA = false; |
|
539 serialize("]]>"); |
|
540 return true; |
|
541 } |
|
542 |
|
543 bool XMLWriter::startDTD(const QString &name, |
|
544 const QString &publicId, |
|
545 const QString &systemId) |
|
546 { |
|
547 Q_ASSERT_X(!d->insideCDATA, Q_FUNC_INFO, |
|
548 "Only characters() can be received when inside CDATA."); |
|
549 Q_ASSERT_X(!name.isEmpty(), Q_FUNC_INFO, |
|
550 "The DOCTYPE name cannot be empty."); |
|
551 Q_ASSERT_X(d->tags.isEmpty() && d->namespaces.isEmpty(), Q_FUNC_INFO, |
|
552 "No content such as namespace declarations or elements can be serialized " |
|
553 "before the DOCTYPE declaration, the XML is invalid."); |
|
554 Q_ASSERT_X(!publicId.contains(QLatin1Char('"')), Q_FUNC_INFO, |
|
555 "The PUBLIC ID cannot contain quotes('\"')."); |
|
556 Q_ASSERT_X(!systemId.contains(QLatin1Char('"')), Q_FUNC_INFO, |
|
557 "The SYSTEM ID cannot contain quotes('\"')."); |
|
558 |
|
559 serialize(QLatin1String("<!DOCTYPE ")); |
|
560 serialize(name); |
|
561 |
|
562 if(!publicId.isEmpty()) |
|
563 { |
|
564 Q_ASSERT_X(!systemId.isEmpty(), Q_FUNC_INFO, |
|
565 "When a public identifier is specified, a system identifier " |
|
566 "must also be specified in order to produce valid XML."); |
|
567 serialize(" PUBLIC \""); |
|
568 serialize(publicId); |
|
569 serialize('"'); |
|
570 } |
|
571 |
|
572 if(!systemId.isEmpty()) |
|
573 { |
|
574 if(publicId.isEmpty()) |
|
575 serialize(" SYSTEM"); |
|
576 |
|
577 serialize(" \""); |
|
578 serialize(systemId); |
|
579 serialize('"'); |
|
580 } |
|
581 |
|
582 return true; |
|
583 } |
|
584 |
|
585 bool XMLWriter::endDTD() |
|
586 { |
|
587 Q_ASSERT_X(d->tags.isEmpty() && d->namespaces.isEmpty(), Q_FUNC_INFO, |
|
588 "Content such as namespace declarations or elements cannot occur inside " |
|
589 "the DOCTYPE declaration, the XML is invalid."); |
|
590 serialize(QLatin1String(">\n")); |
|
591 return true; |
|
592 } |
|
593 |
|
594 bool XMLWriter::startEntity(const QString &) |
|
595 { |
|
596 return true; |
|
597 } |
|
598 |
|
599 bool XMLWriter::endEntity(const QString &) |
|
600 { |
|
601 return true; |
|
602 } |
|
603 |
|
604 void XMLWriter::setMessage(const QString &msg) |
|
605 { |
|
606 d->msg = msg; |
|
607 } |
|
608 |
|
609 QString XMLWriter::modificationMessage() const |
|
610 { |
|
611 return d->msg; |
|
612 } |
|
613 |
|
614 bool XMLWriter::endDocument() |
|
615 { |
|
616 Q_ASSERT_X(d->tags.isEmpty(), Q_FUNC_INFO, |
|
617 "endDocument() called before all elements were closed with endElement()."); |
|
618 d->device()->close(); |
|
619 return true; |
|
620 } |
|
621 |
|
622 QString XMLWriter::errorString() const |
|
623 { |
|
624 return d->errorString; |
|
625 } |
|
626 |
|
627 bool XMLWriter::ignorableWhitespace(const QString &ch) |
|
628 { |
|
629 return characters(ch); |
|
630 } |
|
631 |
|
632 bool XMLWriter::endPrefixMapping(const QString &) |
|
633 { |
|
634 /* Again, should we do something with this? */ |
|
635 return true; |
|
636 } |
|
637 |
|
638 bool XMLWriter::skippedEntity(const QString &) |
|
639 { |
|
640 return true; |
|
641 } |
|
642 |
|
643 void XMLWriter::setDocumentLocator(QXmlLocator *) |
|
644 { |
|
645 } |
|
646 |
|
647 QIODevice *XMLWriter::device() const |
|
648 { |
|
649 return d->dev; |
|
650 } |
|
651 |
|
652 void XMLWriter::setDevice(QIODevice *dev) |
|
653 { |
|
654 d->dev = dev; |
|
655 } |
|
656 |
|
657 void XMLWriter::setAddMessage(const bool toggle) |
|
658 { |
|
659 d->addModificationNote = toggle; |
|
660 } |
|
661 |
|
662 bool XMLWriter::addModificationMessage() const |
|
663 { |
|
664 return d->addModificationNote; |
|
665 } |
|
666 |
|
667 #undef serialize |
|
668 // vim: et:ts=4:sw=4:sts=4 |
|
669 |