1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "QtCore/qxmlstream.h"
5
6#if QT_CONFIG(xmlstream)
7
8#include "qxmlutils_p.h"
9#include <qdebug.h>
10#include <qfile.h>
11#include <stdio.h>
12#include <qstringconverter.h>
13#include <qstack.h>
14#include <qbuffer.h>
15#include <qscopeguard.h>
16#include <qcoreapplication.h>
17
18#include <private/qoffsetstringarray_p.h>
19#include <private/qtools_p.h>
20
21#include <iterator>
22#include "qxmlstream_p.h"
23#include "qxmlstreamparser_p.h"
24#include <private/qstringconverter_p.h>
25
26QT_BEGIN_NAMESPACE
27
28using namespace QtPrivate;
29using namespace Qt::StringLiterals;
30using namespace QtMiscUtils;
31
32enum { StreamEOF = ~0U };
33
34namespace {
35template <typename Range>
36auto reversed(Range &r)
37{
38 struct R {
39 Range *r;
40 auto begin() { return std::make_reverse_iterator(std::end(*r)); }
41 auto end() { return std::make_reverse_iterator(std::begin(*r)); }
42 };
43
44 return R{&r};
45}
46
47template <typename Range>
48void reversed(const Range &&) = delete;
49
50// implementation of missing QUtf8StringView methods for ASCII-only needles:
51auto transform(QLatin1StringView haystack, char needle)
52{
53 struct R { QLatin1StringView haystack; char16_t needle; };
54 return R{.haystack: haystack, .needle: uchar(needle)};
55}
56
57auto transform(QStringView haystack, char needle)
58{
59 struct R { QStringView haystack; char16_t needle; };
60 return R{.haystack: haystack, .needle: uchar(needle)};
61}
62
63auto transform(QUtf8StringView haystack, char needle)
64{
65 struct R { QByteArrayView haystack; char needle; };
66 return R{.haystack: haystack, .needle: needle};
67}
68
69auto transform(QLatin1StringView haystack, QLatin1StringView needle)
70{
71 struct R { QLatin1StringView haystack; QLatin1StringView needle; };
72 return R{.haystack: haystack, .needle: needle};
73}
74
75auto transform(QStringView haystack, QLatin1StringView needle)
76{
77 struct R { QStringView haystack; QLatin1StringView needle; };
78 return R{.haystack: haystack, .needle: needle};
79}
80
81auto transform(QUtf8StringView haystack, QLatin1StringView needle)
82{
83 struct R { QLatin1StringView haystack; QLatin1StringView needle; };
84 return R{.haystack: QLatin1StringView{QByteArrayView{haystack}}, .needle: needle};
85}
86
87#define WRAP(method, Needle) \
88 auto method (QAnyStringView s, Needle needle) noexcept \
89 { \
90 return s.visit([needle](auto s) { \
91 auto r = transform(s, needle); \
92 return r.haystack. method (r.needle); \
93 }); \
94 } \
95 /*end*/
96
97WRAP(count, char)
98WRAP(contains, char)
99WRAP(contains, QLatin1StringView)
100WRAP(endsWith, char)
101WRAP(indexOf, QLatin1StringView)
102
103} // unnamed namespace
104
105/*!
106 \enum QXmlStreamReader::TokenType
107
108 This enum specifies the type of token the reader just read.
109
110 \value NoToken The reader has not yet read anything.
111
112 \value Invalid An error has occurred, reported in error() and
113 errorString().
114
115 \value StartDocument The reader reports the XML version number in
116 documentVersion(), and the encoding as specified in the XML
117 document in documentEncoding(). If the document is declared
118 standalone, isStandaloneDocument() returns \c true; otherwise it
119 returns \c false.
120
121 \value EndDocument The reader reports the end of the document.
122
123 \value StartElement The reader reports the start of an element
124 with namespaceUri() and name(). Empty elements are also reported
125 as StartElement, followed directly by EndElement. The convenience
126 function readElementText() can be called to concatenate all
127 content until the corresponding EndElement. Attributes are
128 reported in attributes(), namespace declarations in
129 namespaceDeclarations().
130
131 \value EndElement The reader reports the end of an element with
132 namespaceUri() and name().
133
134 \value Characters The reader reports characters in text(). If the
135 characters are all white-space, isWhitespace() returns \c true. If
136 the characters stem from a CDATA section, isCDATA() returns \c true.
137
138 \value Comment The reader reports a comment in text().
139
140 \value DTD The reader reports a DTD in text(), notation
141 declarations in notationDeclarations(), and entity declarations in
142 entityDeclarations(). Details of the DTD declaration are reported
143 in dtdName(), dtdPublicId(), and dtdSystemId().
144
145 \value EntityReference The reader reports an entity reference that
146 could not be resolved. The name of the reference is reported in
147 name(), the replacement text in text().
148
149 \value ProcessingInstruction The reader reports a processing
150 instruction in processingInstructionTarget() and
151 processingInstructionData().
152*/
153
154/*!
155 \enum QXmlStreamReader::ReadElementTextBehaviour
156
157 This enum specifies the different behaviours of readElementText().
158
159 \value ErrorOnUnexpectedElement Raise an UnexpectedElementError and return
160 what was read so far when a child element is encountered.
161
162 \value IncludeChildElements Recursively include the text from child elements.
163
164 \value SkipChildElements Skip child elements.
165
166 \since 4.6
167*/
168
169/*!
170 \enum QXmlStreamReader::Error
171
172 This enum specifies different error cases
173
174 \value NoError No error has occurred.
175
176 \value CustomError A custom error has been raised with
177 raiseError()
178
179 \value NotWellFormedError The parser internally raised an error
180 due to the read XML not being well-formed.
181
182 \value PrematureEndOfDocumentError The input stream ended before a
183 well-formed XML document was parsed. Recovery from this error is
184 possible if more XML arrives in the stream, either by calling
185 addData() or by waiting for it to arrive on the device().
186
187 \value UnexpectedElementError The parser encountered an element
188 or token that was different to those it expected.
189
190*/
191
192/*!
193 \class QXmlStreamEntityResolver
194 \inmodule QtCore
195 \reentrant
196 \since 4.4
197
198 \brief The QXmlStreamEntityResolver class provides an entity
199 resolver for a QXmlStreamReader.
200
201 \ingroup xml-tools
202 */
203
204/*!
205 Destroys the entity resolver.
206 */
207QXmlStreamEntityResolver::~QXmlStreamEntityResolver()
208{
209}
210
211/*!
212 \internal
213
214This function is a stub for later functionality.
215*/
216QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/)
217{
218 return QString();
219}
220
221
222/*!
223 Resolves the undeclared entity \a name and returns its replacement
224 text. If the entity is also unknown to the entity resolver, it
225 returns an empty string.
226
227 The default implementation always returns an empty string.
228*/
229
230QString QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/)
231{
232 return QString();
233}
234
235#if QT_CONFIG(xmlstreamreader)
236
237QString QXmlStreamReaderPrivate::resolveUndeclaredEntity(const QString &name)
238{
239 if (entityResolver)
240 return entityResolver->resolveUndeclaredEntity(name);
241 return QString();
242}
243
244
245
246/*!
247 \since 4.4
248
249 Makes \a resolver the new entityResolver().
250
251 The stream reader does \e not take ownership of the resolver. It's
252 the callers responsibility to ensure that the resolver is valid
253 during the entire life-time of the stream reader object, or until
254 another resolver or \nullptr is set.
255
256 \sa entityResolver()
257 */
258void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver)
259{
260 Q_D(QXmlStreamReader);
261 d->entityResolver = resolver;
262}
263
264/*!
265 \since 4.4
266
267 Returns the entity resolver, or \nullptr if there is no entity resolver.
268
269 \sa setEntityResolver()
270 */
271QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const
272{
273 Q_D(const QXmlStreamReader);
274 return d->entityResolver;
275}
276
277
278
279/*!
280 \class QXmlStreamReader
281 \inmodule QtCore
282 \reentrant
283 \since 4.3
284
285 \brief The QXmlStreamReader class provides a fast parser for reading
286 well-formed XML via a simple streaming API.
287
288
289 \ingroup xml-tools
290
291 \ingroup qtserialization
292
293 QXmlStreamReader provides a simple streaming API to parse well-formed
294 XML. It is an alternative to first loading the complete XML into a
295 DOM tree (see \l QDomDocument). QXmlStreamReader reads data either
296 from a QIODevice (see setDevice()), or from a raw QByteArray (see addData()).
297
298 Qt provides QXmlStreamWriter for writing XML.
299
300 The basic concept of a stream reader is to report an XML document as
301 a stream of tokens, similar to SAX. The main difference between
302 QXmlStreamReader and SAX is \e how these XML tokens are reported.
303 With SAX, the application must provide handlers (callback functions)
304 that receive so-called XML \e events from the parser at the parser's
305 convenience. With QXmlStreamReader, the application code itself
306 drives the loop and pulls \e tokens from the reader, one after
307 another, as it needs them. This is done by calling readNext(), where
308 the reader reads from the input stream until it completes the next
309 token, at which point it returns the tokenType(). A set of
310 convenient functions including isStartElement() and text() can then
311 be used to examine the token to obtain information about what has
312 been read. The big advantage of this \e pulling approach is the
313 possibility to build recursive descent parsers with it, meaning you
314 can split your XML parsing code easily into different methods or
315 classes. This makes it easy to keep track of the application's own
316 state when parsing XML.
317
318 A typical loop with QXmlStreamReader looks like this:
319
320 \snippet code/src_corelib_xml_qxmlstream.cpp 0
321
322
323 QXmlStreamReader is a well-formed XML 1.0 parser that does \e not
324 include external parsed entities. As long as no error occurs, the
325 application code can thus be assured, that
326 \list
327 \li the data provided by the stream reader satisfies the W3C's
328 criteria for well-formed XML,
329 \li tokens are provided in a valid order.
330 \endlist
331
332 Unless QXmlStreamReader raises an error, it guarantees the following:
333 \list
334 \li All tags are nested and closed properly.
335 \li References to internal entities have been replaced with the
336 correct replacement text.
337 \li Attributes have been normalized or added according to the
338 internal subset of the \l DTD.
339 \li Tokens of type \l StartDocument happen before all others,
340 aside from comments and processing instructions.
341 \li At most one DOCTYPE element (a token of type \l DTD) is present.
342 \li If present, the DOCTYPE appears before all other elements,
343 aside from StartDocument, comments and processing instructions.
344 \endlist
345
346 In particular, once any token of type \l StartElement, \l EndElement,
347 \l Characters, \l EntityReference or \l EndDocument is seen, no
348 tokens of type StartDocument or DTD will be seen. If one is present in
349 the input stream, out of order, an error is raised.
350
351 \note The token types \l Comment and \l ProcessingInstruction may appear
352 anywhere in the stream.
353
354 If an error occurs while parsing, atEnd() and hasError() return
355 true, and error() returns the error that occurred. The functions
356 errorString(), lineNumber(), columnNumber(), and characterOffset()
357 are for constructing an appropriate error or warning message. To
358 simplify application code, QXmlStreamReader contains a raiseError()
359 mechanism that lets you raise custom errors that trigger the same
360 error handling described.
361
362 The \l{QXmlStream Bookmarks Example} illustrates how to use the
363 recursive descent technique to read an XML bookmark file (XBEL) with
364 a stream reader.
365
366 \section1 Namespaces
367
368 QXmlStream understands and resolves XML namespaces. E.g. in case of
369 a StartElement, namespaceUri() returns the namespace the element is
370 in, and name() returns the element's \e local name. The combination
371 of namespaceUri and name uniquely identifies an element. If a
372 namespace prefix was not declared in the XML entities parsed by the
373 reader, the namespaceUri is empty.
374
375 If you parse XML data that does not utilize namespaces according to
376 the XML specification or doesn't use namespaces at all, you can use
377 the element's qualifiedName() instead. A qualified name is the
378 element's prefix() followed by colon followed by the element's local
379 name() - exactly like the element appears in the raw XML data. Since
380 the mapping namespaceUri to prefix is neither unique nor universal,
381 qualifiedName() should be avoided for namespace-compliant XML data.
382
383 In order to parse standalone documents that do use undeclared
384 namespace prefixes, you can turn off namespace processing completely
385 with the \l namespaceProcessing property.
386
387 \section1 Incremental Parsing
388
389 QXmlStreamReader is an incremental parser. It can handle the case
390 where the document can't be parsed all at once because it arrives in
391 chunks (e.g. from multiple files, or over a network connection).
392 When the reader runs out of data before the complete document has
393 been parsed, it reports a PrematureEndOfDocumentError. When more
394 data arrives, either because of a call to addData() or because more
395 data is available through the network device(), the reader recovers
396 from the PrematureEndOfDocumentError error and continues parsing the
397 new data with the next call to readNext().
398
399 For example, if your application reads data from the network using a
400 \l{QNetworkAccessManager} {network access manager}, you would issue
401 a \l{QNetworkRequest} {network request} to the manager and receive a
402 \l{QNetworkReply} {network reply} in return. Since a QNetworkReply
403 is a QIODevice, you connect its \l{QIODevice::readyRead()}
404 {readyRead()} signal to a custom slot, e.g. \c{slotReadyRead()} in
405 the code snippet shown in the discussion for QNetworkAccessManager.
406 In this slot, you read all available data with
407 \l{QIODevice::readAll()} {readAll()} and pass it to the XML
408 stream reader using addData(). Then you call your custom parsing
409 function that reads the XML events from the reader.
410
411 \section1 Performance and Memory Consumption
412
413 QXmlStreamReader is memory-conservative by design, since it doesn't
414 store the entire XML document tree in memory, but only the current
415 token at the time it is reported. In addition, QXmlStreamReader
416 avoids the many small string allocations that it normally takes to
417 map an XML document to a convenient and Qt-ish API. It does this by
418 reporting all string data as QStringView rather than real QString
419 objects. Calling \l{QStringView::toString()}{toString()} on any of
420 those objects returns an equivalent real QString object.
421*/
422
423
424/*!
425 Constructs a stream reader.
426
427 \sa setDevice(), addData()
428 */
429QXmlStreamReader::QXmlStreamReader()
430 : d_ptr(new QXmlStreamReaderPrivate(this))
431{
432}
433
434/*! Creates a new stream reader that reads from \a device.
435
436\sa setDevice(), clear()
437 */
438QXmlStreamReader::QXmlStreamReader(QIODevice *device)
439 : d_ptr(new QXmlStreamReaderPrivate(this))
440{
441 setDevice(device);
442}
443
444/*!
445 \overload
446
447 \fn QXmlStreamReader::QXmlStreamReader(const QByteArray &data)
448
449 Creates a new stream reader that reads from \a data.
450
451 \sa addData(), clear(), setDevice()
452*/
453
454/*!
455 Creates a new stream reader that reads from \a data.
456
457 \note In Qt versions prior to 6.5, this constructor was overloaded
458 for QString and \c {const char*}.
459
460 \sa addData(), clear(), setDevice()
461*/
462QXmlStreamReader::QXmlStreamReader(QAnyStringView data)
463 : d_ptr(new QXmlStreamReaderPrivate(this))
464{
465 Q_D(QXmlStreamReader);
466 data.visit(v: [d](auto data) {
467 if constexpr (std::is_same_v<decltype(data), QStringView>) {
468 d->dataBuffer = data.toUtf8();
469 d->decoder = QStringDecoder(QStringDecoder::Utf8);
470 d->lockEncoding = true;
471 } else if constexpr (std::is_same_v<decltype(data), QLatin1StringView>) {
472 // Conversion to a QString is required, to avoid breaking
473 // pre-existing (before porting to QAnyStringView) behavior.
474 d->dataBuffer = QString::fromLatin1(data).toUtf8();
475 d->decoder = QStringDecoder(QStringDecoder::Utf8);
476 d->lockEncoding = true;
477 } else {
478 d->dataBuffer = QByteArray(data.data(), data.size());
479 }
480 });
481}
482
483/*!
484 \internal
485
486 Creates a new stream reader that reads from \a data.
487 Used by the weak constructor taking a QByteArray.
488*/
489QXmlStreamReader::QXmlStreamReader(const QByteArray &data, PrivateConstructorTag)
490 : d_ptr(new QXmlStreamReaderPrivate(this))
491{
492 Q_D(QXmlStreamReader);
493 d->dataBuffer = data;
494}
495
496/*!
497 Destructs the reader.
498 */
499QXmlStreamReader::~QXmlStreamReader()
500{
501 Q_D(QXmlStreamReader);
502 if (d->deleteDevice)
503 delete d->device;
504}
505
506/*! \fn bool QXmlStreamReader::hasError() const
507 Returns \c true if an error has occurred, otherwise \c false.
508
509 \sa errorString(), error()
510 */
511
512/*!
513 Sets the current device to \a device. Setting the device resets
514 the stream to its initial state.
515
516 \sa device(), clear()
517*/
518void QXmlStreamReader::setDevice(QIODevice *device)
519{
520 Q_D(QXmlStreamReader);
521 if (d->deleteDevice) {
522 delete d->device;
523 d->deleteDevice = false;
524 }
525 d->device = device;
526 d->init();
527
528}
529
530/*!
531 Returns the current device associated with the QXmlStreamReader,
532 or \nullptr if no device has been assigned.
533
534 \sa setDevice()
535*/
536QIODevice *QXmlStreamReader::device() const
537{
538 Q_D(const QXmlStreamReader);
539 return d->device;
540}
541
542/*!
543 \overload
544
545 \fn void QXmlStreamReader::addData(const QByteArray &data)
546
547 Adds more \a data for the reader to read. This function does
548 nothing if the reader has a device().
549
550 \sa readNext(), clear()
551*/
552
553/*!
554 Adds more \a data for the reader to read. This function does
555 nothing if the reader has a device().
556
557 \note In Qt versions prior to 6.5, this function was overloaded
558 for QString and \c {const char*}.
559
560 \sa readNext(), clear()
561*/
562void QXmlStreamReader::addData(QAnyStringView data)
563{
564 Q_D(QXmlStreamReader);
565 data.visit(v: [this, d](auto data) {
566 if constexpr (std::is_same_v<decltype(data), QStringView>) {
567 d->lockEncoding = true;
568 if (!d->decoder.isValid())
569 d->decoder = QStringDecoder(QStringDecoder::Utf8);
570 addDataImpl(data: data.toUtf8());
571 } else if constexpr (std::is_same_v<decltype(data), QLatin1StringView>) {
572 // Conversion to a QString is required, to avoid breaking
573 // pre-existing (before porting to QAnyStringView) behavior.
574 if (!d->decoder.isValid())
575 d->decoder = QStringDecoder(QStringDecoder::Utf8);
576 addDataImpl(data: QString::fromLatin1(data).toUtf8());
577 } else {
578 addDataImpl(data: QByteArray(data.data(), data.size()));
579 }
580 });
581}
582
583/*!
584 \internal
585
586 Adds more \a data for the reader to read. This function does
587 nothing if the reader has a device().
588*/
589void QXmlStreamReader::addDataImpl(const QByteArray &data)
590{
591 Q_D(QXmlStreamReader);
592 if (d->device) {
593 qWarning(msg: "QXmlStreamReader: addData() with device()");
594 return;
595 }
596 d->dataBuffer += data;
597}
598
599/*!
600 Removes any device() or data from the reader and resets its
601 internal state to the initial state.
602
603 \sa addData()
604 */
605void QXmlStreamReader::clear()
606{
607 Q_D(QXmlStreamReader);
608 d->init();
609 if (d->device) {
610 if (d->deleteDevice)
611 delete d->device;
612 d->device = nullptr;
613 }
614}
615
616/*!
617 Returns \c true if the reader has read until the end of the XML
618 document, or if an error() has occurred and reading has been
619 aborted. Otherwise, it returns \c false.
620
621 When atEnd() and hasError() return true and error() returns
622 PrematureEndOfDocumentError, it means the XML has been well-formed
623 so far, but a complete XML document has not been parsed. The next
624 chunk of XML can be added with addData(), if the XML is being read
625 from a QByteArray, or by waiting for more data to arrive if the
626 XML is being read from a QIODevice. Either way, atEnd() will
627 return false once more data is available.
628
629 \sa hasError(), error(), device(), QIODevice::atEnd()
630 */
631bool QXmlStreamReader::atEnd() const
632{
633 Q_D(const QXmlStreamReader);
634 if (d->atEnd
635 && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError)
636 || (d->type == QXmlStreamReader::EndDocument))) {
637 if (d->device)
638 return d->device->atEnd();
639 else
640 return !d->dataBuffer.size();
641 }
642 return (d->atEnd || d->type == QXmlStreamReader::Invalid);
643}
644
645
646/*!
647 Reads the next token and returns its type.
648
649 With one exception, once an error() is reported by readNext(),
650 further reading of the XML stream is not possible. Then atEnd()
651 returns \c true, hasError() returns \c true, and this function returns
652 QXmlStreamReader::Invalid.
653
654 The exception is when error() returns PrematureEndOfDocumentError.
655 This error is reported when the end of an otherwise well-formed
656 chunk of XML is reached, but the chunk doesn't represent a complete
657 XML document. In that case, parsing \e can be resumed by calling
658 addData() to add the next chunk of XML, when the stream is being
659 read from a QByteArray, or by waiting for more data to arrive when
660 the stream is being read from a device().
661
662 \sa tokenType(), tokenString()
663 */
664QXmlStreamReader::TokenType QXmlStreamReader::readNext()
665{
666 Q_D(QXmlStreamReader);
667 if (d->type != Invalid) {
668 if (!d->hasCheckedStartDocument)
669 if (!d->checkStartDocument())
670 return d->type; // synthetic StartDocument or error
671 d->parse();
672 if (d->atEnd && d->type != EndDocument && d->type != Invalid)
673 d->raiseError(error: PrematureEndOfDocumentError);
674 else if (!d->atEnd && d->type == EndDocument)
675 d->raiseWellFormedError(message: QXmlStream::tr(sourceText: "Extra content at end of document."));
676 } else if (d->error == PrematureEndOfDocumentError) {
677 // resume error
678 d->type = NoToken;
679 d->atEnd = false;
680 d->token = -1;
681 return readNext();
682 }
683 d->checkToken();
684 return d->type;
685}
686
687
688/*!
689 Returns the type of the current token.
690
691 The current token can also be queried with the convenience functions
692 isStartDocument(), isEndDocument(), isStartElement(),
693 isEndElement(), isCharacters(), isComment(), isDTD(),
694 isEntityReference(), and isProcessingInstruction().
695
696 \sa tokenString()
697 */
698QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const
699{
700 Q_D(const QXmlStreamReader);
701 return d->type;
702}
703
704/*!
705 Reads until the next start element within the current element. Returns \c true
706 when a start element was reached. When the end element was reached, or when
707 an error occurred, false is returned.
708
709 The current element is the element matching the most recently parsed start
710 element of which a matching end element has not yet been reached. When the
711 parser has reached the end element, the current element becomes the parent
712 element.
713
714 This is a convenience function for when you're only concerned with parsing
715 XML elements. The \l{QXmlStream Bookmarks Example} makes extensive use of
716 this function.
717
718 \since 4.6
719 \sa readNext()
720 */
721bool QXmlStreamReader::readNextStartElement()
722{
723 while (readNext() != Invalid) {
724 if (isEndElement() || isEndDocument())
725 return false;
726 else if (isStartElement())
727 return true;
728 }
729 return false;
730}
731
732/*!
733 Reads until the end of the current element, skipping any child nodes.
734 This function is useful for skipping unknown elements.
735
736 The current element is the element matching the most recently parsed start
737 element of which a matching end element has not yet been reached. When the
738 parser has reached the end element, the current element becomes the parent
739 element.
740
741 \since 4.6
742 */
743void QXmlStreamReader::skipCurrentElement()
744{
745 int depth = 1;
746 while (depth && readNext() != Invalid) {
747 if (isEndElement())
748 --depth;
749 else if (isStartElement())
750 ++depth;
751 }
752}
753
754static constexpr auto QXmlStreamReader_tokenTypeString = qOffsetStringArray(
755 strings: "NoToken",
756 strings: "Invalid",
757 strings: "StartDocument",
758 strings: "EndDocument",
759 strings: "StartElement",
760 strings: "EndElement",
761 strings: "Characters",
762 strings: "Comment",
763 strings: "DTD",
764 strings: "EntityReference",
765 strings: "ProcessingInstruction"
766);
767
768static constexpr auto QXmlStreamReader_XmlContextString = qOffsetStringArray(
769 strings: "Prolog",
770 strings: "Body"
771);
772
773/*!
774 \property QXmlStreamReader::namespaceProcessing
775 \brief the namespace-processing flag of the stream reader.
776
777 This property controls whether or not the stream reader processes
778 namespaces. If enabled, the reader processes namespaces, otherwise
779 it does not.
780
781 By default, namespace-processing is enabled.
782*/
783
784
785void QXmlStreamReader::setNamespaceProcessing(bool enable)
786{
787 Q_D(QXmlStreamReader);
788 d->namespaceProcessing = enable;
789}
790
791bool QXmlStreamReader::namespaceProcessing() const
792{
793 Q_D(const QXmlStreamReader);
794 return d->namespaceProcessing;
795}
796
797/*! Returns the reader's current token as string.
798
799\sa tokenType()
800*/
801QString QXmlStreamReader::tokenString() const
802{
803 Q_D(const QXmlStreamReader);
804 return QLatin1StringView(QXmlStreamReader_tokenTypeString.at(index: d->type));
805}
806
807/*!
808 \internal
809 \return \param ctxt (Prolog/Body) as a string.
810 */
811static constexpr QLatin1StringView contextString(QXmlStreamReaderPrivate::XmlContext ctxt)
812{
813 return QLatin1StringView(QXmlStreamReader_XmlContextString.at(index: static_cast<int>(ctxt)));
814}
815
816#endif // feature xmlstreamreader
817
818QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack()
819{
820 tagStack.reserve(extraCapacity: 16);
821 tagStackStringStorage.reserve(asize: 32);
822 tagStackStringStorageSize = 0;
823 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
824 namespaceDeclaration.prefix = addToStringStorage(s: u"xml");
825 namespaceDeclaration.namespaceUri = addToStringStorage(s: u"http://www.w3.org/XML/1998/namespace");
826 initialTagStackStringStorageSize = tagStackStringStorageSize;
827 tagsDone = false;
828}
829
830#if QT_CONFIG(xmlstreamreader)
831
832QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q)
833 :q_ptr(q)
834{
835 device = nullptr;
836 deleteDevice = false;
837 stack_size = 64;
838 sym_stack = nullptr;
839 state_stack = nullptr;
840 reallocateStack();
841 entityResolver = nullptr;
842 init();
843#define ADD_PREDEFINED(n, v) \
844 do { \
845 Entity e = Entity::createLiteral(n##_L1, v##_L1); \
846 entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \
847 } while (false)
848 ADD_PREDEFINED("lt", "<");
849 ADD_PREDEFINED("gt", ">");
850 ADD_PREDEFINED("amp", "&");
851 ADD_PREDEFINED("apos", "'");
852 ADD_PREDEFINED("quot", "\"");
853#undef ADD_PREDEFINED
854}
855
856void QXmlStreamReaderPrivate::init()
857{
858 scanDtd = false;
859 lastAttributeIsCData = false;
860 token = -1;
861 token_char = 0;
862 isEmptyElement = false;
863 isWhitespace = true;
864 isCDATA = false;
865 standalone = false;
866 hasStandalone = false;
867 tos = 0;
868 resumeReduction = 0;
869 state_stack[tos++] = 0;
870 state_stack[tos] = 0;
871 putStack.clear();
872 putStack.reserve(extraCapacity: 32);
873 textBuffer.clear();
874 textBuffer.reserve(asize: 256);
875 tagStack.clear();
876 tagsDone = false;
877 attributes.clear();
878 attributes.reserve(asize: 16);
879 lineNumber = lastLineStart = characterOffset = 0;
880 readBufferPos = 0;
881 nbytesread = 0;
882 decoder = QStringDecoder();
883 attributeStack.clear();
884 attributeStack.reserve(extraCapacity: 16);
885 entityParser.reset();
886 hasCheckedStartDocument = false;
887 normalizeLiterals = false;
888 hasSeenTag = false;
889 atEnd = false;
890 inParseEntity = false;
891 referenceToUnparsedEntityDetected = false;
892 referenceToParameterEntityDetected = false;
893 hasExternalDtdSubset = false;
894 lockEncoding = false;
895 namespaceProcessing = true;
896 rawReadBuffer.clear();
897 dataBuffer.clear();
898 readBuffer.clear();
899 tagStackStringStorageSize = initialTagStackStringStorageSize;
900
901 type = QXmlStreamReader::NoToken;
902 error = QXmlStreamReader::NoError;
903 currentContext = XmlContext::Prolog;
904 foundDTD = false;
905}
906
907/*
908 Well-formed requires that we verify entity values. We do this with a
909 standard parser.
910 */
911void QXmlStreamReaderPrivate::parseEntity(const QString &value)
912{
913 Q_Q(QXmlStreamReader);
914
915 if (value.isEmpty())
916 return;
917
918
919 if (!entityParser)
920 entityParser = std::make_unique<QXmlStreamReaderPrivate>(args: q);
921 else
922 entityParser->init();
923 entityParser->inParseEntity = true;
924 entityParser->readBuffer = value;
925 entityParser->injectToken(tokenToInject: PARSE_ENTITY);
926 while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid)
927 entityParser->parse();
928 if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size())
929 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Invalid entity value."));
930
931}
932
933inline void QXmlStreamReaderPrivate::reallocateStack()
934{
935 stack_size <<= 1;
936 sym_stack = reinterpret_cast<Value*> (realloc(ptr: sym_stack, size: stack_size * sizeof(Value)));
937 Q_CHECK_PTR(sym_stack);
938 state_stack = reinterpret_cast<int*> (realloc(ptr: state_stack, size: stack_size * sizeof(int)));
939 Q_CHECK_PTR(state_stack);
940}
941
942
943QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate()
944{
945 free(ptr: sym_stack);
946 free(ptr: state_stack);
947}
948
949
950inline uint QXmlStreamReaderPrivate::filterCarriageReturn()
951{
952 uint peekc = peekChar();
953 if (peekc == '\n') {
954 if (putStack.size())
955 putStack.pop();
956 else
957 ++readBufferPos;
958 return peekc;
959 }
960 if (peekc == StreamEOF) {
961 putChar(c: '\r');
962 return 0;
963 }
964 return '\n';
965}
966
967/*!
968 \internal
969 If the end of the file is encountered, ~0 is returned.
970 */
971inline uint QXmlStreamReaderPrivate::getChar()
972{
973 uint c;
974 if (putStack.size()) {
975 c = atEnd ? StreamEOF : putStack.pop();
976 } else {
977 if (readBufferPos < readBuffer.size())
978 c = readBuffer.at(i: readBufferPos++).unicode();
979 else
980 c = getChar_helper();
981 }
982
983 return c;
984}
985
986inline uint QXmlStreamReaderPrivate::peekChar()
987{
988 uint c;
989 if (putStack.size()) {
990 c = putStack.top();
991 } else if (readBufferPos < readBuffer.size()) {
992 c = readBuffer.at(i: readBufferPos).unicode();
993 } else {
994 if ((c = getChar_helper()) != StreamEOF)
995 --readBufferPos;
996 }
997
998 return c;
999}
1000
1001/*!
1002 \internal
1003
1004 Scans characters until \a str is encountered, and validates the characters
1005 as according to the Char[2] production and do the line-ending normalization.
1006 If any character is invalid, false is returned, otherwise true upon success.
1007
1008 If \a tokenToInject is not less than zero, injectToken() is called with
1009 \a tokenToInject when \a str is found.
1010
1011 If any error occurred, false is returned, otherwise true.
1012 */
1013bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject)
1014{
1015 const qsizetype pos = textBuffer.size();
1016 const auto oldLineNumber = lineNumber;
1017
1018 uint c;
1019 while ((c = getChar()) != StreamEOF) {
1020 /* First, we do the validation & normalization. */
1021 switch (c) {
1022 case '\r':
1023 if ((c = filterCarriageReturn()) == 0)
1024 break;
1025 Q_FALLTHROUGH();
1026 case '\n':
1027 ++lineNumber;
1028 lastLineStart = characterOffset + readBufferPos;
1029 Q_FALLTHROUGH();
1030 case '\t':
1031 textBuffer += QChar(c);
1032 continue;
1033 default:
1034 if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) {
1035 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Invalid XML character."));
1036 lineNumber = oldLineNumber;
1037 return false;
1038 }
1039 textBuffer += QChar(c);
1040 }
1041
1042
1043 /* Second, attempt to lookup str. */
1044 if (c == uint(*str)) {
1045 if (!*(str + 1)) {
1046 if (tokenToInject >= 0)
1047 injectToken(tokenToInject);
1048 return true;
1049 } else {
1050 if (scanString(str: str + 1, tokenToInject, requireSpace: false))
1051 return true;
1052 }
1053 }
1054 }
1055 putString(s: textBuffer, from: pos);
1056 textBuffer.resize(size: pos);
1057 lineNumber = oldLineNumber;
1058 return false;
1059}
1060
1061bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace)
1062{
1063 qsizetype n = 0;
1064 while (str[n]) {
1065 uint c = getChar();
1066 if (c != ushort(str[n])) {
1067 if (c != StreamEOF)
1068 putChar(c);
1069 while (n--) {
1070 putChar(c: ushort(str[n]));
1071 }
1072 return false;
1073 }
1074 ++n;
1075 }
1076 textBuffer += QLatin1StringView(str, n);
1077 if (requireSpace) {
1078 const qsizetype s = fastScanSpace();
1079 if (!s || atEnd) {
1080 qsizetype pos = textBuffer.size() - n - s;
1081 putString(s: textBuffer, from: pos);
1082 textBuffer.resize(size: pos);
1083 return false;
1084 }
1085 }
1086 if (tokenToInject >= 0)
1087 injectToken(tokenToInject);
1088 return true;
1089}
1090
1091bool QXmlStreamReaderPrivate::scanAfterLangleBang()
1092{
1093 switch (peekChar()) {
1094 case '[':
1095 return scanString(str: spell[CDATA_START], tokenToInject: CDATA_START, requireSpace: false);
1096 case 'D':
1097 return scanString(str: spell[DOCTYPE], tokenToInject: DOCTYPE);
1098 case 'A':
1099 return scanString(str: spell[ATTLIST], tokenToInject: ATTLIST);
1100 case 'N':
1101 return scanString(str: spell[NOTATION], tokenToInject: NOTATION);
1102 case 'E':
1103 if (scanString(str: spell[ELEMENT], tokenToInject: ELEMENT))
1104 return true;
1105 return scanString(str: spell[ENTITY], tokenToInject: ENTITY);
1106
1107 default:
1108 ;
1109 };
1110 return false;
1111}
1112
1113bool QXmlStreamReaderPrivate::scanPublicOrSystem()
1114{
1115 switch (peekChar()) {
1116 case 'S':
1117 return scanString(str: spell[SYSTEM], tokenToInject: SYSTEM);
1118 case 'P':
1119 return scanString(str: spell[PUBLIC], tokenToInject: PUBLIC);
1120 default:
1121 ;
1122 }
1123 return false;
1124}
1125
1126bool QXmlStreamReaderPrivate::scanNData()
1127{
1128 if (fastScanSpace()) {
1129 if (scanString(str: spell[NDATA], tokenToInject: NDATA))
1130 return true;
1131 putChar(c: ' ');
1132 }
1133 return false;
1134}
1135
1136bool QXmlStreamReaderPrivate::scanAfterDefaultDecl()
1137{
1138 switch (peekChar()) {
1139 case 'R':
1140 return scanString(str: spell[REQUIRED], tokenToInject: REQUIRED, requireSpace: false);
1141 case 'I':
1142 return scanString(str: spell[IMPLIED], tokenToInject: IMPLIED, requireSpace: false);
1143 case 'F':
1144 return scanString(str: spell[FIXED], tokenToInject: FIXED, requireSpace: false);
1145 default:
1146 ;
1147 }
1148 return false;
1149}
1150
1151bool QXmlStreamReaderPrivate::scanAttType()
1152{
1153 switch (peekChar()) {
1154 case 'C':
1155 return scanString(str: spell[CDATA], tokenToInject: CDATA);
1156 case 'I':
1157 if (scanString(str: spell[ID], tokenToInject: ID))
1158 return true;
1159 if (scanString(str: spell[IDREF], tokenToInject: IDREF))
1160 return true;
1161 return scanString(str: spell[IDREFS], tokenToInject: IDREFS);
1162 case 'E':
1163 if (scanString(str: spell[ENTITY], tokenToInject: ENTITY))
1164 return true;
1165 return scanString(str: spell[ENTITIES], tokenToInject: ENTITIES);
1166 case 'N':
1167 if (scanString(str: spell[NOTATION], tokenToInject: NOTATION))
1168 return true;
1169 if (scanString(str: spell[NMTOKEN], tokenToInject: NMTOKEN))
1170 return true;
1171 return scanString(str: spell[NMTOKENS], tokenToInject: NMTOKENS);
1172 default:
1173 ;
1174 }
1175 return false;
1176}
1177
1178/*!
1179 \internal
1180
1181 Scan strings with quotes or apostrophes surround them. For instance,
1182 attributes, the version and encoding field in the XML prolog and
1183 entity declarations.
1184
1185 If normalizeLiterals is set to true, the function also normalizes
1186 whitespace. It is set to true when the first start tag is
1187 encountered.
1188
1189 */
1190inline qsizetype QXmlStreamReaderPrivate::fastScanLiteralContent()
1191{
1192 qsizetype n = 0;
1193 uint c;
1194 while ((c = getChar()) != StreamEOF) {
1195 switch (ushort(c)) {
1196 case 0xfffe:
1197 case 0xffff:
1198 case 0:
1199 /* The putChar() call is necessary so the parser re-gets
1200 * the character from the input source, when raising an error. */
1201 putChar(c);
1202 return n;
1203 case '\r':
1204 if (filterCarriageReturn() == 0)
1205 return n;
1206 Q_FALLTHROUGH();
1207 case '\n':
1208 ++lineNumber;
1209 lastLineStart = characterOffset + readBufferPos;
1210 Q_FALLTHROUGH();
1211 case ' ':
1212 case '\t':
1213 if (normalizeLiterals)
1214 textBuffer += u' ';
1215 else
1216 textBuffer += QChar(c);
1217 ++n;
1218 break;
1219 case '&':
1220 case '<':
1221 case '\"':
1222 case '\'':
1223 if (!(c & 0xff0000)) {
1224 putChar(c);
1225 return n;
1226 }
1227 Q_FALLTHROUGH();
1228 default:
1229 if (c < 0x20) {
1230 putChar(c);
1231 return n;
1232 }
1233 textBuffer += QChar(ushort(c));
1234 ++n;
1235 }
1236 }
1237 return n;
1238}
1239
1240inline qsizetype QXmlStreamReaderPrivate::fastScanSpace()
1241{
1242 qsizetype n = 0;
1243 uint c;
1244 while ((c = getChar()) != StreamEOF) {
1245 switch (c) {
1246 case '\r':
1247 if ((c = filterCarriageReturn()) == 0)
1248 return n;
1249 Q_FALLTHROUGH();
1250 case '\n':
1251 ++lineNumber;
1252 lastLineStart = characterOffset + readBufferPos;
1253 Q_FALLTHROUGH();
1254 case ' ':
1255 case '\t':
1256 textBuffer += QChar(c);
1257 ++n;
1258 break;
1259 default:
1260 putChar(c);
1261 return n;
1262 }
1263 }
1264 return n;
1265}
1266
1267/*!
1268 \internal
1269
1270 Used for text nodes essentially. That is, characters appearing
1271 inside elements.
1272 */
1273inline qsizetype QXmlStreamReaderPrivate::fastScanContentCharList()
1274{
1275 qsizetype n = 0;
1276 uint c;
1277 while ((c = getChar()) != StreamEOF) {
1278 switch (ushort(c)) {
1279 case 0xfffe:
1280 case 0xffff:
1281 case 0:
1282 putChar(c);
1283 return n;
1284 case ']': {
1285 isWhitespace = false;
1286 const qsizetype pos = textBuffer.size();
1287 textBuffer += QChar(ushort(c));
1288 ++n;
1289 while ((c = getChar()) == ']') {
1290 textBuffer += QChar(ushort(c));
1291 ++n;
1292 }
1293 if (c == 0) {
1294 putString(s: textBuffer, from: pos);
1295 textBuffer.resize(size: pos);
1296 } else if (c == '>' && textBuffer.at(i: textBuffer.size() - 2) == u']') {
1297 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Sequence ']]>' not allowed in content."));
1298 } else {
1299 putChar(c);
1300 break;
1301 }
1302 return n;
1303 } break;
1304 case '\r':
1305 if ((c = filterCarriageReturn()) == 0)
1306 return n;
1307 Q_FALLTHROUGH();
1308 case '\n':
1309 ++lineNumber;
1310 lastLineStart = characterOffset + readBufferPos;
1311 Q_FALLTHROUGH();
1312 case ' ':
1313 case '\t':
1314 textBuffer += QChar(ushort(c));
1315 ++n;
1316 break;
1317 case '&':
1318 case '<':
1319 if (!(c & 0xff0000)) {
1320 putChar(c);
1321 return n;
1322 }
1323 Q_FALLTHROUGH();
1324 default:
1325 if (c < 0x20) {
1326 putChar(c);
1327 return n;
1328 }
1329 isWhitespace = false;
1330 textBuffer += QChar(ushort(c));
1331 ++n;
1332 }
1333 }
1334 return n;
1335}
1336
1337// Fast scan an XML attribute name (e.g. "xml:lang").
1338inline std::optional<qsizetype> QXmlStreamReaderPrivate::fastScanName(Value *val)
1339{
1340 qsizetype n = 0;
1341 uint c;
1342 while ((c = getChar()) != StreamEOF) {
1343 if (n >= 4096) {
1344 // This is too long to be a sensible name, and
1345 // can exhaust memory, or the range of decltype(*prefix)
1346 raiseNamePrefixTooLongError();
1347 return std::nullopt;
1348 }
1349 switch (c) {
1350 case '\n':
1351 case ' ':
1352 case '\t':
1353 case '\r':
1354 case '&':
1355 case '#':
1356 case '\'':
1357 case '\"':
1358 case '<':
1359 case '>':
1360 case '[':
1361 case ']':
1362 case '=':
1363 case '%':
1364 case '/':
1365 case ';':
1366 case '?':
1367 case '!':
1368 case '^':
1369 case '|':
1370 case ',':
1371 case '(':
1372 case ')':
1373 case '+':
1374 case '*':
1375 putChar(c);
1376 if (val && val->prefix == n + 1) {
1377 val->prefix = 0;
1378 putChar(c: ':');
1379 --n;
1380 }
1381 return n;
1382 case ':':
1383 if (val) {
1384 if (val->prefix == 0) {
1385 val->prefix = qint16(n + 2);
1386 } else { // only one colon allowed according to the namespace spec.
1387 putChar(c);
1388 return n;
1389 }
1390 } else {
1391 putChar(c);
1392 return n;
1393 }
1394 Q_FALLTHROUGH();
1395 default:
1396 textBuffer += QChar(ushort(c));
1397 ++n;
1398 }
1399 }
1400
1401 if (val)
1402 val->prefix = 0;
1403 qsizetype pos = textBuffer.size() - n;
1404 putString(s: textBuffer, from: pos);
1405 textBuffer.resize(size: pos);
1406 return 0;
1407}
1408
1409enum NameChar { NameBeginning, NameNotBeginning, NotName };
1410
1411static const char Begi = static_cast<char>(NameBeginning);
1412static const char NtBg = static_cast<char>(NameNotBeginning);
1413static const char NotN = static_cast<char>(NotName);
1414
1415static const char nameCharTable[128] =
1416{
1417// 0x00
1418 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1419 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1420// 0x10
1421 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1422 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1423// 0x20 (0x2D is '-', 0x2E is '.')
1424 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1425 NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN,
1426// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':')
1427 NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg,
1428 NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN,
1429// 0x40 (0x41..0x5A are 'A'..'Z')
1430 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1431 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1432// 0x50 (0x5F is '_')
1433 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1434 Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi,
1435// 0x60 (0x61..0x7A are 'a'..'z')
1436 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1437 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1438// 0x70
1439 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1440 Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN
1441};
1442
1443static inline NameChar fastDetermineNameChar(QChar ch)
1444{
1445 ushort uc = ch.unicode();
1446 if (!(uc & ~0x7f)) // uc < 128
1447 return static_cast<NameChar>(nameCharTable[uc]);
1448
1449 QChar::Category cat = ch.category();
1450 // ### some these categories might be slightly wrong
1451 if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other)
1452 || cat == QChar::Number_Letter)
1453 return NameBeginning;
1454 if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other)
1455 || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing))
1456 return NameNotBeginning;
1457 return NotName;
1458}
1459
1460inline qsizetype QXmlStreamReaderPrivate::fastScanNMTOKEN()
1461{
1462 qsizetype n = 0;
1463 uint c;
1464 while ((c = getChar()) != StreamEOF) {
1465 if (fastDetermineNameChar(ch: QChar(c)) == NotName) {
1466 putChar(c);
1467 return n;
1468 } else {
1469 ++n;
1470 textBuffer += QChar(c);
1471 }
1472 }
1473
1474 qsizetype pos = textBuffer.size() - n;
1475 putString(s: textBuffer, from: pos);
1476 textBuffer.resize(size: pos);
1477
1478 return n;
1479}
1480
1481void QXmlStreamReaderPrivate::putString(QStringView s, qsizetype from)
1482{
1483 if (from != 0) {
1484 putString(s: s.mid(pos: from));
1485 return;
1486 }
1487 putStack.reserve(extraCapacity: s.size());
1488 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1489 putStack.rawPush() = it->unicode();
1490}
1491
1492void QXmlStreamReaderPrivate::putStringLiteral(QStringView s)
1493{
1494 putStack.reserve(extraCapacity: s.size());
1495 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1496 putStack.rawPush() = ((LETTER << 16) | it->unicode());
1497}
1498
1499void QXmlStreamReaderPrivate::putReplacement(QStringView s)
1500{
1501 putStack.reserve(extraCapacity: s.size());
1502 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1503 char16_t c = it->unicode();
1504 if (c == '\n' || c == '\r')
1505 putStack.rawPush() = ((LETTER << 16) | c);
1506 else
1507 putStack.rawPush() = c;
1508 }
1509}
1510void QXmlStreamReaderPrivate::putReplacementInAttributeValue(QStringView s)
1511{
1512 putStack.reserve(extraCapacity: s.size());
1513 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1514 char16_t c = it->unicode();
1515 if (c == '&' || c == ';')
1516 putStack.rawPush() = c;
1517 else if (c == '\n' || c == '\r')
1518 putStack.rawPush() = ' ';
1519 else
1520 putStack.rawPush() = ((LETTER << 16) | c);
1521 }
1522}
1523
1524uint QXmlStreamReaderPrivate::getChar_helper()
1525{
1526 constexpr qsizetype BUFFER_SIZE = 8192;
1527 characterOffset += readBufferPos;
1528 readBufferPos = 0;
1529 if (readBuffer.size())
1530 readBuffer.resize(size: 0);
1531 if (decoder.isValid())
1532 nbytesread = 0;
1533 if (device) {
1534 rawReadBuffer.resize(size: BUFFER_SIZE);
1535 qint64 nbytesreadOrMinus1 = device->read(data: rawReadBuffer.data() + nbytesread, maxlen: BUFFER_SIZE - nbytesread);
1536 nbytesread += qMax(a: nbytesreadOrMinus1, b: qint64{0});
1537 } else {
1538 if (nbytesread)
1539 rawReadBuffer += dataBuffer;
1540 else
1541 rawReadBuffer = dataBuffer;
1542 nbytesread = rawReadBuffer.size();
1543 dataBuffer.clear();
1544 }
1545 if (!nbytesread) {
1546 atEnd = true;
1547 return StreamEOF;
1548 }
1549
1550 if (!decoder.isValid()) {
1551 if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus
1552 // one extra for the utf8 codec
1553 atEnd = true;
1554 return StreamEOF;
1555 }
1556 auto encoding = QStringDecoder::encodingForData(data: rawReadBuffer, expectedFirstCharacter: char16_t('<'));
1557 if (!encoding)
1558 // assume utf-8
1559 encoding = QStringDecoder::Utf8;
1560 decoder = QStringDecoder(*encoding);
1561 }
1562
1563 readBuffer = decoder(QByteArrayView(rawReadBuffer).first(n: nbytesread));
1564
1565 if (lockEncoding && decoder.hasError()) {
1566 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Encountered incorrectly encoded content."));
1567 readBuffer.clear();
1568 return StreamEOF;
1569 }
1570
1571 readBuffer.reserve(asize: 1); // keep capacity when calling resize() next time
1572
1573 if (readBufferPos < readBuffer.size()) {
1574 ushort c = readBuffer.at(i: readBufferPos++).unicode();
1575 return c;
1576 }
1577
1578 atEnd = true;
1579 return StreamEOF;
1580}
1581
1582XmlStringRef QXmlStreamReaderPrivate::namespaceForPrefix(QStringView prefix)
1583{
1584 for (const NamespaceDeclaration &namespaceDeclaration : reversed(r&: namespaceDeclarations)) {
1585 if (namespaceDeclaration.prefix == prefix) {
1586 return namespaceDeclaration.namespaceUri;
1587 }
1588 }
1589
1590#if 1
1591 if (namespaceProcessing && !prefix.isEmpty())
1592 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Namespace prefix '%1' not declared").arg(a: prefix));
1593#endif
1594
1595 return XmlStringRef();
1596}
1597
1598/*
1599 uses namespaceForPrefix and builds the attribute vector
1600 */
1601void QXmlStreamReaderPrivate::resolveTag()
1602{
1603 const auto attributeStackCleaner = qScopeGuard(f: [this](){ attributeStack.clear(); });
1604 const qsizetype n = attributeStack.size();
1605
1606 if (namespaceProcessing) {
1607 for (DtdAttribute &dtdAttribute : dtdAttributes) {
1608 if (!dtdAttribute.isNamespaceAttribute
1609 || dtdAttribute.defaultValue.isNull()
1610 || dtdAttribute.tagName != qualifiedName
1611 || dtdAttribute.attributeQualifiedName.isNull())
1612 continue;
1613 qsizetype i = 0;
1614 while (i < n && symName(symbol: attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1615 ++i;
1616 if (i != n)
1617 continue;
1618 if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == "xmlns"_L1) {
1619 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1620 namespaceDeclaration.prefix.clear();
1621
1622 const XmlStringRef ns(dtdAttribute.defaultValue);
1623 if (ns == "http://www.w3.org/2000/xmlns/"_L1 ||
1624 ns == "http://www.w3.org/XML/1998/namespace"_L1)
1625 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Illegal namespace declaration."));
1626 else
1627 namespaceDeclaration.namespaceUri = ns;
1628 } else if (dtdAttribute.attributePrefix == "xmlns"_L1) {
1629 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1630 XmlStringRef namespacePrefix = dtdAttribute.attributeName;
1631 XmlStringRef namespaceUri = dtdAttribute.defaultValue;
1632 if (((namespacePrefix == "xml"_L1)
1633 ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1))
1634 || namespaceUri == "http://www.w3.org/2000/xmlns/"_L1
1635 || namespaceUri.isEmpty()
1636 || namespacePrefix == "xmlns"_L1)
1637 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Illegal namespace declaration."));
1638
1639 namespaceDeclaration.prefix = namespacePrefix;
1640 namespaceDeclaration.namespaceUri = namespaceUri;
1641 }
1642 }
1643 }
1644
1645 tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix);
1646
1647 attributes.resize(size: n);
1648
1649 for (qsizetype i = 0; i < n; ++i) {
1650 QXmlStreamAttribute &attribute = attributes[i];
1651 Attribute &attrib = attributeStack[i];
1652 XmlStringRef prefix(symPrefix(symbol: attrib.key));
1653 XmlStringRef name(symString(symbol: attrib.key));
1654 XmlStringRef qualifiedName(symName(symbol: attrib.key));
1655 XmlStringRef value(symString(symbol: attrib.value));
1656
1657 attribute.m_name = name;
1658 attribute.m_qualifiedName = qualifiedName;
1659 attribute.m_value = value;
1660
1661 if (!prefix.isEmpty()) {
1662 XmlStringRef attributeNamespaceUri = namespaceForPrefix(prefix);
1663 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1664 }
1665
1666 for (qsizetype j = 0; j < i; ++j) {
1667 if (attributes[j].name() == attribute.name()
1668 && attributes[j].namespaceUri() == attribute.namespaceUri()
1669 && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName()))
1670 {
1671 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Attribute '%1' redefined.").arg(a: attribute.qualifiedName()));
1672 return;
1673 }
1674 }
1675 }
1676
1677 for (DtdAttribute &dtdAttribute : dtdAttributes) {
1678 if (dtdAttribute.isNamespaceAttribute
1679 || dtdAttribute.defaultValue.isNull()
1680 || dtdAttribute.tagName != qualifiedName
1681 || dtdAttribute.attributeQualifiedName.isNull())
1682 continue;
1683 qsizetype i = 0;
1684 while (i < n && symName(symbol: attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1685 ++i;
1686 if (i != n)
1687 continue;
1688
1689
1690
1691 QXmlStreamAttribute attribute;
1692 attribute.m_name = dtdAttribute.attributeName;
1693 attribute.m_qualifiedName = dtdAttribute.attributeQualifiedName;
1694 attribute.m_value = dtdAttribute.defaultValue;
1695
1696 if (!dtdAttribute.attributePrefix.isEmpty()) {
1697 XmlStringRef attributeNamespaceUri = namespaceForPrefix(prefix: dtdAttribute.attributePrefix);
1698 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1699 }
1700 attribute.m_isDefault = true;
1701 attributes.append(t: std::move(attribute));
1702 }
1703}
1704
1705void QXmlStreamReaderPrivate::resolvePublicNamespaces()
1706{
1707 const Tag &tag = tagStack.top();
1708 qsizetype n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize;
1709 publicNamespaceDeclarations.resize(size: n);
1710 for (qsizetype i = 0; i < n; ++i) {
1711 const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(index: tag.namespaceDeclarationsSize + i);
1712 QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i];
1713 publicNamespaceDeclaration.m_prefix = namespaceDeclaration.prefix;
1714 publicNamespaceDeclaration.m_namespaceUri = namespaceDeclaration.namespaceUri;
1715 }
1716}
1717
1718void QXmlStreamReaderPrivate::resolveDtd()
1719{
1720 publicNotationDeclarations.resize(size: notationDeclarations.size());
1721 for (qsizetype i = 0; i < notationDeclarations.size(); ++i) {
1722 const QXmlStreamReaderPrivate::NotationDeclaration &notationDeclaration = notationDeclarations.at(index: i);
1723 QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i];
1724 publicNotationDeclaration.m_name = notationDeclaration.name;
1725 publicNotationDeclaration.m_systemId = notationDeclaration.systemId;
1726 publicNotationDeclaration.m_publicId = notationDeclaration.publicId;
1727
1728 }
1729 notationDeclarations.clear();
1730 publicEntityDeclarations.resize(size: entityDeclarations.size());
1731 for (qsizetype i = 0; i < entityDeclarations.size(); ++i) {
1732 const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(index: i);
1733 QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i];
1734 publicEntityDeclaration.m_name = entityDeclaration.name;
1735 publicEntityDeclaration.m_notationName = entityDeclaration.notationName;
1736 publicEntityDeclaration.m_systemId = entityDeclaration.systemId;
1737 publicEntityDeclaration.m_publicId = entityDeclaration.publicId;
1738 publicEntityDeclaration.m_value = entityDeclaration.value;
1739 }
1740 entityDeclarations.clear();
1741 parameterEntityHash.clear();
1742}
1743
1744uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex)
1745{
1746 bool ok = true;
1747 uint s;
1748 // ### add toXShort to XmlString?
1749 if (sym(index: symbolIndex).c == 'x')
1750 s = symString(index: symbolIndex, offset: 1).view().toUInt(ok: &ok, base: 16);
1751 else
1752 s = symString(index: symbolIndex).view().toUInt(ok: &ok, base: 10);
1753
1754 ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff)
1755 || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint));
1756
1757 return ok ? s : 0;
1758}
1759
1760
1761void QXmlStreamReaderPrivate::checkPublicLiteral(QStringView publicId)
1762{
1763//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
1764
1765 const char16_t *data = publicId.utf16();
1766 uchar c = 0;
1767 qsizetype i;
1768 for (i = publicId.size() - 1; i >= 0; --i) {
1769 if (data[i] < 256)
1770 switch ((c = data[i])) {
1771 case ' ': case '\n': case '\r': case '-': case '(': case ')':
1772 case '+': case ',': case '.': case '/': case ':': case '=':
1773 case '?': case ';': case '!': case '*': case '#': case '@':
1774 case '$': case '_': case '%': case '\'': case '\"':
1775 continue;
1776 default:
1777 if (isAsciiLetterOrNumber(c))
1778 continue;
1779 }
1780 break;
1781 }
1782 if (i >= 0)
1783 raiseWellFormedError(message: QXmlStream::tr(sourceText: "Unexpected character '%1' in public id literal.").arg(a: QChar(QLatin1Char(c))));
1784}
1785
1786/*
1787 Checks whether the document starts with an xml declaration. If it
1788 does, this function returns \c true; otherwise it sets up everything
1789 for a synthetic start document event and returns \c false.
1790 */
1791bool QXmlStreamReaderPrivate::checkStartDocument()
1792{
1793 hasCheckedStartDocument = true;
1794
1795 if (scanString(str: spell[XML], tokenToInject: XML))
1796 return true;
1797
1798 type = QXmlStreamReader::StartDocument;
1799 if (atEnd) {
1800 hasCheckedStartDocument = false;
1801 raiseError(error: QXmlStreamReader::PrematureEndOfDocumentError);
1802 }
1803 return false;
1804}
1805
1806void QXmlStreamReaderPrivate::startDocument()
1807{
1808 QString err;
1809 if (documentVersion != "1.0"_L1) {
1810 if (documentVersion.view().contains(c: u' '))
1811 err = QXmlStream::tr(sourceText: "Invalid XML version string.");
1812 else
1813 err = QXmlStream::tr(sourceText: "Unsupported XML version.");
1814 }
1815 qsizetype n = attributeStack.size();
1816
1817 /* We use this bool to ensure that the pesudo attributes are in the
1818 * proper order:
1819 *
1820 * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
1821
1822 for (qsizetype i = 0; err.isNull() && i < n; ++i) {
1823 Attribute &attrib = attributeStack[i];
1824 XmlStringRef prefix(symPrefix(symbol: attrib.key));
1825 XmlStringRef key(symString(symbol: attrib.key));
1826 XmlStringRef value(symString(symbol: attrib.value));
1827
1828 if (prefix.isEmpty() && key == "encoding"_L1) {
1829 documentEncoding = value;
1830
1831 if (hasStandalone)
1832 err = QXmlStream::tr(sourceText: "The standalone pseudo attribute must appear after the encoding.");
1833 if (!QXmlUtils::isEncName(encName: value))
1834 err = QXmlStream::tr(sourceText: "%1 is an invalid encoding name.").arg(a: value);
1835 else {
1836 QByteArray enc = value.toString().toUtf8();
1837 if (!lockEncoding) {
1838 decoder = QStringDecoder(enc.constData());
1839 if (!decoder.isValid()) {
1840 err = QXmlStream::tr(sourceText: "Encoding %1 is unsupported").arg(a: value);
1841 } else {
1842 readBuffer = decoder(QByteArrayView(rawReadBuffer).first(n: nbytesread));
1843 }
1844 }
1845 }
1846 } else if (prefix.isEmpty() && key == "standalone"_L1) {
1847 hasStandalone = true;
1848 if (value == "yes"_L1)
1849 standalone = true;
1850 else if (value == "no"_L1)
1851 standalone = false;
1852 else
1853 err = QXmlStream::tr(sourceText: "Standalone accepts only yes or no.");
1854 } else {
1855 err = QXmlStream::tr(sourceText: "Invalid attribute in XML declaration: %1 = %2").arg(a: key).arg(a: value);
1856 }
1857 }
1858
1859 if (!err.isNull())
1860 raiseWellFormedError(message: err);
1861 attributeStack.clear();
1862}
1863
1864
1865void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message)
1866{
1867 this->error = error;
1868 errorString = message;
1869 if (errorString.isNull()) {
1870 if (error == QXmlStreamReader::PrematureEndOfDocumentError)
1871 errorString = QXmlStream::tr(sourceText: "Premature end of document.");
1872 else if (error == QXmlStreamReader::CustomError)
1873 errorString = QXmlStream::tr(sourceText: "Invalid document.");
1874 }
1875
1876 type = QXmlStreamReader::Invalid;
1877}
1878
1879void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message)
1880{
1881 raiseError(error: QXmlStreamReader::NotWellFormedError, message);
1882}
1883
1884void QXmlStreamReaderPrivate::raiseNamePrefixTooLongError()
1885{
1886 // TODO: add a ImplementationLimitsExceededError and use it instead
1887 raiseError(error: QXmlStreamReader::NotWellFormedError,
1888 message: QXmlStream::tr(sourceText: "Length of XML attribute name exceeds implementation limits (4KiB "
1889 "characters)."));
1890}
1891
1892void QXmlStreamReaderPrivate::parseError()
1893{
1894
1895 if (token == EOF_SYMBOL) {
1896 raiseError(error: QXmlStreamReader::PrematureEndOfDocumentError);
1897 return;
1898 }
1899 const int nmax = 4;
1900 QString error_message;
1901 int ers = state_stack[tos];
1902 int nexpected = 0;
1903 int expected[nmax];
1904 if (token != XML_ERROR)
1905 for (int tk = 0; tk < TERMINAL_COUNT; ++tk) {
1906 int k = t_action(state: ers, token: tk);
1907 if (k <= 0)
1908 continue;
1909 if (spell[tk]) {
1910 if (nexpected < nmax)
1911 expected[nexpected++] = tk;
1912 }
1913 }
1914
1915 if (nexpected && nexpected < nmax) {
1916 //: '<first option>'
1917 QString exp_str = QXmlStream::tr(sourceText: "'%1'", disambiguation: "expected")
1918 .arg(a: QLatin1StringView(spell[expected[0]]));
1919 if (nexpected == 2) {
1920 //: <first option>, '<second option>'
1921 exp_str = QXmlStream::tr(sourceText: "%1 or '%2'", disambiguation: "expected")
1922 .arg(args&: exp_str, args: QLatin1StringView(spell[expected[1]]));
1923 } else if (nexpected > 2) {
1924 int s = 1;
1925 for (; s < nexpected - 1; ++s) {
1926 //: <options so far>, '<next option>'
1927 exp_str = QXmlStream::tr(sourceText: "%1, '%2'", disambiguation: "expected")
1928 .arg(args&: exp_str, args: QLatin1StringView(spell[expected[s]]));
1929 }
1930 //: <options so far>, or '<final option>'
1931 exp_str = QXmlStream::tr(sourceText: "%1, or '%2'", disambiguation: "expected")
1932 .arg(args&: exp_str, args: QLatin1StringView(spell[expected[s]]));
1933 }
1934 error_message = QXmlStream::tr(sourceText: "Expected %1, but got '%2'.")
1935 .arg(args&: exp_str, args: QLatin1StringView(spell[token]));
1936 } else {
1937 error_message = QXmlStream::tr(sourceText: "Unexpected '%1'.").arg(a: QLatin1StringView(spell[token]));
1938 }
1939
1940 raiseWellFormedError(message: error_message);
1941}
1942
1943void QXmlStreamReaderPrivate::resume(int rule) {
1944 resumeReduction = rule;
1945 if (error == QXmlStreamReader::NoError)
1946 raiseError(error: QXmlStreamReader::PrematureEndOfDocumentError);
1947}
1948
1949/*! Returns the current line number, starting with 1.
1950
1951\sa columnNumber(), characterOffset()
1952 */
1953qint64 QXmlStreamReader::lineNumber() const
1954{
1955 Q_D(const QXmlStreamReader);
1956 return d->lineNumber + 1; // in public we start with 1
1957}
1958
1959/*! Returns the current column number, starting with 0.
1960
1961\sa lineNumber(), characterOffset()
1962 */
1963qint64 QXmlStreamReader::columnNumber() const
1964{
1965 Q_D(const QXmlStreamReader);
1966 return d->characterOffset - d->lastLineStart + d->readBufferPos;
1967}
1968
1969/*! Returns the current character offset, starting with 0.
1970
1971\sa lineNumber(), columnNumber()
1972*/
1973qint64 QXmlStreamReader::characterOffset() const
1974{
1975 Q_D(const QXmlStreamReader);
1976 return d->characterOffset + d->readBufferPos;
1977}
1978
1979
1980/*! Returns the text of \l Characters, \l Comment, \l DTD, or
1981 EntityReference.
1982 */
1983QStringView QXmlStreamReader::text() const
1984{
1985 Q_D(const QXmlStreamReader);
1986 return d->text;
1987}
1988
1989
1990/*! If the tokenType() is \l DTD, this function returns the DTD's
1991 notation declarations. Otherwise an empty vector is returned.
1992
1993 The QXmlStreamNotationDeclarations class is defined to be a QList
1994 of QXmlStreamNotationDeclaration.
1995 */
1996QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const
1997{
1998 Q_D(const QXmlStreamReader);
1999 if (d->notationDeclarations.size())
2000 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2001 return d->publicNotationDeclarations;
2002}
2003
2004
2005/*! If the tokenType() is \l DTD, this function returns the DTD's
2006 unparsed (external) entity declarations. Otherwise an empty vector is returned.
2007
2008 The QXmlStreamEntityDeclarations class is defined to be a QList
2009 of QXmlStreamEntityDeclaration.
2010 */
2011QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const
2012{
2013 Q_D(const QXmlStreamReader);
2014 if (d->entityDeclarations.size())
2015 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2016 return d->publicEntityDeclarations;
2017}
2018
2019/*!
2020 \since 4.4
2021
2022 If the tokenType() is \l DTD, this function returns the DTD's
2023 name. Otherwise an empty string is returned.
2024
2025 */
2026QStringView QXmlStreamReader::dtdName() const
2027{
2028 Q_D(const QXmlStreamReader);
2029 if (d->type == QXmlStreamReader::DTD)
2030 return d->dtdName;
2031 return QStringView();
2032}
2033
2034/*!
2035 \since 4.4
2036
2037 If the tokenType() is \l DTD, this function returns the DTD's
2038 public identifier. Otherwise an empty string is returned.
2039
2040 */
2041QStringView QXmlStreamReader::dtdPublicId() const
2042{
2043 Q_D(const QXmlStreamReader);
2044 if (d->type == QXmlStreamReader::DTD)
2045 return d->dtdPublicId;
2046 return QStringView();
2047}
2048
2049/*!
2050 \since 4.4
2051
2052 If the tokenType() is \l DTD, this function returns the DTD's
2053 system identifier. Otherwise an empty string is returned.
2054
2055 */
2056QStringView QXmlStreamReader::dtdSystemId() const
2057{
2058 Q_D(const QXmlStreamReader);
2059 if (d->type == QXmlStreamReader::DTD)
2060 return d->dtdSystemId;
2061 return QStringView();
2062}
2063
2064/*!
2065 \since 5.15
2066
2067 Returns the maximum amount of characters a single entity is
2068 allowed to expand into. If a single entity expands past the
2069 given limit, the document is not considered well formed.
2070
2071 \sa setEntityExpansionLimit
2072*/
2073int QXmlStreamReader::entityExpansionLimit() const
2074{
2075 Q_D(const QXmlStreamReader);
2076 return d->entityExpansionLimit;
2077}
2078
2079/*!
2080 \since 5.15
2081
2082 Sets the maximum amount of characters a single entity is
2083 allowed to expand into to \a limit. If a single entity expands
2084 past the given limit, the document is not considered well formed.
2085
2086 The limit is there to prevent DoS attacks when loading unknown
2087 XML documents where recursive entity expansion could otherwise
2088 exhaust all available memory.
2089
2090 The default value for this property is 4096 characters.
2091
2092 \sa entityExpansionLimit
2093*/
2094void QXmlStreamReader::setEntityExpansionLimit(int limit)
2095{
2096 Q_D(QXmlStreamReader);
2097 d->entityExpansionLimit = limit;
2098}
2099
2100/*! If the tokenType() is \l StartElement, this function returns the
2101 element's namespace declarations. Otherwise an empty vector is
2102 returned.
2103
2104 The QXmlStreamNamespaceDeclarations class is defined to be a QList
2105 of QXmlStreamNamespaceDeclaration.
2106
2107 \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations()
2108 */
2109QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const
2110{
2111 Q_D(const QXmlStreamReader);
2112 if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement)
2113 const_cast<QXmlStreamReaderPrivate *>(d)->resolvePublicNamespaces();
2114 return d->publicNamespaceDeclarations;
2115}
2116
2117
2118/*!
2119 \since 4.4
2120
2121 Adds an \a extraNamespaceDeclaration. The declaration will be
2122 valid for children of the current element, or - should the function
2123 be called before any elements are read - for the entire XML
2124 document.
2125
2126 \sa namespaceDeclarations(), addExtraNamespaceDeclarations(), setNamespaceProcessing()
2127 */
2128void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration)
2129{
2130 Q_D(QXmlStreamReader);
2131 QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
2132 namespaceDeclaration.prefix = d->addToStringStorage(s: extraNamespaceDeclaration.prefix());
2133 namespaceDeclaration.namespaceUri = d->addToStringStorage(s: extraNamespaceDeclaration.namespaceUri());
2134}
2135
2136/*!
2137 \since 4.4
2138
2139 Adds a vector of declarations specified by \a extraNamespaceDeclarations.
2140
2141 \sa namespaceDeclarations(), addExtraNamespaceDeclaration()
2142 */
2143void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations)
2144{
2145 for (const auto &extraNamespaceDeclaration : extraNamespaceDeclarations)
2146 addExtraNamespaceDeclaration(extraNamespaceDeclaration);
2147}
2148
2149
2150/*! Convenience function to be called in case a StartElement was
2151 read. Reads until the corresponding EndElement and returns all text
2152 in-between. In case of no error, the current token (see tokenType())
2153 after having called this function is EndElement.
2154
2155 The function concatenates text() when it reads either \l Characters
2156 or EntityReference tokens, but skips ProcessingInstruction and \l
2157 Comment. If the current token is not StartElement, an empty string is
2158 returned.
2159
2160 The \a behaviour defines what happens in case anything else is
2161 read before reaching EndElement. The function can include the text from
2162 child elements (useful for example for HTML), ignore child elements, or
2163 raise an UnexpectedElementError and return what was read so far (default).
2164
2165 \since 4.6
2166 */
2167QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour)
2168{
2169 Q_D(QXmlStreamReader);
2170 if (isStartElement()) {
2171 QString result;
2172 forever {
2173 switch (readNext()) {
2174 case Characters:
2175 case EntityReference:
2176 result.insert(i: result.size(), v: d->text);
2177 break;
2178 case EndElement:
2179 return result;
2180 case ProcessingInstruction:
2181 case Comment:
2182 break;
2183 case StartElement:
2184 if (behaviour == SkipChildElements) {
2185 skipCurrentElement();
2186 break;
2187 } else if (behaviour == IncludeChildElements) {
2188 result += readElementText(behaviour);
2189 break;
2190 }
2191 Q_FALLTHROUGH();
2192 default:
2193 if (d->error || behaviour == ErrorOnUnexpectedElement) {
2194 if (!d->error)
2195 d->raiseError(error: UnexpectedElementError, message: QXmlStream::tr(sourceText: "Expected character data."));
2196 return result;
2197 }
2198 }
2199 }
2200 }
2201 return QString();
2202}
2203
2204/*! Raises a custom error with an optional error \a message.
2205
2206 \sa error(), errorString()
2207 */
2208void QXmlStreamReader::raiseError(const QString& message)
2209{
2210 Q_D(QXmlStreamReader);
2211 d->raiseError(error: CustomError, message);
2212}
2213
2214/*!
2215 Returns the error message that was set with raiseError().
2216
2217 \sa error(), lineNumber(), columnNumber(), characterOffset()
2218 */
2219QString QXmlStreamReader::errorString() const
2220{
2221 Q_D(const QXmlStreamReader);
2222 if (d->type == QXmlStreamReader::Invalid)
2223 return d->errorString;
2224 return QString();
2225}
2226
2227/*! Returns the type of the current error, or NoError if no error occurred.
2228
2229 \sa errorString(), raiseError()
2230 */
2231QXmlStreamReader::Error QXmlStreamReader::error() const
2232{
2233 Q_D(const QXmlStreamReader);
2234 if (d->type == QXmlStreamReader::Invalid)
2235 return d->error;
2236 return NoError;
2237}
2238
2239/*!
2240 Returns the target of a ProcessingInstruction.
2241 */
2242QStringView QXmlStreamReader::processingInstructionTarget() const
2243{
2244 Q_D(const QXmlStreamReader);
2245 return d->processingInstructionTarget;
2246}
2247
2248/*!
2249 Returns the data of a ProcessingInstruction.
2250 */
2251QStringView QXmlStreamReader::processingInstructionData() const
2252{
2253 Q_D(const QXmlStreamReader);
2254 return d->processingInstructionData;
2255}
2256
2257
2258
2259/*!
2260 Returns the local name of a StartElement, EndElement, or an EntityReference.
2261
2262 \sa namespaceUri(), qualifiedName()
2263 */
2264QStringView QXmlStreamReader::name() const
2265{
2266 Q_D(const QXmlStreamReader);
2267 return d->name;
2268}
2269
2270/*!
2271 Returns the namespaceUri of a StartElement or EndElement.
2272
2273 \sa name(), qualifiedName()
2274 */
2275QStringView QXmlStreamReader::namespaceUri() const
2276{
2277 Q_D(const QXmlStreamReader);
2278 return d->namespaceUri;
2279}
2280
2281/*!
2282 Returns the qualified name of a StartElement or EndElement;
2283
2284 A qualified name is the raw name of an element in the XML data. It
2285 consists of the namespace prefix, followed by colon, followed by the
2286 element's local name. Since the namespace prefix is not unique (the
2287 same prefix can point to different namespaces and different prefixes
2288 can point to the same namespace), you shouldn't use qualifiedName(),
2289 but the resolved namespaceUri() and the attribute's local name().
2290
2291 \sa name(), prefix(), namespaceUri()
2292 */
2293QStringView QXmlStreamReader::qualifiedName() const
2294{
2295 Q_D(const QXmlStreamReader);
2296 return d->qualifiedName;
2297}
2298
2299
2300
2301/*!
2302 \since 4.4
2303
2304 Returns the prefix of a StartElement or EndElement.
2305
2306 \sa name(), qualifiedName()
2307*/
2308QStringView QXmlStreamReader::prefix() const
2309{
2310 Q_D(const QXmlStreamReader);
2311 return d->prefix;
2312}
2313
2314/*!
2315 Returns the attributes of a StartElement.
2316 */
2317QXmlStreamAttributes QXmlStreamReader::attributes() const
2318{
2319 Q_D(const QXmlStreamReader);
2320 return d->attributes;
2321}
2322
2323#endif // feature xmlstreamreader
2324
2325/*!
2326 \class QXmlStreamAttribute
2327 \inmodule QtCore
2328 \since 4.3
2329 \reentrant
2330 \brief The QXmlStreamAttribute class represents a single XML attribute.
2331
2332 \ingroup xml-tools
2333
2334 An attribute consists of an optionally empty namespaceUri(), a
2335 name(), a value(), and an isDefault() attribute.
2336
2337 The raw XML attribute name is returned as qualifiedName().
2338*/
2339
2340/*!
2341 Creates an empty attribute.
2342 */
2343QXmlStreamAttribute::QXmlStreamAttribute()
2344{
2345 m_isDefault = false;
2346}
2347
2348/*! Constructs an attribute in the namespace described with \a
2349 namespaceUri with \a name and value \a value.
2350 */
2351QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)
2352{
2353 m_namespaceUri = namespaceUri;
2354 m_name = m_qualifiedName = name;
2355 m_value = value;
2356 m_namespaceUri = namespaceUri;
2357}
2358
2359/*!
2360 Constructs an attribute with qualified name \a qualifiedName and value \a value.
2361 */
2362QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value)
2363{
2364 qsizetype colon = qualifiedName.indexOf(c: u':');
2365 m_name = qualifiedName.mid(position: colon + 1);
2366 m_qualifiedName = qualifiedName;
2367 m_value = value;
2368}
2369
2370/*! \fn QStringView QXmlStreamAttribute::namespaceUri() const
2371
2372 Returns the attribute's resolved namespaceUri, or an empty string
2373 reference if the attribute does not have a defined namespace.
2374 */
2375/*! \fn QStringView QXmlStreamAttribute::name() const
2376 Returns the attribute's local name.
2377 */
2378/*! \fn QStringView QXmlStreamAttribute::qualifiedName() const
2379 Returns the attribute's qualified name.
2380
2381 A qualified name is the raw name of an attribute in the XML
2382 data. It consists of the namespace prefix(), followed by colon,
2383 followed by the attribute's local name(). Since the namespace prefix
2384 is not unique (the same prefix can point to different namespaces
2385 and different prefixes can point to the same namespace), you
2386 shouldn't use qualifiedName(), but the resolved namespaceUri() and
2387 the attribute's local name().
2388 */
2389/*!
2390 \fn QStringView QXmlStreamAttribute::prefix() const
2391 \since 4.4
2392 Returns the attribute's namespace prefix.
2393
2394 \sa name(), qualifiedName()
2395
2396*/
2397
2398/*! \fn QStringView QXmlStreamAttribute::value() const
2399 Returns the attribute's value.
2400 */
2401
2402/*! \fn bool QXmlStreamAttribute::isDefault() const
2403
2404 Returns \c true if the parser added this attribute with a default
2405 value following an ATTLIST declaration in the DTD; otherwise
2406 returns \c false.
2407*/
2408/*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &other) const
2409
2410 Compares this attribute with \a other and returns \c true if they are
2411 equal; otherwise returns \c false.
2412 */
2413/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other) const
2414
2415 Compares this attribute with \a other and returns \c true if they are
2416 not equal; otherwise returns \c false.
2417 */
2418
2419/*!
2420 \class QXmlStreamAttributes
2421 \inmodule QtCore
2422 \since 4.3
2423 \reentrant
2424 \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute.
2425
2426 Attributes are returned by a QXmlStreamReader in
2427 \l{QXmlStreamReader::attributes()} {attributes()} when the reader
2428 reports a \l {QXmlStreamReader::StartElement}{start element}. The
2429 class can also be used with a QXmlStreamWriter as an argument to
2430 \l {QXmlStreamWriter::writeAttributes()}{writeAttributes()}.
2431
2432 The convenience function value() loops over the vector and returns
2433 an attribute value for a given namespaceUri and an attribute's
2434 name.
2435
2436 New attributes can be added with append().
2437
2438 \ingroup xml-tools
2439*/
2440
2441/*!
2442 \fn QXmlStreamAttributes::QXmlStreamAttributes()
2443
2444 A constructor for QXmlStreamAttributes.
2445*/
2446
2447/*!
2448 \typedef QXmlStreamNotationDeclarations
2449 \relates QXmlStreamNotationDeclaration
2450
2451 Synonym for QList<QXmlStreamNotationDeclaration>.
2452*/
2453
2454
2455/*!
2456 \class QXmlStreamNotationDeclaration
2457 \inmodule QtCore
2458 \since 4.3
2459 \reentrant
2460 \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration.
2461
2462 \ingroup xml-tools
2463
2464 An notation declaration consists of a name(), a systemId(), and a publicId().
2465*/
2466
2467/*!
2468 Creates an empty notation declaration.
2469*/
2470QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration()
2471{
2472}
2473
2474/*! \fn QStringView QXmlStreamNotationDeclaration::name() const
2475
2476Returns the notation name.
2477*/
2478/*! \fn QStringView QXmlStreamNotationDeclaration::systemId() const
2479
2480Returns the system identifier.
2481*/
2482/*! \fn QStringView QXmlStreamNotationDeclaration::publicId() const
2483
2484Returns the public identifier.
2485*/
2486
2487/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other) const
2488
2489 Compares this notation declaration with \a other and returns \c true
2490 if they are equal; otherwise returns \c false.
2491 */
2492/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other) const
2493
2494 Compares this notation declaration with \a other and returns \c true
2495 if they are not equal; otherwise returns \c false.
2496 */
2497
2498/*!
2499 \typedef QXmlStreamNamespaceDeclarations
2500 \relates QXmlStreamNamespaceDeclaration
2501
2502 Synonym for QList<QXmlStreamNamespaceDeclaration>.
2503*/
2504
2505/*!
2506 \class QXmlStreamNamespaceDeclaration
2507 \inmodule QtCore
2508 \since 4.3
2509 \reentrant
2510 \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration.
2511
2512 \ingroup xml-tools
2513
2514 An namespace declaration consists of a prefix() and a namespaceUri().
2515*/
2516/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other) const
2517
2518 Compares this namespace declaration with \a other and returns \c true
2519 if they are equal; otherwise returns \c false.
2520 */
2521/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other) const
2522
2523 Compares this namespace declaration with \a other and returns \c true
2524 if they are not equal; otherwise returns \c false.
2525 */
2526
2527/*!
2528 Creates an empty namespace declaration.
2529*/
2530QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration()
2531{
2532}
2533
2534/*!
2535 \since 4.4
2536
2537 Creates a namespace declaration with \a prefix and \a namespaceUri.
2538*/
2539QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri)
2540{
2541 m_prefix = prefix;
2542 m_namespaceUri = namespaceUri;
2543}
2544
2545/*! \fn QStringView QXmlStreamNamespaceDeclaration::prefix() const
2546
2547Returns the prefix.
2548*/
2549/*! \fn QStringView QXmlStreamNamespaceDeclaration::namespaceUri() const
2550
2551Returns the namespaceUri.
2552*/
2553
2554
2555
2556
2557/*!
2558 \typedef QXmlStreamEntityDeclarations
2559 \relates QXmlStreamEntityDeclaration
2560
2561 Synonym for QList<QXmlStreamEntityDeclaration>.
2562*/
2563
2564/*!
2565 \class QXmlString
2566 \inmodule QtCore
2567 \since 6.0
2568 \internal
2569*/
2570
2571/*!
2572 \class QXmlStreamEntityDeclaration
2573 \inmodule QtCore
2574 \since 4.3
2575 \reentrant
2576 \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration.
2577
2578 \ingroup xml-tools
2579
2580 An entity declaration consists of a name(), a notationName(), a
2581 systemId(), a publicId(), and a value().
2582*/
2583
2584/*!
2585 Creates an empty entity declaration.
2586*/
2587QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration()
2588{
2589}
2590
2591/*! \fn QStringView QXmlStreamEntityDeclaration::name() const
2592
2593Returns the entity name.
2594*/
2595/*! \fn QStringView QXmlStreamEntityDeclaration::notationName() const
2596
2597Returns the notation name.
2598*/
2599/*! \fn QStringView QXmlStreamEntityDeclaration::systemId() const
2600
2601Returns the system identifier.
2602*/
2603/*! \fn QStringView QXmlStreamEntityDeclaration::publicId() const
2604
2605Returns the public identifier.
2606*/
2607/*! \fn QStringView QXmlStreamEntityDeclaration::value() const
2608
2609Returns the entity's value.
2610*/
2611
2612/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other) const
2613
2614 Compares this entity declaration with \a other and returns \c true if
2615 they are equal; otherwise returns \c false.
2616 */
2617/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other) const
2618
2619 Compares this entity declaration with \a other and returns \c true if
2620 they are not equal; otherwise returns \c false.
2621 */
2622
2623/*! Returns the value of the attribute \a name in the namespace
2624 described with \a namespaceUri, or an empty string reference if the
2625 attribute is not defined. The \a namespaceUri can be empty.
2626
2627 \note In Qt versions prior to 6.6, this function was implemented as an
2628 overload set accepting combinations of QString and QLatin1StringView only.
2629 */
2630QStringView QXmlStreamAttributes::value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
2631{
2632 for (const QXmlStreamAttribute &attribute : *this) {
2633 if (attribute.name() == name && attribute.namespaceUri() == namespaceUri)
2634 return attribute.value();
2635 }
2636 return QStringView();
2637}
2638
2639/*!\overload
2640
2641 Returns the value of the attribute with qualified name \a
2642 qualifiedName , or an empty string reference if the attribute is not
2643 defined. A qualified name is the raw name of an attribute in the XML
2644 data. It consists of the namespace prefix, followed by colon,
2645 followed by the attribute's local name. Since the namespace prefix
2646 is not unique (the same prefix can point to different namespaces and
2647 different prefixes can point to the same namespace), you shouldn't
2648 use qualified names, but a resolved namespaceUri and the attribute's
2649 local name.
2650
2651 \note In Qt versions prior to 6.6, this function was implemented as an
2652 overload set accepting QString and QLatin1StringView only.
2653
2654 */
2655QStringView QXmlStreamAttributes::value(QAnyStringView qualifiedName) const noexcept
2656{
2657 for (const QXmlStreamAttribute &attribute : *this) {
2658 if (attribute.qualifiedName() == qualifiedName)
2659 return attribute.value();
2660 }
2661 return QStringView();
2662}
2663
2664/*!Appends a new attribute with \a name in the namespace
2665 described with \a namespaceUri, and value \a value. The \a
2666 namespaceUri can be empty.
2667 */
2668void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value)
2669{
2670 append(t: QXmlStreamAttribute(namespaceUri, name, value));
2671}
2672
2673/*!\overload
2674 Appends a new attribute with qualified name \a qualifiedName and
2675 value \a value.
2676 */
2677void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value)
2678{
2679 append(t: QXmlStreamAttribute(qualifiedName, value));
2680}
2681
2682#if QT_CONFIG(xmlstreamreader)
2683
2684/*! \fn bool QXmlStreamReader::isStartDocument() const
2685 Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false.
2686*/
2687/*! \fn bool QXmlStreamReader::isEndDocument() const
2688 Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false.
2689*/
2690/*! \fn bool QXmlStreamReader::isStartElement() const
2691 Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false.
2692*/
2693/*! \fn bool QXmlStreamReader::isEndElement() const
2694 Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false.
2695*/
2696/*! \fn bool QXmlStreamReader::isCharacters() const
2697 Returns \c true if tokenType() equals \l Characters; otherwise returns \c false.
2698
2699 \sa isWhitespace(), isCDATA()
2700*/
2701/*! \fn bool QXmlStreamReader::isComment() const
2702 Returns \c true if tokenType() equals \l Comment; otherwise returns \c false.
2703*/
2704/*! \fn bool QXmlStreamReader::isDTD() const
2705 Returns \c true if tokenType() equals \l DTD; otherwise returns \c false.
2706*/
2707/*! \fn bool QXmlStreamReader::isEntityReference() const
2708 Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false.
2709*/
2710/*! \fn bool QXmlStreamReader::isProcessingInstruction() const
2711 Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false.
2712*/
2713
2714/*! Returns \c true if the reader reports characters that only consist
2715 of white-space; otherwise returns \c false.
2716
2717 \sa isCharacters(), text()
2718*/
2719bool QXmlStreamReader::isWhitespace() const
2720{
2721 Q_D(const QXmlStreamReader);
2722 return d->type == QXmlStreamReader::Characters && d->isWhitespace;
2723}
2724
2725/*! Returns \c true if the reader reports characters that stem from a
2726 CDATA section; otherwise returns \c false.
2727
2728 \sa isCharacters(), text()
2729*/
2730bool QXmlStreamReader::isCDATA() const
2731{
2732 Q_D(const QXmlStreamReader);
2733 return d->type == QXmlStreamReader::Characters && d->isCDATA;
2734}
2735
2736
2737
2738/*!
2739 Returns \c true if this document has been declared standalone in the
2740 XML declaration; otherwise returns \c false.
2741
2742 If no XML declaration has been parsed, this function returns \c false.
2743
2744 \sa hasStandaloneDeclaration()
2745 */
2746bool QXmlStreamReader::isStandaloneDocument() const
2747{
2748 Q_D(const QXmlStreamReader);
2749 return d->standalone;
2750}
2751
2752/*!
2753 \since 6.6
2754
2755 Returns \c true if this document has an explicit standalone
2756 declaration (can be 'yes' or 'no'); otherwise returns \c false;
2757
2758 If no XML declaration has been parsed, this function returns \c false.
2759
2760 \sa isStandaloneDocument()
2761 */
2762bool QXmlStreamReader::hasStandaloneDeclaration() const
2763{
2764 Q_D(const QXmlStreamReader);
2765 return d->hasStandalone;
2766}
2767
2768/*!
2769 \since 4.4
2770
2771 If the tokenType() is \l StartDocument, this function returns the
2772 version string as specified in the XML declaration.
2773 Otherwise an empty string is returned.
2774 */
2775QStringView QXmlStreamReader::documentVersion() const
2776{
2777 Q_D(const QXmlStreamReader);
2778 if (d->type == QXmlStreamReader::StartDocument)
2779 return d->documentVersion;
2780 return QStringView();
2781}
2782
2783/*!
2784 \since 4.4
2785
2786 If the tokenType() is \l StartDocument, this function returns the
2787 encoding string as specified in the XML declaration.
2788 Otherwise an empty string is returned.
2789 */
2790QStringView QXmlStreamReader::documentEncoding() const
2791{
2792 Q_D(const QXmlStreamReader);
2793 if (d->type == QXmlStreamReader::StartDocument)
2794 return d->documentEncoding;
2795 return QStringView();
2796}
2797
2798#endif // feature xmlstreamreader
2799
2800/*!
2801 \class QXmlStreamWriter
2802 \inmodule QtCore
2803 \since 4.3
2804 \reentrant
2805
2806 \brief The QXmlStreamWriter class provides an XML writer with a
2807 simple streaming API.
2808
2809 \ingroup xml-tools
2810 \ingroup qtserialization
2811
2812 QXmlStreamWriter is the counterpart to QXmlStreamReader for writing
2813 XML. Like its related class, it operates on a QIODevice specified
2814 with setDevice(). The API is simple and straightforward: for every
2815 XML token or event you want to write, the writer provides a
2816 specialized function.
2817
2818 You start a document with writeStartDocument() and end it with
2819 writeEndDocument(). This will implicitly close all remaining open
2820 tags.
2821
2822 Element tags are opened with writeStartElement() followed by
2823 writeAttribute() or writeAttributes(), element content, and then
2824 writeEndElement(). A shorter form writeEmptyElement() can be used
2825 to write empty elements, followed by writeAttributes().
2826
2827 Element content consists of either characters, entity references or
2828 nested elements. It is written with writeCharacters(), which also
2829 takes care of escaping all forbidden characters and character
2830 sequences, writeEntityReference(), or subsequent calls to
2831 writeStartElement(). A convenience method writeTextElement() can be
2832 used for writing terminal elements that contain nothing but text.
2833
2834 The following abridged code snippet shows the basic use of the class
2835 to write formatted XML with indentation:
2836
2837 \snippet qxmlstreamwriter/main.cpp start stream
2838 \dots
2839 \snippet qxmlstreamwriter/main.cpp write element
2840 \dots
2841 \snippet qxmlstreamwriter/main.cpp finish stream
2842
2843 QXmlStreamWriter takes care of prefixing namespaces, all you have to
2844 do is specify the \c namespaceUri when writing elements or
2845 attributes. If you must conform to certain prefixes, you can force
2846 the writer to use them by declaring the namespaces manually with
2847 either writeNamespace() or writeDefaultNamespace(). Alternatively,
2848 you can bypass the stream writer's namespace support and use
2849 overloaded methods that take a qualified name instead. The namespace
2850 \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the
2851 prefix \e xml.
2852
2853 The stream writer can automatically format the generated XML data by
2854 adding line-breaks and indentation to empty sections between
2855 elements, making the XML data more readable for humans and easier to
2856 work with for most source code management systems. The feature can
2857 be turned on with the \l autoFormatting property, and customized
2858 with the \l autoFormattingIndent property.
2859
2860 Other functions are writeCDATA(), writeComment(),
2861 writeProcessingInstruction(), and writeDTD(). Chaining of XML
2862 streams is supported with writeCurrentToken().
2863
2864 QXmlStreamWriter always encodes XML in UTF-8.
2865
2866 If an error occurs while writing to the underlying device, hasError()
2867 starts returning true and subsequent writes are ignored.
2868
2869 The \l{QXmlStream Bookmarks Example} illustrates how to use a
2870 stream writer to write an XML bookmark file (XBEL) that
2871 was previously read in by a QXmlStreamReader.
2872
2873*/
2874
2875#if QT_CONFIG(xmlstreamwriter)
2876
2877class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
2878{
2879 QXmlStreamWriter *q_ptr;
2880 Q_DECLARE_PUBLIC(QXmlStreamWriter)
2881public:
2882 QXmlStreamWriterPrivate(QXmlStreamWriter *q);
2883 ~QXmlStreamWriterPrivate() {
2884 if (deleteDevice)
2885 delete device;
2886 }
2887
2888 void write(QAnyStringView s);
2889 void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
2890 bool finishStartElement(bool contents = true);
2891 void writeStartElement(QAnyStringView namespaceUri, QAnyStringView name);
2892 QIODevice *device;
2893 QString *stringDevice;
2894 uint deleteDevice :1;
2895 uint inStartElement :1;
2896 uint inEmptyElement :1;
2897 uint lastWasStartElement :1;
2898 uint wroteSomething :1;
2899 uint hasIoError :1;
2900 uint hasEncodingError :1;
2901 uint autoFormatting :1;
2902 std::string autoFormattingIndent;
2903 NamespaceDeclaration emptyNamespace;
2904 qsizetype lastNamespaceDeclaration;
2905
2906 NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
2907 void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration);
2908
2909 int namespacePrefixCount;
2910
2911 void indent(int level);
2912private:
2913 void doWriteToDevice(QStringView s);
2914 void doWriteToDevice(QUtf8StringView s);
2915 void doWriteToDevice(QLatin1StringView s);
2916};
2917
2918
2919QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
2920 : autoFormattingIndent(4, ' ')
2921{
2922 q_ptr = q;
2923 device = nullptr;
2924 stringDevice = nullptr;
2925 deleteDevice = false;
2926 inStartElement = inEmptyElement = false;
2927 wroteSomething = false;
2928 hasIoError = false;
2929 hasEncodingError = false;
2930 lastWasStartElement = false;
2931 lastNamespaceDeclaration = 1;
2932 autoFormatting = false;
2933 namespacePrefixCount = 0;
2934}
2935
2936void QXmlStreamWriterPrivate::write(QAnyStringView s)
2937{
2938 if (device) {
2939 if (hasIoError)
2940 return;
2941
2942 s.visit(v: [&] (auto s) { doWriteToDevice(s); });
2943 } else if (stringDevice) {
2944 s.visit(v: [&] (auto s) { stringDevice->append(s); });
2945 } else {
2946 qWarning(msg: "QXmlStreamWriter: No device");
2947 }
2948}
2949
2950void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
2951{
2952 QString escaped;
2953 escaped.reserve(asize: s.size());
2954 s.visit(v: [&] (auto s) {
2955 using View = decltype(s);
2956
2957 auto it = s.begin();
2958 const auto end = s.end();
2959
2960 while (it != end) {
2961 QLatin1StringView replacement;
2962 auto mark = it;
2963
2964 while (it != end) {
2965 if (*it == u'<') {
2966 replacement = "&lt;"_L1;
2967 break;
2968 } else if (*it == u'>') {
2969 replacement = "&gt;"_L1;
2970 break;
2971 } else if (*it == u'&') {
2972 replacement = "&amp;"_L1;
2973 break;
2974 } else if (*it == u'\"') {
2975 replacement = "&quot;"_L1;
2976 break;
2977 } else if (*it == u'\t') {
2978 if (escapeWhitespace) {
2979 replacement = "&#9;"_L1;
2980 break;
2981 }
2982 } else if (*it == u'\n') {
2983 if (escapeWhitespace) {
2984 replacement = "&#10;"_L1;
2985 break;
2986 }
2987 } else if (*it == u'\v' || *it == u'\f') {
2988 hasEncodingError = true;
2989 break;
2990 } else if (*it == u'\r') {
2991 if (escapeWhitespace) {
2992 replacement = "&#13;"_L1;
2993 break;
2994 }
2995 } else if (*it <= u'\x1F' || *it >= u'\uFFFE') {
2996 hasEncodingError = true;
2997 break;
2998 }
2999 ++it;
3000 }
3001
3002 escaped.append(View{mark, it});
3003 escaped.append(s: replacement);
3004 if (it != end)
3005 ++it;
3006 }
3007 } );
3008
3009 write(s: escaped);
3010}
3011
3012void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) {
3013 if (namespaceDeclaration.prefix.isEmpty()) {
3014 write(s: " xmlns=\"");
3015 write(s: namespaceDeclaration.namespaceUri);
3016 write(s: "\"");
3017 } else {
3018 write(s: " xmlns:");
3019 write(s: namespaceDeclaration.prefix);
3020 write(s: "=\"");
3021 write(s: namespaceDeclaration.namespaceUri);
3022 write(s: "\"");
3023 }
3024}
3025
3026bool QXmlStreamWriterPrivate::finishStartElement(bool contents)
3027{
3028 bool hadSomethingWritten = wroteSomething;
3029 wroteSomething = contents;
3030 if (!inStartElement)
3031 return hadSomethingWritten;
3032
3033 if (inEmptyElement) {
3034 write(s: "/>");
3035 QXmlStreamWriterPrivate::Tag tag = tagStack_pop();
3036 lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3037 lastWasStartElement = false;
3038 } else {
3039 write(s: ">");
3040 }
3041 inStartElement = inEmptyElement = false;
3042 lastNamespaceDeclaration = namespaceDeclarations.size();
3043 return hadSomethingWritten;
3044}
3045
3046QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(QAnyStringView namespaceUri, bool writeDeclaration, bool noDefault)
3047{
3048 for (NamespaceDeclaration &namespaceDeclaration : reversed(r&: namespaceDeclarations)) {
3049 if (namespaceDeclaration.namespaceUri == namespaceUri) {
3050 if (!noDefault || !namespaceDeclaration.prefix.isEmpty())
3051 return namespaceDeclaration;
3052 }
3053 }
3054 if (namespaceUri.isEmpty())
3055 return emptyNamespace;
3056 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
3057 if (namespaceUri.isEmpty()) {
3058 namespaceDeclaration.prefix.clear();
3059 } else {
3060 QString s;
3061 int n = ++namespacePrefixCount;
3062 forever {
3063 s = u'n' + QString::number(n++);
3064 qsizetype j = namespaceDeclarations.size() - 2;
3065 while (j >= 0 && namespaceDeclarations.at(index: j).prefix != s)
3066 --j;
3067 if (j < 0)
3068 break;
3069 }
3070 namespaceDeclaration.prefix = addToStringStorage(s);
3071 }
3072 namespaceDeclaration.namespaceUri = addToStringStorage(s: namespaceUri);
3073 if (writeDeclaration)
3074 writeNamespaceDeclaration(namespaceDeclaration);
3075 return namespaceDeclaration;
3076}
3077
3078
3079
3080void QXmlStreamWriterPrivate::indent(int level)
3081{
3082 write(s: "\n");
3083 for (int i = 0; i < level; ++i)
3084 write(s: autoFormattingIndent);
3085}
3086
3087void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
3088{
3089 constexpr qsizetype MaxChunkSize = 512;
3090 char buffer [3 * MaxChunkSize];
3091 QStringEncoder::State state;
3092 while (!s.isEmpty()) {
3093 const qsizetype chunkSize = std::min(a: s.size(), b: MaxChunkSize);
3094 char *end = QUtf8::convertFromUnicode(out: buffer, in: s.first(n: chunkSize), state: &state);
3095 doWriteToDevice(s: QUtf8StringView{buffer, end});
3096 s = s.sliced(pos: chunkSize);
3097 }
3098 if (state.remainingChars > 0)
3099 hasEncodingError = true;
3100}
3101
3102void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
3103{
3104 QByteArrayView bytes = s;
3105 if (device->write(data: bytes.data(), len: bytes.size()) != bytes.size())
3106 hasIoError = true;
3107}
3108
3109void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
3110{
3111 constexpr qsizetype MaxChunkSize = 512;
3112 char buffer [2 * MaxChunkSize];
3113 while (!s.isEmpty()) {
3114 const qsizetype chunkSize = std::min(a: s.size(), b: MaxChunkSize);
3115 char *end = QUtf8::convertFromLatin1(out: buffer, in: s.first(n: chunkSize));
3116 doWriteToDevice(s: QUtf8StringView{buffer, end});
3117 s = s.sliced(pos: chunkSize);
3118 }
3119}
3120
3121/*!
3122 Constructs a stream writer.
3123
3124 \sa setDevice()
3125 */
3126QXmlStreamWriter::QXmlStreamWriter()
3127 : d_ptr(new QXmlStreamWriterPrivate(this))
3128{
3129}
3130
3131/*!
3132 Constructs a stream writer that writes into \a device;
3133 */
3134QXmlStreamWriter::QXmlStreamWriter(QIODevice *device)
3135 : d_ptr(new QXmlStreamWriterPrivate(this))
3136{
3137 Q_D(QXmlStreamWriter);
3138 d->device = device;
3139}
3140
3141/*! Constructs a stream writer that writes into \a array. This is the
3142 same as creating an xml writer that operates on a QBuffer device
3143 which in turn operates on \a array.
3144 */
3145QXmlStreamWriter::QXmlStreamWriter(QByteArray *array)
3146 : d_ptr(new QXmlStreamWriterPrivate(this))
3147{
3148 Q_D(QXmlStreamWriter);
3149 d->device = new QBuffer(array);
3150 d->device->open(mode: QIODevice::WriteOnly);
3151 d->deleteDevice = true;
3152}
3153
3154
3155/*! Constructs a stream writer that writes into \a string.
3156 *
3157 */
3158QXmlStreamWriter::QXmlStreamWriter(QString *string)
3159 : d_ptr(new QXmlStreamWriterPrivate(this))
3160{
3161 Q_D(QXmlStreamWriter);
3162 d->stringDevice = string;
3163}
3164
3165/*!
3166 Destructor.
3167*/
3168QXmlStreamWriter::~QXmlStreamWriter()
3169{
3170}
3171
3172
3173/*!
3174 Sets the current device to \a device. If you want the stream to
3175 write into a QByteArray, you can create a QBuffer device.
3176
3177 \sa device()
3178*/
3179void QXmlStreamWriter::setDevice(QIODevice *device)
3180{
3181 Q_D(QXmlStreamWriter);
3182 if (device == d->device)
3183 return;
3184 d->stringDevice = nullptr;
3185 if (d->deleteDevice) {
3186 delete d->device;
3187 d->deleteDevice = false;
3188 }
3189 d->device = device;
3190}
3191
3192/*!
3193 Returns the current device associated with the QXmlStreamWriter,
3194 or \nullptr if no device has been assigned.
3195
3196 \sa setDevice()
3197*/
3198QIODevice *QXmlStreamWriter::device() const
3199{
3200 Q_D(const QXmlStreamWriter);
3201 return d->device;
3202}
3203
3204/*!
3205 \property QXmlStreamWriter::autoFormatting
3206 \since 4.4
3207 \brief the auto-formatting flag of the stream writer.
3208
3209 This property controls whether or not the stream writer
3210 automatically formats the generated XML data. If enabled, the
3211 writer automatically adds line-breaks and indentation to empty
3212 sections between elements (ignorable whitespace). The main purpose
3213 of auto-formatting is to split the data into several lines, and to
3214 increase readability for a human reader. The indentation depth can
3215 be controlled through the \l autoFormattingIndent property.
3216
3217 By default, auto-formatting is disabled.
3218*/
3219
3220/*!
3221 \since 4.4
3222
3223 Enables auto formatting if \a enable is \c true, otherwise
3224 disables it.
3225
3226 The default value is \c false.
3227 */
3228void QXmlStreamWriter::setAutoFormatting(bool enable)
3229{
3230 Q_D(QXmlStreamWriter);
3231 d->autoFormatting = enable;
3232}
3233
3234/*!
3235 \since 4.4
3236
3237 Returns \c true if auto formatting is enabled, otherwise \c false.
3238 */
3239bool QXmlStreamWriter::autoFormatting() const
3240{
3241 Q_D(const QXmlStreamWriter);
3242 return d->autoFormatting;
3243}
3244
3245/*!
3246 \property QXmlStreamWriter::autoFormattingIndent
3247 \since 4.4
3248
3249 \brief the number of spaces or tabs used for indentation when
3250 auto-formatting is enabled. Positive numbers indicate spaces,
3251 negative numbers tabs.
3252
3253 The default indentation is 4.
3254
3255 \sa autoFormatting
3256*/
3257
3258
3259void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs)
3260{
3261 Q_D(QXmlStreamWriter);
3262 d->autoFormattingIndent.assign(n: size_t(qAbs(t: spacesOrTabs)), c: spacesOrTabs >= 0 ? ' ' : '\t');
3263}
3264
3265int QXmlStreamWriter::autoFormattingIndent() const
3266{
3267 Q_D(const QXmlStreamWriter);
3268 const QLatin1StringView indent(d->autoFormattingIndent);
3269 return indent.count(ch: u' ') - indent.count(ch: u'\t');
3270}
3271
3272/*!
3273 Returns \c true if writing failed.
3274
3275 This can happen if the stream failed to write to the underlying
3276 device or if the data to be written contained invalid characters.
3277
3278 The error status is never reset. Writes happening after the error
3279 occurred may be ignored, even if the error condition is cleared.
3280 */
3281bool QXmlStreamWriter::hasError() const
3282{
3283 Q_D(const QXmlStreamWriter);
3284 return d->hasIoError || d->hasEncodingError;
3285}
3286
3287/*!
3288 \overload
3289 Writes an attribute with \a qualifiedName and \a value.
3290
3291
3292 This function can only be called after writeStartElement() before
3293 any content is written, or after writeEmptyElement().
3294
3295 \note In Qt versions prior to 6.5, this function took QString, not
3296 QAnyStringView.
3297 */
3298void QXmlStreamWriter::writeAttribute(QAnyStringView qualifiedName, QAnyStringView value)
3299{
3300 Q_D(QXmlStreamWriter);
3301 Q_ASSERT(d->inStartElement);
3302 Q_ASSERT(count(qualifiedName, ':') <= 1);
3303 d->write(s: " ");
3304 d->write(s: qualifiedName);
3305 d->write(s: "=\"");
3306 d->writeEscaped(s: value, escapeWhitespace: true);
3307 d->write(s: "\"");
3308}
3309
3310/*! Writes an attribute with \a name and \a value, prefixed for
3311 the specified \a namespaceUri. If the namespace has not been
3312 declared yet, QXmlStreamWriter will generate a namespace declaration
3313 for it.
3314
3315 This function can only be called after writeStartElement() before
3316 any content is written, or after writeEmptyElement().
3317
3318 \note In Qt versions prior to 6.5, this function took QString, not
3319 QAnyStringView.
3320 */
3321void QXmlStreamWriter::writeAttribute(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView value)
3322{
3323 Q_D(QXmlStreamWriter);
3324 Q_ASSERT(d->inStartElement);
3325 Q_ASSERT(!contains(name, ':'));
3326 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, writeDeclaration: true, noDefault: true);
3327 d->write(s: " ");
3328 if (!namespaceDeclaration.prefix.isEmpty()) {
3329 d->write(s: namespaceDeclaration.prefix);
3330 d->write(s: ":");
3331 }
3332 d->write(s: name);
3333 d->write(s: "=\"");
3334 d->writeEscaped(s: value, escapeWhitespace: true);
3335 d->write(s: "\"");
3336}
3337
3338/*!
3339 \overload
3340
3341 Writes the \a attribute.
3342
3343 This function can only be called after writeStartElement() before
3344 any content is written, or after writeEmptyElement().
3345 */
3346void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute)
3347{
3348 if (attribute.namespaceUri().isEmpty())
3349 writeAttribute(qualifiedName: attribute.qualifiedName(), value: attribute.value());
3350 else
3351 writeAttribute(namespaceUri: attribute.namespaceUri(), name: attribute.name(), value: attribute.value());
3352}
3353
3354
3355/*! Writes the attribute vector \a attributes. If a namespace
3356 referenced in an attribute not been declared yet, QXmlStreamWriter
3357 will generate a namespace declaration for it.
3358
3359 This function can only be called after writeStartElement() before
3360 any content is written, or after writeEmptyElement().
3361
3362 \sa writeAttribute(), writeNamespace()
3363 */
3364void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
3365{
3366 Q_D(QXmlStreamWriter);
3367 Q_ASSERT(d->inStartElement);
3368 Q_UNUSED(d);
3369 for (const auto &attr : attributes)
3370 writeAttribute(attribute: attr);
3371}
3372
3373
3374/*! Writes \a text as CDATA section. If \a text contains the
3375 forbidden character sequence "]]>", it is split into different CDATA
3376 sections.
3377
3378 This function mainly exists for completeness. Normally you should
3379 not need use it, because writeCharacters() automatically escapes all
3380 non-content characters.
3381
3382 \note In Qt versions prior to 6.5, this function took QString, not
3383 QAnyStringView.
3384 */
3385void QXmlStreamWriter::writeCDATA(QAnyStringView text)
3386{
3387 Q_D(QXmlStreamWriter);
3388 d->finishStartElement();
3389 d->write(s: "<![CDATA[");
3390 while (!text.isEmpty()) {
3391 const auto idx = indexOf(s: text, needle: "]]>"_L1);
3392 if (idx < 0)
3393 break; // no forbidden sequence found
3394 d->write(s: text.first(n: idx));
3395 d->write(s: "]]" // text[idx, idx + 2)
3396 "]]><![CDATA[" // escape sequence to separate ]] and >
3397 ">"); // text[idx + 2, idx + 3)
3398 text = text.sliced(pos: idx + 3); // skip over "]]>"
3399 }
3400 d->write(s: text); // write remainder
3401 d->write(s: "]]>");
3402}
3403
3404
3405/*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity
3406 references "&lt;", "&amp;, and "&quot;". To avoid the forbidden sequence
3407 "]]>", ">" is also escaped as "&gt;".
3408
3409 \sa writeEntityReference()
3410
3411 \note In Qt versions prior to 6.5, this function took QString, not
3412 QAnyStringView.
3413 */
3414void QXmlStreamWriter::writeCharacters(QAnyStringView text)
3415{
3416 Q_D(QXmlStreamWriter);
3417 d->finishStartElement();
3418 d->writeEscaped(s: text);
3419}
3420
3421
3422/*! Writes \a text as XML comment, where \a text must not contain the
3423 forbidden sequence \c{--} or end with \c{-}. Note that XML does not
3424 provide any way to escape \c{-} in a comment.
3425
3426 \note In Qt versions prior to 6.5, this function took QString, not
3427 QAnyStringView.
3428 */
3429void QXmlStreamWriter::writeComment(QAnyStringView text)
3430{
3431 Q_D(QXmlStreamWriter);
3432 Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
3433 if (!d->finishStartElement(contents: false) && d->autoFormatting)
3434 d->indent(level: d->tagStack.size());
3435 d->write(s: "<!--");
3436 d->write(s: text);
3437 d->write(s: "-->");
3438 d->inStartElement = d->lastWasStartElement = false;
3439}
3440
3441
3442/*! Writes a DTD section. The \a dtd represents the entire
3443 doctypedecl production from the XML 1.0 specification.
3444
3445 \note In Qt versions prior to 6.5, this function took QString, not
3446 QAnyStringView.
3447 */
3448void QXmlStreamWriter::writeDTD(QAnyStringView dtd)
3449{
3450 Q_D(QXmlStreamWriter);
3451 d->finishStartElement();
3452 if (d->autoFormatting)
3453 d->write(s: "\n");
3454 d->write(s: dtd);
3455 if (d->autoFormatting)
3456 d->write(s: "\n");
3457}
3458
3459
3460
3461/*! \overload
3462 Writes an empty element with qualified name \a qualifiedName.
3463 Subsequent calls to writeAttribute() will add attributes to this element.
3464
3465 \note In Qt versions prior to 6.5, this function took QString, not
3466 QAnyStringView.
3467*/
3468void QXmlStreamWriter::writeEmptyElement(QAnyStringView qualifiedName)
3469{
3470 Q_D(QXmlStreamWriter);
3471 Q_ASSERT(count(qualifiedName, ':') <= 1);
3472 d->writeStartElement(namespaceUri: {}, name: qualifiedName);
3473 d->inEmptyElement = true;
3474}
3475
3476
3477/*! Writes an empty element with \a name, prefixed for the specified
3478 \a namespaceUri. If the namespace has not been declared,
3479 QXmlStreamWriter will generate a namespace declaration for it.
3480 Subsequent calls to writeAttribute() will add attributes to this element.
3481
3482 \sa writeNamespace()
3483
3484 \note In Qt versions prior to 6.5, this function took QString, not
3485 QAnyStringView.
3486 */
3487void QXmlStreamWriter::writeEmptyElement(QAnyStringView namespaceUri, QAnyStringView name)
3488{
3489 Q_D(QXmlStreamWriter);
3490 Q_ASSERT(!contains(name, ':'));
3491 d->writeStartElement(namespaceUri, name);
3492 d->inEmptyElement = true;
3493}
3494
3495
3496/*!\overload
3497 Writes a text element with \a qualifiedName and \a text.
3498
3499
3500 This is a convenience function equivalent to:
3501 \snippet code/src_corelib_xml_qxmlstream.cpp 1
3502
3503 \note In Qt versions prior to 6.5, this function took QString, not
3504 QAnyStringView.
3505*/
3506void QXmlStreamWriter::writeTextElement(QAnyStringView qualifiedName, QAnyStringView text)
3507{
3508 writeStartElement(qualifiedName);
3509 writeCharacters(text);
3510 writeEndElement();
3511}
3512
3513/*! Writes a text element with \a name, prefixed for the specified \a
3514 namespaceUri, and \a text. If the namespace has not been
3515 declared, QXmlStreamWriter will generate a namespace declaration
3516 for it.
3517
3518
3519 This is a convenience function equivalent to:
3520 \snippet code/src_corelib_xml_qxmlstream.cpp 2
3521
3522 \note In Qt versions prior to 6.5, this function took QString, not
3523 QAnyStringView.
3524*/
3525void QXmlStreamWriter::writeTextElement(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView text)
3526{
3527 writeStartElement(namespaceUri, name);
3528 writeCharacters(text);
3529 writeEndElement();
3530}
3531
3532
3533/*!
3534 Closes all remaining open start elements and writes a newline.
3535
3536 \sa writeStartDocument()
3537 */
3538void QXmlStreamWriter::writeEndDocument()
3539{
3540 Q_D(QXmlStreamWriter);
3541 while (d->tagStack.size())
3542 writeEndElement();
3543 d->write(s: "\n");
3544}
3545
3546/*!
3547 Closes the previous start element.
3548
3549 \sa writeStartElement()
3550 */
3551void QXmlStreamWriter::writeEndElement()
3552{
3553 Q_D(QXmlStreamWriter);
3554 if (d->tagStack.isEmpty())
3555 return;
3556
3557 // shortcut: if nothing was written, close as empty tag
3558 if (d->inStartElement && !d->inEmptyElement) {
3559 d->write(s: "/>");
3560 d->lastWasStartElement = d->inStartElement = false;
3561 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
3562 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3563 return;
3564 }
3565
3566 if (!d->finishStartElement(contents: false) && !d->lastWasStartElement && d->autoFormatting)
3567 d->indent(level: d->tagStack.size()-1);
3568 if (d->tagStack.isEmpty())
3569 return;
3570 d->lastWasStartElement = false;
3571 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
3572 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3573 d->write(s: "</");
3574 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
3575 d->write(s: tag.namespaceDeclaration.prefix);
3576 d->write(s: ":");
3577 }
3578 d->write(s: tag.name);
3579 d->write(s: ">");
3580}
3581
3582
3583
3584/*!
3585 Writes the entity reference \a name to the stream, as "&\a{name};".
3586
3587 \note In Qt versions prior to 6.5, this function took QString, not
3588 QAnyStringView.
3589 */
3590void QXmlStreamWriter::writeEntityReference(QAnyStringView name)
3591{
3592 Q_D(QXmlStreamWriter);
3593 d->finishStartElement();
3594 d->write(s: "&");
3595 d->write(s: name);
3596 d->write(s: ";");
3597}
3598
3599
3600/*! Writes a namespace declaration for \a namespaceUri with \a
3601 prefix. If \a prefix is empty, QXmlStreamWriter assigns a unique
3602 prefix consisting of the letter 'n' followed by a number.
3603
3604 If writeStartElement() or writeEmptyElement() was called, the
3605 declaration applies to the current element; otherwise it applies to
3606 the next child element.
3607
3608 Note that the prefix \e xml is both predefined and reserved for
3609 \e http://www.w3.org/XML/1998/namespace, which in turn cannot be
3610 bound to any other prefix. The prefix \e xmlns and its URI
3611 \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism
3612 itself and thus completely forbidden in declarations.
3613
3614 \note In Qt versions prior to 6.5, this function took QString, not
3615 QAnyStringView.
3616 */
3617void QXmlStreamWriter::writeNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
3618{
3619 Q_D(QXmlStreamWriter);
3620 Q_ASSERT(prefix != "xmlns"_L1);
3621 if (prefix.isEmpty()) {
3622 d->findNamespace(namespaceUri, writeDeclaration: d->inStartElement);
3623 } else {
3624 Q_ASSERT(!((prefix == "xml"_L1) ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1)));
3625 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
3626 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
3627 namespaceDeclaration.prefix = d->addToStringStorage(s: prefix);
3628 namespaceDeclaration.namespaceUri = d->addToStringStorage(s: namespaceUri);
3629 if (d->inStartElement)
3630 d->writeNamespaceDeclaration(namespaceDeclaration);
3631 }
3632}
3633
3634
3635/*! Writes a default namespace declaration for \a namespaceUri.
3636
3637 If writeStartElement() or writeEmptyElement() was called, the
3638 declaration applies to the current element; otherwise it applies to
3639 the next child element.
3640
3641 Note that the namespaces \e http://www.w3.org/XML/1998/namespace
3642 (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to
3643 \e xml) by definition cannot be declared as default.
3644
3645 \note In Qt versions prior to 6.5, this function took QString, not
3646 QAnyStringView.
3647 */
3648void QXmlStreamWriter::writeDefaultNamespace(QAnyStringView namespaceUri)
3649{
3650 Q_D(QXmlStreamWriter);
3651 Q_ASSERT(namespaceUri != "http://www.w3.org/XML/1998/namespace"_L1);
3652 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
3653 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
3654 namespaceDeclaration.prefix.clear();
3655 namespaceDeclaration.namespaceUri = d->addToStringStorage(s: namespaceUri);
3656 if (d->inStartElement)
3657 d->writeNamespaceDeclaration(namespaceDeclaration);
3658}
3659
3660
3661/*!
3662 Writes an XML processing instruction with \a target and \a data,
3663 where \a data must not contain the sequence "?>".
3664
3665 \note In Qt versions prior to 6.5, this function took QString, not
3666 QAnyStringView.
3667 */
3668void QXmlStreamWriter::writeProcessingInstruction(QAnyStringView target, QAnyStringView data)
3669{
3670 Q_D(QXmlStreamWriter);
3671 Q_ASSERT(!contains(data, "?>"_L1));
3672 if (!d->finishStartElement(contents: false) && d->autoFormatting)
3673 d->indent(level: d->tagStack.size());
3674 d->write(s: "<?");
3675 d->write(s: target);
3676 if (!data.isNull()) {
3677 d->write(s: " ");
3678 d->write(s: data);
3679 }
3680 d->write(s: "?>");
3681}
3682
3683
3684
3685/*!\overload
3686
3687 Writes a document start with XML version number "1.0".
3688
3689 \sa writeEndDocument()
3690 \since 4.5
3691 */
3692void QXmlStreamWriter::writeStartDocument()
3693{
3694 writeStartDocument(version: "1.0"_L1);
3695}
3696
3697
3698/*!
3699 Writes a document start with the XML version number \a version.
3700
3701 \sa writeEndDocument()
3702
3703 \note In Qt versions prior to 6.5, this function took QString, not
3704 QAnyStringView.
3705 */
3706void QXmlStreamWriter::writeStartDocument(QAnyStringView version)
3707{
3708 Q_D(QXmlStreamWriter);
3709 d->finishStartElement(contents: false);
3710 d->write(s: "<?xml version=\"");
3711 d->write(s: version);
3712 if (d->device) // stringDevice does not get any encoding
3713 d->write(s: "\" encoding=\"UTF-8");
3714 d->write(s: "\"?>");
3715}
3716
3717/*! Writes a document start with the XML version number \a version
3718 and a standalone attribute \a standalone.
3719
3720 \sa writeEndDocument()
3721 \since 4.5
3722
3723 \note In Qt versions prior to 6.5, this function took QString, not
3724 QAnyStringView.
3725 */
3726void QXmlStreamWriter::writeStartDocument(QAnyStringView version, bool standalone)
3727{
3728 Q_D(QXmlStreamWriter);
3729 d->finishStartElement(contents: false);
3730 d->write(s: "<?xml version=\"");
3731 d->write(s: version);
3732 if (d->device) // stringDevice does not get any encoding
3733 d->write(s: "\" encoding=\"UTF-8");
3734 if (standalone)
3735 d->write(s: "\" standalone=\"yes\"?>");
3736 else
3737 d->write(s: "\" standalone=\"no\"?>");
3738}
3739
3740
3741/*!\overload
3742
3743 Writes a start element with \a qualifiedName. Subsequent calls to
3744 writeAttribute() will add attributes to this element.
3745
3746 \sa writeEndElement(), writeEmptyElement()
3747
3748 \note In Qt versions prior to 6.5, this function took QString, not
3749 QAnyStringView.
3750 */
3751void QXmlStreamWriter::writeStartElement(QAnyStringView qualifiedName)
3752{
3753 Q_D(QXmlStreamWriter);
3754 Q_ASSERT(count(qualifiedName, ':') <= 1);
3755 d->writeStartElement(namespaceUri: {}, name: qualifiedName);
3756}
3757
3758
3759/*! Writes a start element with \a name, prefixed for the specified
3760 \a namespaceUri. If the namespace has not been declared yet,
3761 QXmlStreamWriter will generate a namespace declaration for
3762 it. Subsequent calls to writeAttribute() will add attributes to this
3763 element.
3764
3765 \sa writeNamespace(), writeEndElement(), writeEmptyElement()
3766
3767 \note In Qt versions prior to 6.5, this function took QString, not
3768 QAnyStringView.
3769 */
3770void QXmlStreamWriter::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
3771{
3772 Q_D(QXmlStreamWriter);
3773 Q_ASSERT(!contains(name, ':'));
3774 d->writeStartElement(namespaceUri, name);
3775}
3776
3777void QXmlStreamWriterPrivate::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
3778{
3779 if (!finishStartElement(contents: false) && autoFormatting)
3780 indent(level: tagStack.size());
3781
3782 Tag &tag = tagStack_push();
3783 tag.name = addToStringStorage(s: name);
3784 tag.namespaceDeclaration = findNamespace(namespaceUri);
3785 write(s: "<");
3786 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
3787 write(s: tag.namespaceDeclaration.prefix);
3788 write(s: ":");
3789 }
3790 write(s: tag.name);
3791 inStartElement = lastWasStartElement = true;
3792
3793 for (qsizetype i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i)
3794 writeNamespaceDeclaration(namespaceDeclaration: namespaceDeclarations[i]);
3795 tag.namespaceDeclarationsSize = lastNamespaceDeclaration;
3796}
3797
3798#if QT_CONFIG(xmlstreamreader)
3799/*! Writes the current state of the \a reader. All possible valid
3800 states are supported.
3801
3802 The purpose of this function is to support chained processing of XML data.
3803
3804 \sa QXmlStreamReader::tokenType()
3805 */
3806void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
3807{
3808 switch (reader.tokenType()) {
3809 case QXmlStreamReader::NoToken:
3810 break;
3811 case QXmlStreamReader::StartDocument:
3812 writeStartDocument();
3813 break;
3814 case QXmlStreamReader::EndDocument:
3815 writeEndDocument();
3816 break;
3817 case QXmlStreamReader::StartElement: {
3818 writeStartElement(namespaceUri: reader.namespaceUri(), name: reader.name());
3819 const QXmlStreamNamespaceDeclarations decls = reader.namespaceDeclarations();
3820 for (const auto &namespaceDeclaration : decls) {
3821 writeNamespace(namespaceUri: namespaceDeclaration.namespaceUri(),
3822 prefix: namespaceDeclaration.prefix());
3823 }
3824 writeAttributes(attributes: reader.attributes());
3825 } break;
3826 case QXmlStreamReader::EndElement:
3827 writeEndElement();
3828 break;
3829 case QXmlStreamReader::Characters:
3830 if (reader.isCDATA())
3831 writeCDATA(text: reader.text());
3832 else
3833 writeCharacters(text: reader.text());
3834 break;
3835 case QXmlStreamReader::Comment:
3836 writeComment(text: reader.text());
3837 break;
3838 case QXmlStreamReader::DTD:
3839 writeDTD(dtd: reader.text());
3840 break;
3841 case QXmlStreamReader::EntityReference:
3842 writeEntityReference(name: reader.name());
3843 break;
3844 case QXmlStreamReader::ProcessingInstruction:
3845 writeProcessingInstruction(target: reader.processingInstructionTarget(),
3846 data: reader.processingInstructionData());
3847 break;
3848 default:
3849 Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid);
3850 qWarning(msg: "QXmlStreamWriter: writeCurrentToken() with invalid state.");
3851 break;
3852 }
3853}
3854
3855static constexpr bool isTokenAllowedInContext(QXmlStreamReader::TokenType type,
3856 QXmlStreamReaderPrivate::XmlContext ctxt)
3857{
3858 switch (type) {
3859 case QXmlStreamReader::StartDocument:
3860 case QXmlStreamReader::DTD:
3861 return ctxt == QXmlStreamReaderPrivate::XmlContext::Prolog;
3862
3863 case QXmlStreamReader::StartElement:
3864 case QXmlStreamReader::EndElement:
3865 case QXmlStreamReader::Characters:
3866 case QXmlStreamReader::EntityReference:
3867 case QXmlStreamReader::EndDocument:
3868 return ctxt == QXmlStreamReaderPrivate::XmlContext::Body;
3869
3870 case QXmlStreamReader::Comment:
3871 case QXmlStreamReader::ProcessingInstruction:
3872 return true;
3873
3874 case QXmlStreamReader::NoToken:
3875 case QXmlStreamReader::Invalid:
3876 return false;
3877 }
3878
3879 // GCC 8.x does not treat __builtin_unreachable() as constexpr
3880#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
3881 Q_UNREACHABLE_RETURN(false);
3882#else
3883 return false;
3884#endif
3885}
3886
3887/*!
3888 \internal
3889 \brief QXmlStreamReader::isValidToken
3890 \return \c true if \param type is a valid token type.
3891 \return \c false if \param type is an unexpected token,
3892 which indicates a non-well-formed or invalid XML stream.
3893 */
3894bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type)
3895{
3896 // Don't change currentContext, if Invalid or NoToken occur in the prolog
3897 if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken)
3898 return false;
3899
3900 // If a token type gets rejected in the body, there is no recovery
3901 const bool result = isTokenAllowedInContext(type, ctxt: currentContext);
3902 if (result || currentContext == XmlContext::Body)
3903 return result;
3904
3905 // First non-Prolog token observed => switch context to body and check again.
3906 currentContext = XmlContext::Body;
3907 return isTokenAllowedInContext(type, ctxt: currentContext);
3908}
3909
3910/*!
3911 \internal
3912 Checks token type and raises an error, if it is invalid
3913 in the current context (prolog/body).
3914 */
3915void QXmlStreamReaderPrivate::checkToken()
3916{
3917 Q_Q(QXmlStreamReader);
3918
3919 // The token type must be consumed, to keep track if the body has been reached.
3920 const XmlContext context = currentContext;
3921 const bool ok = isValidToken(type);
3922
3923 // Do nothing if an error has been raised already (going along with an unexpected token)
3924 if (error != QXmlStreamReader::Error::NoError)
3925 return;
3926
3927 if (!ok) {
3928 raiseError(error: QXmlStreamReader::UnexpectedElementError,
3929 message: QXmlStream::tr(sourceText: "Unexpected token type %1 in %2.")
3930 .arg(args: q->tokenString(), args: contextString(ctxt: context)));
3931 return;
3932 }
3933
3934 if (type != QXmlStreamReader::DTD)
3935 return;
3936
3937 // Raise error on multiple DTD tokens
3938 if (foundDTD) {
3939 raiseError(error: QXmlStreamReader::UnexpectedElementError,
3940 message: QXmlStream::tr(sourceText: "Found second DTD token in %1.").arg(a: contextString(ctxt: context)));
3941 } else {
3942 foundDTD = true;
3943 }
3944}
3945
3946/*!
3947 \fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView qualifiedName) const
3948
3949 Returns \c true if this QXmlStreamAttributes has an attribute whose
3950 qualified name is \a qualifiedName; otherwise returns \c false.
3951
3952 Note that this is not namespace aware. For instance, if this
3953 QXmlStreamAttributes contains an attribute whose lexical name is "xlink:href"
3954 this doesn't tell that an attribute named \c href in the XLink namespace is
3955 present, since the \c xlink prefix can be bound to any namespace. Use the
3956 overload that takes a namespace URI and a local name as parameter, for
3957 namespace aware code.
3958*/
3959
3960/*!
3961 \fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView namespaceUri,
3962 QAnyStringView name) const
3963 \overload
3964
3965 Returns \c true if this QXmlStreamAttributes has an attribute whose
3966 namespace URI and name correspond to \a namespaceUri and \a name;
3967 otherwise returns \c false.
3968*/
3969
3970#endif // feature xmlstreamreader
3971#endif // feature xmlstreamwriter
3972
3973QT_END_NAMESPACE
3974
3975#endif // feature xmlstream
3976

source code of qtbase/src/corelib/serialization/qxmlstream.cpp