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