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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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