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 tagStackStringStorageSize = 0;
969 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
970 namespaceDeclaration.namespaceUri = addToStringStorage(u"http://www.w3.org/XML/1998/namespace");
971 namespaceDeclaration.prefix = addToStringStorage(u"xml");
972 initialTagStackStringStorageSize = tagStackStringStorageSize;
973 tagsDone = false;
974}
975
976#if QT_CONFIG(xmlstreamreader)
977
978QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q)
979 :q_ptr(q)
980{
981 device = nullptr;
982 deleteDevice = false;
983 stack_size = 64;
984 sym_stack = nullptr;
985 state_stack = nullptr;
986 reallocateStack();
987 entityResolver = nullptr;
988 init();
989#define ADD_PREDEFINED(n, v)
990 do {
991 Entity e = Entity::createLiteral(n##_L1, v##_L1);
992 entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e));
993 } while (false)
994 ADD_PREDEFINED("lt", "<");
995 ADD_PREDEFINED("gt", ">");
996 ADD_PREDEFINED("amp", "&");
997 ADD_PREDEFINED("apos", "'");
998 ADD_PREDEFINED("quot", "\"");
999#undef ADD_PREDEFINED
1000}
1001
1002void QXmlStreamReaderPrivate::init()
1003{
1004 scanDtd = false;
1005 lastAttributeIsCData = false;
1006 token = -1;
1007 token_char = 0;
1008 isEmptyElement = false;
1009 isWhitespace = true;
1010 isCDATA = false;
1011 standalone = false;
1012 hasStandalone = false;
1013 tos = 0;
1014 resumeReduction = 0;
1015 state_stack[tos++] = 0;
1016 state_stack[tos] = 0;
1017 putStack.clear();
1018 putStack.reserve(32);
1019 textBuffer.clear();
1020 textBuffer.reserve(256);
1021 tagStack.clear();
1022 tagsDone = false;
1023 attributes.clear();
1024 attributes.reserve(16);
1025 lineNumber = lastLineStart = characterOffset = 0;
1026 readBufferPos = 0;
1027 nbytesread = 0;
1028 decoder = QStringDecoder();
1029 attributeStack.clear();
1030 attributeStack.reserve(16);
1031 entityParser.reset();
1032 hasCheckedStartDocument = false;
1033 normalizeLiterals = false;
1034 hasSeenTag = false;
1035 atEnd = false;
1036 inParseEntity = false;
1037 referenceToUnparsedEntityDetected = false;
1038 referenceToParameterEntityDetected = false;
1039 hasExternalDtdSubset = false;
1040 lockEncoding = false;
1041 namespaceProcessing = true;
1042 rawReadBuffer.clear();
1043 chunkDecoder = QStringDecoder();
1044 dataInfo.clear();
1045 readBuffer.clear();
1046 tagStackStringStorageSize = initialTagStackStringStorageSize;
1047
1048 type = QXmlStreamReader::NoToken;
1049 error = QXmlStreamReader::NoError;
1050 currentContext = XmlContext::Prolog;
1051 foundDTD = false;
1052}
1053
1054/*
1055 Well-formed requires that we verify entity values. We do this with a
1056 standard parser.
1057 */
1058void QXmlStreamReaderPrivate::parseEntity(const QString &value)
1059{
1060 Q_Q(QXmlStreamReader);
1061
1062 if (value.isEmpty())
1063 return;
1064
1065
1066 if (!entityParser)
1067 entityParser = std::make_unique<QXmlStreamReaderPrivate>(q);
1068 else
1069 entityParser->init();
1070 entityParser->inParseEntity = true;
1071 entityParser->readBuffer = value;
1072 entityParser->injectToken(PARSE_ENTITY);
1073 while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid)
1074 entityParser->parse();
1075 if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size())
1076 raiseWellFormedError(QXmlStream::tr("Invalid entity value."));
1077
1078}
1079
1080inline void QXmlStreamReaderPrivate::reallocateStack()
1081{
1082 stack_size <<= 1;
1083 void *p = realloc(sym_stack, stack_size * sizeof(Value));
1084 Q_CHECK_PTR(p);
1085 sym_stack = static_cast<Value*>(p);
1086 p = realloc(state_stack, stack_size * sizeof(int));
1087 Q_CHECK_PTR(p);
1088 state_stack = static_cast<int*>(p);
1089}
1090
1091
1092QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate()
1093{
1094 free(sym_stack);
1095 free(state_stack);
1096}
1097
1098
1099inline uint QXmlStreamReaderPrivate::filterCarriageReturn()
1100{
1101 uint peekc = peekChar();
1102 if (peekc == '\n') {
1103 if (putStack.size())
1104 putStack.pop();
1105 else
1106 ++readBufferPos;
1107 return peekc;
1108 }
1109 if (peekc == StreamEOF) {
1110 putChar('\r');
1111 return 0;
1112 }
1113 return '\n';
1114}
1115
1116/*!
1117 \internal
1118 If the end of the file is encountered, ~0 is returned.
1119 */
1120inline uint QXmlStreamReaderPrivate::getChar()
1121{
1122 uint c;
1123 if (putStack.size()) {
1124 c = atEnd ? StreamEOF : putStack.pop();
1125 } else {
1126 if (readBufferPos < readBuffer.size())
1127 c = readBuffer.at(readBufferPos++).unicode();
1128 else
1129 c = getChar_helper();
1130 }
1131
1132 return c;
1133}
1134
1135inline uint QXmlStreamReaderPrivate::peekChar()
1136{
1137 uint c;
1138 if (putStack.size()) {
1139 c = putStack.top();
1140 } else if (readBufferPos < readBuffer.size()) {
1141 c = readBuffer.at(readBufferPos).unicode();
1142 } else {
1143 if ((c = getChar_helper()) != StreamEOF)
1144 --readBufferPos;
1145 }
1146
1147 return c;
1148}
1149
1150/*!
1151 \internal
1152
1153 Scans characters until \a str is encountered, and validates the characters
1154 as according to the Char[2] production and do the line-ending normalization.
1155 If any character is invalid, false is returned, otherwise true upon success.
1156
1157 If \a tokenToInject is not less than zero, injectToken() is called with
1158 \a tokenToInject when \a str is found.
1159
1160 If any error occurred, false is returned, otherwise true.
1161 */
1162bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject)
1163{
1164 const qsizetype pos = textBuffer.size();
1165 const auto oldLineNumber = lineNumber;
1166
1167 uint c;
1168 while ((c = getChar()) != StreamEOF) {
1169 /* First, we do the validation & normalization. */
1170 switch (c) {
1171 case '\r':
1172 if ((c = filterCarriageReturn()) == 0)
1173 break;
1174 Q_FALLTHROUGH();
1175 case '\n':
1176 ++lineNumber;
1177 lastLineStart = characterOffset + readBufferPos;
1178 Q_FALLTHROUGH();
1179 case '\t':
1180 textBuffer += QChar(c);
1181 continue;
1182 default:
1183 if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) {
1184 raiseWellFormedError(QXmlStream::tr("Invalid XML character."));
1185 lineNumber = oldLineNumber;
1186 return false;
1187 }
1188 textBuffer += QChar(c);
1189 }
1190
1191
1192 /* Second, attempt to lookup str. */
1193 if (c == uint(*str)) {
1194 if (!*(str + 1)) {
1195 if (tokenToInject >= 0)
1196 injectToken(tokenToInject);
1197 return true;
1198 } else {
1199 if (scanString(str + 1, tokenToInject, false))
1200 return true;
1201 }
1202 }
1203 }
1204 putString(textBuffer, pos);
1205 textBuffer.resize(pos);
1206 lineNumber = oldLineNumber;
1207 return false;
1208}
1209
1210bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace)
1211{
1212 qsizetype n = 0;
1213 while (str[n]) {
1214 uint c = getChar();
1215 if (c != ushort(str[n])) {
1216 if (c != StreamEOF)
1217 putChar(c);
1218 while (n--) {
1219 putChar(ushort(str[n]));
1220 }
1221 return false;
1222 }
1223 ++n;
1224 }
1225 textBuffer += QLatin1StringView(str, n);
1226 if (requireSpace) {
1227 const qsizetype s = fastScanSpace();
1228 if (!s || atEnd) {
1229 qsizetype pos = textBuffer.size() - n - s;
1230 putString(textBuffer, pos);
1231 textBuffer.resize(pos);
1232 return false;
1233 }
1234 }
1235 if (tokenToInject >= 0)
1236 injectToken(tokenToInject);
1237 return true;
1238}
1239
1240bool QXmlStreamReaderPrivate::scanAfterLangleBang()
1241{
1242 switch (peekChar()) {
1243 case '[':
1244 return scanString(spell[CDATA_START], CDATA_START, false);
1245 case 'D':
1246 return scanString(spell[DOCTYPE], DOCTYPE);
1247 case 'A':
1248 return scanString(spell[ATTLIST], ATTLIST);
1249 case 'N':
1250 return scanString(spell[NOTATION], NOTATION);
1251 case 'E':
1252 if (scanString(spell[ELEMENT], ELEMENT))
1253 return true;
1254 return scanString(spell[ENTITY], ENTITY);
1255
1256 default:
1257 ;
1258 };
1259 return false;
1260}
1261
1262bool QXmlStreamReaderPrivate::scanPublicOrSystem()
1263{
1264 switch (peekChar()) {
1265 case 'S':
1266 return scanString(spell[SYSTEM], SYSTEM);
1267 case 'P':
1268 return scanString(spell[PUBLIC], PUBLIC);
1269 default:
1270 ;
1271 }
1272 return false;
1273}
1274
1275bool QXmlStreamReaderPrivate::scanNData()
1276{
1277 if (fastScanSpace()) {
1278 if (scanString(spell[NDATA], NDATA))
1279 return true;
1280 putChar(' ');
1281 }
1282 return false;
1283}
1284
1285bool QXmlStreamReaderPrivate::scanAfterDefaultDecl()
1286{
1287 switch (peekChar()) {
1288 case 'R':
1289 return scanString(spell[REQUIRED], REQUIRED, false);
1290 case 'I':
1291 return scanString(spell[IMPLIED], IMPLIED, false);
1292 case 'F':
1293 return scanString(spell[FIXED], FIXED, false);
1294 default:
1295 ;
1296 }
1297 return false;
1298}
1299
1300bool QXmlStreamReaderPrivate::scanAttType()
1301{
1302 switch (peekChar()) {
1303 case 'C':
1304 return scanString(spell[CDATA], CDATA);
1305 case 'I':
1306 if (scanString(spell[ID], ID))
1307 return true;
1308 if (scanString(spell[IDREF], IDREF))
1309 return true;
1310 return scanString(spell[IDREFS], IDREFS);
1311 case 'E':
1312 if (scanString(spell[ENTITY], ENTITY))
1313 return true;
1314 return scanString(spell[ENTITIES], ENTITIES);
1315 case 'N':
1316 if (scanString(spell[NOTATION], NOTATION))
1317 return true;
1318 if (scanString(spell[NMTOKEN], NMTOKEN))
1319 return true;
1320 return scanString(spell[NMTOKENS], NMTOKENS);
1321 default:
1322 ;
1323 }
1324 return false;
1325}
1326
1327/*!
1328 \internal
1329
1330 Scan strings with quotes or apostrophes surround them. For instance,
1331 attributes, the version and encoding field in the XML prolog and
1332 entity declarations.
1333
1334 If normalizeLiterals is set to true, the function also normalizes
1335 whitespace. It is set to true when the first start tag is
1336 encountered.
1337
1338 */
1339inline qsizetype QXmlStreamReaderPrivate::fastScanLiteralContent()
1340{
1341 qsizetype n = 0;
1342 uint c;
1343 while ((c = getChar()) != StreamEOF) {
1344 switch (ushort(c)) {
1345 case 0xfffe:
1346 case 0xffff:
1347 case 0:
1348 /* The putChar() call is necessary so the parser re-gets
1349 * the character from the input source, when raising an error. */
1350 putChar(c);
1351 return n;
1352 case '\r':
1353 if (filterCarriageReturn() == 0)
1354 return n;
1355 Q_FALLTHROUGH();
1356 case '\n':
1357 ++lineNumber;
1358 lastLineStart = characterOffset + readBufferPos;
1359 Q_FALLTHROUGH();
1360 case ' ':
1361 case '\t':
1362 if (normalizeLiterals)
1363 textBuffer += u' ';
1364 else
1365 textBuffer += QChar(c);
1366 ++n;
1367 break;
1368 case '&':
1369 case '<':
1370 case '\"':
1371 case '\'':
1372 if (!(c & 0xff0000)) {
1373 putChar(c);
1374 return n;
1375 }
1376 Q_FALLTHROUGH();
1377 default:
1378 if (c < 0x20) {
1379 putChar(c);
1380 return n;
1381 }
1382 textBuffer += QChar(ushort(c));
1383 ++n;
1384 }
1385 }
1386 return n;
1387}
1388
1389inline qsizetype QXmlStreamReaderPrivate::fastScanSpace()
1390{
1391 qsizetype n = 0;
1392 uint c;
1393 while ((c = getChar()) != StreamEOF) {
1394 switch (c) {
1395 case '\r':
1396 if ((c = filterCarriageReturn()) == 0)
1397 return n;
1398 Q_FALLTHROUGH();
1399 case '\n':
1400 ++lineNumber;
1401 lastLineStart = characterOffset + readBufferPos;
1402 Q_FALLTHROUGH();
1403 case ' ':
1404 case '\t':
1405 textBuffer += QChar(c);
1406 ++n;
1407 break;
1408 default:
1409 putChar(c);
1410 return n;
1411 }
1412 }
1413 return n;
1414}
1415
1416/*!
1417 \internal
1418
1419 Used for text nodes essentially. That is, characters appearing
1420 inside elements.
1421 */
1422inline qsizetype QXmlStreamReaderPrivate::fastScanContentCharList()
1423{
1424 qsizetype n = 0;
1425 uint c;
1426 while ((c = getChar()) != StreamEOF) {
1427 switch (ushort(c)) {
1428 case 0xfffe:
1429 case 0xffff:
1430 case 0:
1431 putChar(c);
1432 return n;
1433 case ']': {
1434 isWhitespace = false;
1435 const qsizetype pos = textBuffer.size();
1436 textBuffer += QChar(ushort(c));
1437 ++n;
1438 while ((c = getChar()) == ']') {
1439 textBuffer += QChar(ushort(c));
1440 ++n;
1441 }
1442 if (c == StreamEOF) {
1443 putString(textBuffer, pos);
1444 textBuffer.resize(pos);
1445 } else if (c == '>' && textBuffer.at(textBuffer.size() - 2) == u']') {
1446 raiseWellFormedError(QXmlStream::tr("Sequence ']]>' not allowed in content."));
1447 } else {
1448 putChar(c);
1449 break;
1450 }
1451 return n;
1452 } break;
1453 case '\r':
1454 if ((c = filterCarriageReturn()) == 0)
1455 return n;
1456 Q_FALLTHROUGH();
1457 case '\n':
1458 ++lineNumber;
1459 lastLineStart = characterOffset + readBufferPos;
1460 Q_FALLTHROUGH();
1461 case ' ':
1462 case '\t':
1463 textBuffer += QChar(ushort(c));
1464 ++n;
1465 break;
1466 case '&':
1467 case '<':
1468 if (!(c & 0xff0000)) {
1469 putChar(c);
1470 return n;
1471 }
1472 Q_FALLTHROUGH();
1473 default:
1474 if (c < 0x20) {
1475 putChar(c);
1476 return n;
1477 }
1478 isWhitespace = false;
1479 textBuffer += QChar(ushort(c));
1480 ++n;
1481 }
1482 }
1483 return n;
1484}
1485
1486// Fast scan an XML attribute name (e.g. "xml:lang").
1487inline std::optional<qsizetype> QXmlStreamReaderPrivate::fastScanName(Value *val)
1488{
1489 qsizetype n = 0;
1490 uint c;
1491 while ((c = getChar()) != StreamEOF) {
1492 if (n >= 4096) {
1493 // This is too long to be a sensible name, and
1494 // can exhaust memory, or the range of decltype(*prefix)
1495 raiseNamePrefixTooLongError();
1496 return std::nullopt;
1497 }
1498 switch (c) {
1499 case '\n':
1500 case ' ':
1501 case '\t':
1502 case '\r':
1503 case '&':
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 putChar(c);
1525 if (val && val->prefix == n + 1) {
1526 val->prefix = 0;
1527 putChar(':');
1528 --n;
1529 }
1530 return n;
1531 case ':':
1532 if (val) {
1533 if (val->prefix == 0) {
1534 val->prefix = qint16(n + 2);
1535 } else { // only one colon allowed according to the namespace spec.
1536 putChar(c);
1537 return n;
1538 }
1539 } else {
1540 putChar(c);
1541 return n;
1542 }
1543 Q_FALLTHROUGH();
1544 default:
1545 textBuffer += QChar(ushort(c));
1546 ++n;
1547 }
1548 }
1549
1550 if (val)
1551 val->prefix = 0;
1552 qsizetype pos = textBuffer.size() - n;
1553 putString(textBuffer, pos);
1554 textBuffer.resize(pos);
1555 return 0;
1556}
1557
1558enum NameChar { NameBeginning, NameNotBeginning, NotName };
1559
1560static const char Begi = static_cast<char>(NameBeginning);
1561static const char NtBg = static_cast<char>(NameNotBeginning);
1562static const char NotN = static_cast<char>(NotName);
1563
1564static const char nameCharTable[128] =
1565{
1566// 0x00
1567 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1568 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1569// 0x10
1570 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1571 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1572// 0x20 (0x2D is '-', 0x2E is '.')
1573 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1574 NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN,
1575// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':')
1576 NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg,
1577 NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN,
1578// 0x40 (0x41..0x5A are 'A'..'Z')
1579 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1580 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1581// 0x50 (0x5F is '_')
1582 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1583 Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi,
1584// 0x60 (0x61..0x7A are 'a'..'z')
1585 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1586 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1587// 0x70
1588 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1589 Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN
1590};
1591
1592static inline NameChar fastDetermineNameChar(QChar ch)
1593{
1594 ushort uc = ch.unicode();
1595 if (!(uc & ~0x7f)) // uc < 128
1596 return static_cast<NameChar>(nameCharTable[uc]);
1597
1598 QChar::Category cat = ch.category();
1599 // ### some these categories might be slightly wrong
1600 if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other)
1601 || cat == QChar::Number_Letter)
1602 return NameBeginning;
1603 if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other)
1604 || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing))
1605 return NameNotBeginning;
1606 return NotName;
1607}
1608
1609inline qsizetype QXmlStreamReaderPrivate::fastScanNMTOKEN()
1610{
1611 qsizetype n = 0;
1612 uint c;
1613 while ((c = getChar()) != StreamEOF) {
1614 if (fastDetermineNameChar(QChar(c)) == NotName) {
1615 putChar(c);
1616 return n;
1617 } else {
1618 ++n;
1619 textBuffer += QChar(c);
1620 }
1621 }
1622
1623 qsizetype pos = textBuffer.size() - n;
1624 putString(textBuffer, pos);
1625 textBuffer.resize(pos);
1626
1627 return n;
1628}
1629
1630void QXmlStreamReaderPrivate::putString(QStringView s, qsizetype from)
1631{
1632 if (from != 0) {
1633 putString(s.mid(from));
1634 return;
1635 }
1636 putStack.reserve(s.size());
1637 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1638 putStack.rawPush() = it->unicode();
1639}
1640
1641void QXmlStreamReaderPrivate::putStringLiteral(QStringView s)
1642{
1643 putStack.reserve(s.size());
1644 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1645 putStack.rawPush() = ((LETTER << 16) | it->unicode());
1646}
1647
1648void QXmlStreamReaderPrivate::putReplacement(QStringView s)
1649{
1650 putStack.reserve(s.size());
1651 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1652 char16_t c = it->unicode();
1653 if (c == '\n' || c == '\r')
1654 putStack.rawPush() = ((LETTER << 16) | c);
1655 else
1656 putStack.rawPush() = c;
1657 }
1658}
1659void QXmlStreamReaderPrivate::putReplacementInAttributeValue(QStringView s)
1660{
1661 putStack.reserve(s.size());
1662 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1663 char16_t c = it->unicode();
1664 if (c == '&' || c == ';')
1665 putStack.rawPush() = c;
1666 else if (c == '\n' || c == '\r')
1667 putStack.rawPush() = ' ';
1668 else
1669 putStack.rawPush() = ((LETTER << 16) | c);
1670 }
1671}
1672
1673uint QXmlStreamReaderPrivate::getChar_helper()
1674{
1675 constexpr qsizetype BUFFER_SIZE = 8192;
1676 characterOffset += readBufferPos;
1677 readBufferPos = 0;
1678 if (readBuffer.size())
1679 readBuffer.resize(0);
1680 if (decoder.isValid())
1681 nbytesread = 0;
1682
1683 auto tryDecodeWithGlobalDecoder = [this]() -> bool {
1684 if (!decoder.isValid()) {
1685 // Need 4 bytes: three for BOM (EF BB BF) plus one for the UTF-8 codec
1686 if (nbytesread < 4) {
1687 atEnd = true;
1688 return false;
1689 }
1690 auto encoding = QStringDecoder::encodingForData(rawReadBuffer, u'<');
1691 if (!encoding) // assume utf-8
1692 encoding = QStringDecoder::Utf8;
1693 decoder = QStringDecoder(*encoding);
1694 }
1695
1696 readBuffer = decoder(QByteArrayView(rawReadBuffer).first(nbytesread));
1697
1698 if (lockEncoding && decoder.hasError()) {
1699 readBuffer.clear();
1700 return false;
1701 }
1702
1703 return true;
1704 };
1705
1706 if (device) {
1707 rawReadBuffer.resize(BUFFER_SIZE);
1708 qint64 nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread);
1709 nbytesread += qMax(nbytesreadOrMinus1, qint64{0});
1710
1711 if (!nbytesread) {
1712 atEnd = true;
1713 return StreamEOF;
1714 }
1715
1716 if (!tryDecodeWithGlobalDecoder())
1717 return StreamEOF;
1718 } else if (dataInfo.empty()) {
1719 atEnd = true;
1720 return StreamEOF;
1721 } else {
1722 const BufferAndEncoding bufAndEnc = dataInfo.takeFirst();
1723
1724 // Use global decoder if the encoding is not set explicitly.
1725 // Here we'll use rawReadBuffer to cache the data from the previous
1726 // chunk with unknown encoding. We need to do it because the size
1727 // of the previous chunk might be too small, and we need to wait
1728 // for more data before we can determine the encoding.
1729 if (bufAndEnc.encoding == QStringDecoder::System) {
1730 if (nbytesread)
1731 rawReadBuffer += bufAndEnc.buffer;
1732 else
1733 rawReadBuffer = bufAndEnc.buffer;
1734 nbytesread = rawReadBuffer.size();
1735
1736 if (!tryDecodeWithGlobalDecoder()) {
1737 // try decoding with the previous chunk decoder
1738 bool hasError = true;
1739 if (chunkDecoder.isValid() && !chunkDecoder.hasError()) {
1740 readBuffer = chunkDecoder(QByteArrayView(rawReadBuffer).first(nbytesread));
1741 hasError = chunkDecoder.hasError();
1742 }
1743 if (hasError) {
1744 raiseWellFormedError(
1745 QXmlStream::tr("Encountered incorrectly encoded content."));
1746 return StreamEOF;
1747 }
1748 }
1749 } else {
1750 if (!isDecoderForEncoding(chunkDecoder, bufAndEnc.encoding))
1751 chunkDecoder = QStringDecoder(bufAndEnc.encoding);
1752 readBuffer = chunkDecoder(bufAndEnc.buffer);
1753 }
1754 }
1755
1756 readBuffer.reserve(1); // keep capacity when calling resize() next time
1757
1758 if (readBufferPos < readBuffer.size()) {
1759 ushort c = readBuffer.at(readBufferPos++).unicode();
1760 return c;
1761 }
1762
1763 atEnd = true;
1764 return StreamEOF;
1765}
1766
1767XmlStringRef QXmlStreamReaderPrivate::namespaceForPrefix(QStringView prefix)
1768{
1769 for (const NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
1770 if (namespaceDeclaration.prefix == prefix) {
1771 return namespaceDeclaration.namespaceUri;
1772 }
1773 }
1774
1775#if 1
1776 if (namespaceProcessing && !prefix.isEmpty())
1777 raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix));
1778#endif
1779
1780 return XmlStringRef();
1781}
1782
1783/*
1784 uses namespaceForPrefix and builds the attribute vector
1785 */
1786void QXmlStreamReaderPrivate::resolveTag()
1787{
1788 const auto attributeStackCleaner = qScopeGuard([this](){ attributeStack.clear(); });
1789 const qsizetype n = attributeStack.size();
1790
1791 if (namespaceProcessing) {
1792 for (const DtdAttribute &dtdAttribute : dtdAttributes) {
1793 if (!dtdAttribute.isNamespaceAttribute
1794 || dtdAttribute.defaultValue.isNull()
1795 || dtdAttribute.tagName != qualifiedName
1796 || dtdAttribute.attributeQualifiedName.isNull())
1797 continue;
1798 qsizetype i = 0;
1799 while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1800 ++i;
1801 if (i != n)
1802 continue;
1803 if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == "xmlns"_L1) {
1804 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1805 namespaceDeclaration.prefix.clear();
1806
1807 const XmlStringRef ns(dtdAttribute.defaultValue);
1808 if (ns == "http://www.w3.org/2000/xmlns/"_L1 ||
1809 ns == "http://www.w3.org/XML/1998/namespace"_L1)
1810 raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
1811 else
1812 namespaceDeclaration.namespaceUri = ns;
1813 } else if (dtdAttribute.attributePrefix == "xmlns"_L1) {
1814 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1815 XmlStringRef namespacePrefix = dtdAttribute.attributeName;
1816 XmlStringRef namespaceUri = dtdAttribute.defaultValue;
1817 if (((namespacePrefix == "xml"_L1)
1818 ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1))
1819 || namespaceUri == "http://www.w3.org/2000/xmlns/"_L1
1820 || namespaceUri.isEmpty()
1821 || namespacePrefix == "xmlns"_L1)
1822 raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
1823
1824 namespaceDeclaration.prefix = namespacePrefix;
1825 namespaceDeclaration.namespaceUri = namespaceUri;
1826 }
1827 }
1828 }
1829
1830 tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix);
1831
1832 attributes.resize(n);
1833
1834 for (qsizetype i = 0; i < n; ++i) {
1835 QXmlStreamAttribute &attribute = attributes[i];
1836 Attribute &attrib = attributeStack[i];
1837 XmlStringRef prefix(symPrefix(attrib.key));
1838 XmlStringRef name(symString(attrib.key));
1839 XmlStringRef qualifiedName(symName(attrib.key));
1840 XmlStringRef value(symString(attrib.value));
1841
1842 attribute.m_name = name;
1843 attribute.m_qualifiedName = qualifiedName;
1844 attribute.m_value = value;
1845
1846 if (!prefix.isEmpty()) {
1847 XmlStringRef attributeNamespaceUri = namespaceForPrefix(prefix);
1848 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1849 }
1850
1851 for (qsizetype j = 0; j < i; ++j) {
1852 if (attributes[j].name() == attribute.name()
1853 && attributes[j].namespaceUri() == attribute.namespaceUri()
1854 && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName()))
1855 {
1856 raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName()));
1857 return;
1858 }
1859 }
1860 }
1861
1862 for (const DtdAttribute &dtdAttribute : dtdAttributes) {
1863 if (dtdAttribute.isNamespaceAttribute
1864 || dtdAttribute.defaultValue.isNull()
1865 || dtdAttribute.tagName != qualifiedName
1866 || dtdAttribute.attributeQualifiedName.isNull())
1867 continue;
1868 qsizetype i = 0;
1869 while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1870 ++i;
1871 if (i != n)
1872 continue;
1873
1874
1875
1876 QXmlStreamAttribute attribute;
1877 attribute.m_name = dtdAttribute.attributeName;
1878 attribute.m_qualifiedName = dtdAttribute.attributeQualifiedName;
1879 attribute.m_value = dtdAttribute.defaultValue;
1880
1881 if (!dtdAttribute.attributePrefix.isEmpty()) {
1882 XmlStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix);
1883 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1884 }
1885 attribute.m_isDefault = true;
1886 attributes.append(std::move(attribute));
1887 }
1888}
1889
1890void QXmlStreamReaderPrivate::resolvePublicNamespaces()
1891{
1892 const Tag &tag = tagStack.top();
1893 qsizetype n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize;
1894 publicNamespaceDeclarations.resize(n);
1895 for (qsizetype i = 0; i < n; ++i) {
1896 const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i);
1897 QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i];
1898 publicNamespaceDeclaration.m_prefix = namespaceDeclaration.prefix;
1899 publicNamespaceDeclaration.m_namespaceUri = namespaceDeclaration.namespaceUri;
1900 }
1901}
1902
1903void QXmlStreamReaderPrivate::resolveDtd()
1904{
1905 publicNotationDeclarations.resize(notationDeclarations.size());
1906 for (qsizetype i = 0; i < notationDeclarations.size(); ++i) {
1907 const QXmlStreamReaderPrivate::NotationDeclaration &notationDeclaration = notationDeclarations.at(i);
1908 QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i];
1909 publicNotationDeclaration.m_name = notationDeclaration.name;
1910 publicNotationDeclaration.m_systemId = notationDeclaration.systemId;
1911 publicNotationDeclaration.m_publicId = notationDeclaration.publicId;
1912
1913 }
1914 notationDeclarations.clear();
1915 publicEntityDeclarations.resize(entityDeclarations.size());
1916 for (qsizetype i = 0; i < entityDeclarations.size(); ++i) {
1917 const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i);
1918 QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i];
1919 publicEntityDeclaration.m_name = entityDeclaration.name;
1920 publicEntityDeclaration.m_notationName = entityDeclaration.notationName;
1921 publicEntityDeclaration.m_systemId = entityDeclaration.systemId;
1922 publicEntityDeclaration.m_publicId = entityDeclaration.publicId;
1923 publicEntityDeclaration.m_value = entityDeclaration.value;
1924 }
1925 entityDeclarations.clear();
1926 parameterEntityHash.clear();
1927}
1928
1929uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex)
1930{
1931 bool ok = true;
1932 uint s;
1933 // ### add toXShort to XmlString?
1934 if (sym(symbolIndex).c == 'x')
1935 s = symString(symbolIndex).view().sliced(1).toUInt(&ok, 16);
1936 else
1937 s = symString(symbolIndex).view().toUInt(&ok, 10);
1938
1939 ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff)
1940 || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint));
1941
1942 return ok ? s : 0;
1943}
1944
1945
1946void QXmlStreamReaderPrivate::checkPublicLiteral(QStringView publicId)
1947{
1948//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
1949
1950 const char16_t *data = publicId.utf16();
1951 uchar c = 0;
1952 qsizetype i;
1953 for (i = publicId.size() - 1; i >= 0; --i) {
1954 if (data[i] < 256)
1955 switch ((c = data[i])) {
1956 case ' ': case '\n': case '\r': case '-': case '(': case ')':
1957 case '+': case ',': case '.': case '/': case ':': case '=':
1958 case '?': case ';': case '!': case '*': case '#': case '@':
1959 case '$': case '_': case '%': case '\'': case '\"':
1960 continue;
1961 default:
1962 if (isAsciiLetterOrNumber(c))
1963 continue;
1964 }
1965 break;
1966 }
1967 if (i >= 0)
1968 raiseWellFormedError(QXmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c))));
1969}
1970
1971/*
1972 Checks whether the document starts with an xml declaration. If it
1973 does, this function returns \c true; otherwise it sets up everything
1974 for a synthetic start document event and returns \c false.
1975 */
1976bool QXmlStreamReaderPrivate::checkStartDocument()
1977{
1978 hasCheckedStartDocument = true;
1979
1980 if (scanString(spell[XML], XML))
1981 return true;
1982
1983 type = QXmlStreamReader::StartDocument;
1984 if (atEnd) {
1985 hasCheckedStartDocument = false;
1986 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1987 }
1988 return false;
1989}
1990
1991void QXmlStreamReaderPrivate::startDocument()
1992{
1993 QString err;
1994 if (documentVersion != "1.0"_L1) {
1995 if (documentVersion.view().contains(u' '))
1996 err = QXmlStream::tr("Invalid XML version string.");
1997 else
1998 err = QXmlStream::tr("Unsupported XML version.");
1999 }
2000 qsizetype n = attributeStack.size();
2001
2002 /* We use this bool to ensure that the pesudo attributes are in the
2003 * proper order:
2004 *
2005 * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
2006
2007 for (qsizetype i = 0; err.isNull() && i < n; ++i) {
2008 Attribute &attrib = attributeStack[i];
2009 XmlStringRef prefix(symPrefix(attrib.key));
2010 XmlStringRef key(symString(attrib.key));
2011 XmlStringRef value(symString(attrib.value));
2012
2013 if (prefix.isEmpty() && key == "encoding"_L1) {
2014 documentEncoding = value;
2015
2016 if (hasStandalone)
2017 err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding.");
2018 if (!QXmlUtils::isEncName(value))
2019 err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value);
2020 else {
2021 QByteArray enc = value.toString().toUtf8();
2022 if (!lockEncoding) {
2023 decoder = QStringDecoder(enc.constData());
2024 if (!decoder.isValid()) {
2025 // Raise an error only if the data was not already processed
2026 // by the chunk decoder. Otherwise simply fall back to
2027 // UTF-8 for backwards compatibility
2028 if (!chunkDecoder.isValid() || chunkDecoder.hasError())
2029 err = QXmlStream::tr("Encoding %1 is unsupported").arg(value);
2030 else
2031 decoder = QStringDecoder(QStringDecoder::Utf8);
2032 } else if (!rawReadBuffer.isEmpty() && nbytesread) {
2033 // Try to decode with the newly-determined encoding.
2034 // If the decoding is successful, consider it as a
2035 // better match for the decoded data.
2036 // That is only applicable if the previous chunk had
2037 // unspecified (i.e. System) encoding.
2038 QString buf = decoder(QByteArrayView(rawReadBuffer).first(nbytesread));
2039 if (!decoder.hasError())
2040 readBuffer = std::move(buf);
2041 }
2042 }
2043 }
2044 } else if (prefix.isEmpty() && key == "standalone"_L1) {
2045 hasStandalone = true;
2046 if (value == "yes"_L1)
2047 standalone = true;
2048 else if (value == "no"_L1)
2049 standalone = false;
2050 else
2051 err = QXmlStream::tr("Standalone accepts only yes or no.");
2052 } else {
2053 err = QXmlStream::tr("Invalid attribute in XML declaration: %1 = %2").arg(key).arg(value);
2054 }
2055 }
2056
2057 if (!err.isNull())
2058 raiseWellFormedError(err);
2059 attributeStack.clear();
2060}
2061
2062
2063void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message)
2064{
2065 this->error = error;
2066 errorString = message;
2067 if (errorString.isNull()) {
2068 if (error == QXmlStreamReader::PrematureEndOfDocumentError)
2069 errorString = QXmlStream::tr("Premature end of document.");
2070 else if (error == QXmlStreamReader::CustomError)
2071 errorString = QXmlStream::tr("Invalid document.");
2072 }
2073
2074 type = QXmlStreamReader::Invalid;
2075}
2076
2077void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message)
2078{
2079 raiseError(QXmlStreamReader::NotWellFormedError, message);
2080}
2081
2082void QXmlStreamReaderPrivate::raiseNamePrefixTooLongError()
2083{
2084 // TODO: add a ImplementationLimitsExceededError and use it instead
2085 raiseError(QXmlStreamReader::NotWellFormedError,
2086 QXmlStream::tr("Length of XML attribute name exceeds implementation limits (4KiB "
2087 "characters)."));
2088}
2089
2090void QXmlStreamReaderPrivate::parseError()
2091{
2092
2093 if (token == EOF_SYMBOL) {
2094 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
2095 return;
2096 }
2097 const int nmax = 4;
2098 QString error_message;
2099 int ers = state_stack[tos];
2100 int nexpected = 0;
2101 int expected[nmax];
2102 if (token != XML_ERROR)
2103 for (int tk = 0; tk < TERMINAL_COUNT; ++tk) {
2104 int k = t_action(ers, tk);
2105 if (k <= 0)
2106 continue;
2107 if (spell[tk]) {
2108 if (nexpected < nmax)
2109 expected[nexpected++] = tk;
2110 }
2111 }
2112
2113 if (nexpected && nexpected < nmax) {
2114 //: '<first option>'
2115 QString exp_str = QXmlStream::tr("'%1'", "expected")
2116 .arg(QLatin1StringView(spell[expected[0]]));
2117 if (nexpected == 2) {
2118 //: <first option>, '<second option>'
2119 exp_str = QXmlStream::tr("%1 or '%2'", "expected")
2120 .arg(exp_str, QLatin1StringView(spell[expected[1]]));
2121 } else if (nexpected > 2) {
2122 int s = 1;
2123 for (; s < nexpected - 1; ++s) {
2124 //: <options so far>, '<next option>'
2125 exp_str = QXmlStream::tr("%1, '%2'", "expected")
2126 .arg(exp_str, QLatin1StringView(spell[expected[s]]));
2127 }
2128 //: <options so far>, or '<final option>'
2129 exp_str = QXmlStream::tr("%1, or '%2'", "expected")
2130 .arg(exp_str, QLatin1StringView(spell[expected[s]]));
2131 }
2132 error_message = QXmlStream::tr("Expected %1, but got '%2'.")
2133 .arg(exp_str, QLatin1StringView(spell[token]));
2134 } else {
2135 error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1StringView(spell[token]));
2136 }
2137
2138 raiseWellFormedError(error_message);
2139}
2140
2141void QXmlStreamReaderPrivate::resume(int rule) {
2142 resumeReduction = rule;
2143 if (error == QXmlStreamReader::NoError)
2144 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
2145}
2146
2147/*! Returns the current line number, starting with 1.
2148
2149\sa columnNumber(), characterOffset()
2150 */
2151qint64 QXmlStreamReader::lineNumber() const
2152{
2153 Q_D(const QXmlStreamReader);
2154 return d->lineNumber + 1; // in public we start with 1
2155}
2156
2157/*! Returns the current column number, starting with 0.
2158
2159\sa lineNumber(), characterOffset()
2160 */
2161qint64 QXmlStreamReader::columnNumber() const
2162{
2163 Q_D(const QXmlStreamReader);
2164 return d->characterOffset - d->lastLineStart + d->readBufferPos;
2165}
2166
2167/*! Returns the current character offset, starting with 0.
2168
2169\sa lineNumber(), columnNumber()
2170*/
2171qint64 QXmlStreamReader::characterOffset() const
2172{
2173 Q_D(const QXmlStreamReader);
2174 return d->characterOffset + d->readBufferPos;
2175}
2176
2177
2178/*! Returns the text of \l Characters, \l Comment, \l DTD, or
2179 EntityReference.
2180 */
2181QStringView QXmlStreamReader::text() const
2182{
2183 Q_D(const QXmlStreamReader);
2184 return d->text;
2185}
2186
2187
2188/*! If the tokenType() is \l DTD, this function returns the DTD's
2189 notation declarations. Otherwise an empty vector is returned.
2190
2191 The QXmlStreamNotationDeclarations class is defined to be a QList
2192 of QXmlStreamNotationDeclaration.
2193 */
2194QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const
2195{
2196 Q_D(const QXmlStreamReader);
2197 if (d->notationDeclarations.size())
2198 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2199 return d->publicNotationDeclarations;
2200}
2201
2202
2203/*! If the tokenType() is \l DTD, this function returns the DTD's
2204 unparsed (external) entity declarations. Otherwise an empty vector is returned.
2205
2206 The QXmlStreamEntityDeclarations class is defined to be a QList
2207 of QXmlStreamEntityDeclaration.
2208 */
2209QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const
2210{
2211 Q_D(const QXmlStreamReader);
2212 if (d->entityDeclarations.size())
2213 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2214 return d->publicEntityDeclarations;
2215}
2216
2217/*!
2218 \since 4.4
2219
2220 If the tokenType() is \l DTD, this function returns the DTD's
2221 name. Otherwise an empty string is returned.
2222
2223 */
2224QStringView QXmlStreamReader::dtdName() const
2225{
2226 Q_D(const QXmlStreamReader);
2227 if (d->type == QXmlStreamReader::DTD)
2228 return d->dtdName;
2229 return QStringView();
2230}
2231
2232/*!
2233 \since 4.4
2234
2235 If the tokenType() is \l DTD, this function returns the DTD's
2236 public identifier. Otherwise an empty string is returned.
2237
2238 */
2239QStringView QXmlStreamReader::dtdPublicId() const
2240{
2241 Q_D(const QXmlStreamReader);
2242 if (d->type == QXmlStreamReader::DTD)
2243 return d->dtdPublicId;
2244 return QStringView();
2245}
2246
2247/*!
2248 \since 4.4
2249
2250 If the tokenType() is \l DTD, this function returns the DTD's
2251 system identifier. Otherwise an empty string is returned.
2252
2253 */
2254QStringView QXmlStreamReader::dtdSystemId() const
2255{
2256 Q_D(const QXmlStreamReader);
2257 if (d->type == QXmlStreamReader::DTD)
2258 return d->dtdSystemId;
2259 return QStringView();
2260}
2261
2262/*!
2263 \since 5.15
2264
2265 Returns the maximum amount of characters a single entity is
2266 allowed to expand into. If a single entity expands past the
2267 given limit, the document is not considered well formed.
2268
2269 \sa setEntityExpansionLimit
2270*/
2271int QXmlStreamReader::entityExpansionLimit() const
2272{
2273 Q_D(const QXmlStreamReader);
2274 return d->entityExpansionLimit;
2275}
2276
2277/*!
2278 \since 5.15
2279
2280 Sets the maximum amount of characters a single entity is
2281 allowed to expand into to \a limit. If a single entity expands
2282 past the given limit, the document is not considered well formed.
2283
2284 The limit is there to prevent DoS attacks when loading unknown
2285 XML documents where recursive entity expansion could otherwise
2286 exhaust all available memory.
2287
2288 The default value for this property is 4096 characters.
2289
2290 \sa entityExpansionLimit
2291*/
2292void QXmlStreamReader::setEntityExpansionLimit(int limit)
2293{
2294 Q_D(QXmlStreamReader);
2295 d->entityExpansionLimit = limit;
2296}
2297
2298/*! If the tokenType() is \l StartElement, this function returns the
2299 element's namespace declarations. Otherwise an empty vector is
2300 returned.
2301
2302 The QXmlStreamNamespaceDeclarations class is defined to be a QList
2303 of QXmlStreamNamespaceDeclaration.
2304
2305 \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations()
2306 */
2307QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const
2308{
2309 Q_D(const QXmlStreamReader);
2310 if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement)
2311 const_cast<QXmlStreamReaderPrivate *>(d)->resolvePublicNamespaces();
2312 return d->publicNamespaceDeclarations;
2313}
2314
2315
2316/*!
2317 \since 4.4
2318
2319 Adds an \a extraNamespaceDeclaration. The declaration will be
2320 valid for children of the current element, or - should the function
2321 be called before any elements are read - for the entire XML
2322 document.
2323
2324 \sa namespaceDeclarations(), addExtraNamespaceDeclarations(), setNamespaceProcessing()
2325 */
2326void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration)
2327{
2328 Q_D(QXmlStreamReader);
2329 QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
2330 namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix());
2331 namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri());
2332}
2333
2334/*!
2335 \since 4.4
2336
2337 Adds a vector of declarations specified by \a extraNamespaceDeclarations.
2338
2339 \sa namespaceDeclarations(), addExtraNamespaceDeclaration()
2340 */
2341void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations)
2342{
2343 for (const auto &extraNamespaceDeclaration : extraNamespaceDeclarations)
2344 addExtraNamespaceDeclaration(extraNamespaceDeclaration);
2345}
2346
2347
2348/*! Convenience function to be called in case a StartElement was
2349 read. Reads until the corresponding EndElement and returns all text
2350 in-between. In case of no error, the current token (see tokenType())
2351 after having called this function is EndElement.
2352
2353 The function concatenates text() when it reads either \l Characters
2354 or EntityReference tokens, but skips ProcessingInstruction and \l
2355 Comment. If the current token is not StartElement, an empty string is
2356 returned.
2357
2358 The \a behaviour defines what happens in case anything else is
2359 read before reaching EndElement. The function can include the text from
2360 child elements (useful for example for HTML), ignore child elements, or
2361 raise an UnexpectedElementError and return what was read so far (default).
2362
2363 \since 4.6
2364 */
2365QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour)
2366{
2367 Q_D(QXmlStreamReader);
2368 if (isStartElement()) {
2369 QString result;
2370 forever {
2371 switch (readNext()) {
2372 case Characters:
2373 case EntityReference:
2374 result.insert(result.size(), d->text);
2375 break;
2376 case EndElement:
2377 return result;
2378 case ProcessingInstruction:
2379 case Comment:
2380 break;
2381 case StartElement:
2382 if (behaviour == SkipChildElements) {
2383 skipCurrentElement();
2384 break;
2385 } else if (behaviour == IncludeChildElements) {
2386 result += readElementText(behaviour);
2387 break;
2388 }
2389 Q_FALLTHROUGH();
2390 default:
2391 if (d->error || behaviour == ErrorOnUnexpectedElement) {
2392 if (!d->error)
2393 d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data."));
2394 return result;
2395 }
2396 }
2397 }
2398 }
2399 return QString();
2400}
2401
2402/*! Raises a custom error with an optional error \a message.
2403
2404 \sa error(), errorString()
2405 */
2406void QXmlStreamReader::raiseError(const QString& message)
2407{
2408 Q_D(QXmlStreamReader);
2409 d->raiseError(CustomError, message);
2410}
2411
2412/*!
2413 Returns the error message that was set with raiseError().
2414
2415 \sa error(), lineNumber(), columnNumber(), characterOffset()
2416 */
2417QString QXmlStreamReader::errorString() const
2418{
2419 Q_D(const QXmlStreamReader);
2420 if (d->type == QXmlStreamReader::Invalid)
2421 return d->errorString;
2422 return QString();
2423}
2424
2425/*! Returns the type of the current error, or NoError if no error occurred.
2426
2427 \sa errorString(), raiseError()
2428 */
2429QXmlStreamReader::Error QXmlStreamReader::error() const
2430{
2431 Q_D(const QXmlStreamReader);
2432 if (d->type == QXmlStreamReader::Invalid)
2433 return d->error;
2434 return NoError;
2435}
2436
2437/*!
2438 Returns the target of a ProcessingInstruction.
2439 */
2440QStringView QXmlStreamReader::processingInstructionTarget() const
2441{
2442 Q_D(const QXmlStreamReader);
2443 return d->processingInstructionTarget;
2444}
2445
2446/*!
2447 Returns the data of a ProcessingInstruction.
2448 */
2449QStringView QXmlStreamReader::processingInstructionData() const
2450{
2451 Q_D(const QXmlStreamReader);
2452 return d->processingInstructionData;
2453}
2454
2455
2456
2457/*!
2458 Returns the local name of a StartElement, EndElement, or an EntityReference.
2459
2460 \sa namespaceUri(), qualifiedName()
2461 */
2462QStringView QXmlStreamReader::name() const
2463{
2464 Q_D(const QXmlStreamReader);
2465 return d->name;
2466}
2467
2468/*!
2469 Returns the namespaceUri of a StartElement or EndElement.
2470
2471 \sa name(), qualifiedName()
2472 */
2473QStringView QXmlStreamReader::namespaceUri() const
2474{
2475 Q_D(const QXmlStreamReader);
2476 return d->namespaceUri;
2477}
2478
2479/*!
2480 Returns the qualified name of a StartElement or EndElement;
2481
2482 A qualified name is the raw name of an element in the XML data. It
2483 consists of the namespace prefix, followed by colon, followed by the
2484 element's local name. Since the namespace prefix is not unique (the
2485 same prefix can point to different namespaces and different prefixes
2486 can point to the same namespace), you shouldn't use qualifiedName(),
2487 but the resolved namespaceUri() and the attribute's local name().
2488
2489 \sa name(), prefix(), namespaceUri()
2490 */
2491QStringView QXmlStreamReader::qualifiedName() const
2492{
2493 Q_D(const QXmlStreamReader);
2494 return d->qualifiedName;
2495}
2496
2497
2498
2499/*!
2500 \since 4.4
2501
2502 Returns the prefix of a StartElement or EndElement.
2503
2504 \sa name(), qualifiedName()
2505*/
2506QStringView QXmlStreamReader::prefix() const
2507{
2508 Q_D(const QXmlStreamReader);
2509 return d->prefix;
2510}
2511
2512/*!
2513 Returns the attributes of a StartElement.
2514 */
2515QXmlStreamAttributes QXmlStreamReader::attributes() const
2516{
2517 Q_D(const QXmlStreamReader);
2518 return d->attributes;
2519}
2520
2521#endif // feature xmlstreamreader
2522
2523/*!
2524 \class QXmlStreamAttribute
2525 \inmodule QtCore
2526 \since 4.3
2527 \reentrant
2528 \brief The QXmlStreamAttribute class represents a single XML attribute.
2529
2530 \ingroup xml-tools
2531
2532 \compares equality
2533
2534 An attribute consists of an optionally empty namespaceUri(), a
2535 name(), a value(), and an isDefault() attribute.
2536
2537 The raw XML attribute name is returned as qualifiedName().
2538*/
2539
2540/*!
2541 Creates an empty attribute.
2542 */
2543QXmlStreamAttribute::QXmlStreamAttribute()
2544{
2545 m_isDefault = false;
2546}
2547
2548/*! Constructs an attribute in the namespace described with \a
2549 namespaceUri with \a name and value \a value.
2550 */
2551QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)
2552{
2553 m_namespaceUri = namespaceUri;
2554 m_name = m_qualifiedName = name;
2555 m_value = value;
2556 m_namespaceUri = namespaceUri;
2557}
2558
2559/*!
2560 Constructs an attribute with qualified name \a qualifiedName and value \a value.
2561 */
2562QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value)
2563{
2564 qsizetype colon = qualifiedName.indexOf(u':');
2565 m_name = qualifiedName.mid(colon + 1);
2566 m_qualifiedName = qualifiedName;
2567 m_value = value;
2568}
2569
2570/*! \fn QStringView QXmlStreamAttribute::namespaceUri() const
2571
2572 Returns the attribute's resolved namespaceUri, or an empty string
2573 reference if the attribute does not have a defined namespace.
2574 */
2575/*! \fn QStringView QXmlStreamAttribute::name() const
2576 Returns the attribute's local name.
2577 */
2578/*! \fn QStringView QXmlStreamAttribute::qualifiedName() const
2579 Returns the attribute's qualified name.
2580
2581 A qualified name is the raw name of an attribute in the XML
2582 data. It consists of the namespace prefix(), followed by colon,
2583 followed by the attribute's local name(). Since the namespace prefix
2584 is not unique (the same prefix can point to different namespaces
2585 and different prefixes can point to the same namespace), you
2586 shouldn't use qualifiedName(), but the resolved namespaceUri() and
2587 the attribute's local name().
2588 */
2589/*!
2590 \fn QStringView QXmlStreamAttribute::prefix() const
2591 \since 4.4
2592 Returns the attribute's namespace prefix.
2593
2594 \sa name(), qualifiedName()
2595
2596*/
2597
2598/*! \fn QStringView QXmlStreamAttribute::value() const
2599 Returns the attribute's value.
2600 */
2601
2602/*! \fn bool QXmlStreamAttribute::isDefault() const
2603
2604 Returns \c true if the parser added this attribute with a default
2605 value following an ATTLIST declaration in the DTD; otherwise
2606 returns \c false.
2607*/
2608/*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &lhs, const QXmlStreamAttribute &rhs)
2609
2610 Compares \a lhs attribute with \a rhs and returns \c true if they are
2611 equal; otherwise returns \c false.
2612 */
2613/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &lhs, const QXmlStreamAttribute &rhs)
2614
2615 Compares \a lhs attribute with \a rhs and returns \c true if they are
2616 not equal; otherwise returns \c false.
2617 */
2618
2619/*!
2620 \class QXmlStreamAttributes
2621 \inmodule QtCore
2622 \since 4.3
2623 \reentrant
2624 \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute.
2625
2626 Attributes are returned by a QXmlStreamReader in
2627 \l{QXmlStreamReader::attributes()} {attributes()} when the reader
2628 reports a \l {QXmlStreamReader::StartElement}{start element}. The
2629 class can also be used with a QXmlStreamWriter as an argument to
2630 \l {QXmlStreamWriter::writeAttributes()}{writeAttributes()}.
2631
2632 The convenience function value() loops over the vector and returns
2633 an attribute value for a given namespaceUri and an attribute's
2634 name.
2635
2636 New attributes can be added with append().
2637
2638 \ingroup xml-tools
2639*/
2640
2641/*!
2642 \fn QXmlStreamAttributes::QXmlStreamAttributes()
2643
2644 A constructor for QXmlStreamAttributes.
2645*/
2646
2647/*!
2648 \typedef QXmlStreamNotationDeclarations
2649 \relates QXmlStreamNotationDeclaration
2650
2651 Synonym for QList<QXmlStreamNotationDeclaration>.
2652*/
2653
2654
2655/*!
2656 \class QXmlStreamNotationDeclaration
2657 \inmodule QtCore
2658 \since 4.3
2659 \reentrant
2660 \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration.
2661
2662 \ingroup xml-tools
2663
2664 \compares equality
2665
2666 An notation declaration consists of a name(), a systemId(), and a publicId().
2667*/
2668
2669/*!
2670 Creates an empty notation declaration.
2671*/
2672QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration()
2673{
2674}
2675
2676/*! \fn QStringView QXmlStreamNotationDeclaration::name() const
2677
2678Returns the notation name.
2679*/
2680/*! \fn QStringView QXmlStreamNotationDeclaration::systemId() const
2681
2682Returns the system identifier.
2683*/
2684/*! \fn QStringView QXmlStreamNotationDeclaration::publicId() const
2685
2686Returns the public identifier.
2687*/
2688
2689/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &lhs, const QXmlStreamNotationDeclaration &rhs)
2690
2691 Compares \a lhs notation declaration with \a rhs and returns \c true
2692 if they are equal; otherwise returns \c false.
2693 */
2694/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &lhs, const QXmlStreamNotationDeclaration &rhs)
2695
2696 Compares \a lhs notation declaration with \a rhs and returns \c true
2697 if they are not equal; otherwise returns \c false.
2698 */
2699
2700/*!
2701 \typedef QXmlStreamNamespaceDeclarations
2702 \relates QXmlStreamNamespaceDeclaration
2703
2704 Synonym for QList<QXmlStreamNamespaceDeclaration>.
2705*/
2706
2707/*!
2708 \class QXmlStreamNamespaceDeclaration
2709 \inmodule QtCore
2710 \since 4.3
2711 \reentrant
2712 \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration.
2713
2714 \ingroup xml-tools
2715
2716 \compares equality
2717
2718 An namespace declaration consists of a prefix() and a namespaceUri().
2719*/
2720/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &lhs, const QXmlStreamNamespaceDeclaration &rhs)
2721
2722 Compares \a lhs namespace declaration with \a rhs and returns \c true
2723 if they are equal; otherwise returns \c false.
2724 */
2725/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &lhs, const QXmlStreamNamespaceDeclaration &rhs)
2726
2727 Compares \a lhs namespace declaration with \a rhs and returns \c true
2728 if they are not equal; otherwise returns \c false.
2729 */
2730
2731/*!
2732 Creates an empty namespace declaration.
2733*/
2734QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration()
2735{
2736}
2737
2738/*!
2739 \since 4.4
2740
2741 Creates a namespace declaration with \a prefix and \a namespaceUri.
2742*/
2743QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri)
2744{
2745 m_prefix = prefix;
2746 m_namespaceUri = namespaceUri;
2747}
2748
2749/*! \fn QStringView QXmlStreamNamespaceDeclaration::prefix() const
2750
2751Returns the prefix.
2752*/
2753/*! \fn QStringView QXmlStreamNamespaceDeclaration::namespaceUri() const
2754
2755Returns the namespaceUri.
2756*/
2757
2758
2759
2760
2761/*!
2762 \typedef QXmlStreamEntityDeclarations
2763 \relates QXmlStreamEntityDeclaration
2764
2765 Synonym for QList<QXmlStreamEntityDeclaration>.
2766*/
2767
2768/*!
2769 \class QXmlString
2770 \inmodule QtCore
2771 \since 6.0
2772 \internal
2773*/
2774
2775/*!
2776 \class QXmlStreamEntityDeclaration
2777 \inmodule QtCore
2778 \since 4.3
2779 \reentrant
2780 \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration.
2781
2782 \ingroup xml-tools
2783
2784 \compares equality
2785 An entity declaration consists of a name(), a notationName(), a
2786 systemId(), a publicId(), and a value().
2787*/
2788
2789/*!
2790 Creates an empty entity declaration.
2791*/
2792QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration()
2793{
2794}
2795
2796/*! \fn QStringView QXmlStreamEntityDeclaration::name() const
2797
2798Returns the entity name.
2799*/
2800/*! \fn QStringView QXmlStreamEntityDeclaration::notationName() const
2801
2802Returns the notation name.
2803*/
2804/*! \fn QStringView QXmlStreamEntityDeclaration::systemId() const
2805
2806Returns the system identifier.
2807*/
2808/*! \fn QStringView QXmlStreamEntityDeclaration::publicId() const
2809
2810Returns the public identifier.
2811*/
2812/*! \fn QStringView QXmlStreamEntityDeclaration::value() const
2813
2814Returns the entity's value.
2815*/
2816
2817/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &lhs, const QXmlStreamEntityDeclaration &rhs)
2818
2819 Compares \a lhs entity declaration with \a rhs and returns \c true if
2820 they are equal; otherwise returns \c false.
2821 */
2822/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &lhs, const QXmlStreamEntityDeclaration &rhs)
2823
2824 Compares \a lhs entity declaration with \a rhs and returns \c true if
2825 they are not equal; otherwise returns \c false.
2826 */
2827
2828/*! Returns the value of the attribute \a name in the namespace
2829 described with \a namespaceUri, or an empty string reference if the
2830 attribute is not defined. The \a namespaceUri can be empty.
2831
2832 \note In Qt versions prior to 6.6, this function was implemented as an
2833 overload set accepting combinations of QString and QLatin1StringView only.
2834 */
2835QStringView QXmlStreamAttributes::value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
2836{
2837 for (const QXmlStreamAttribute &attribute : *this) {
2838 if (attribute.name() == name && attribute.namespaceUri() == namespaceUri)
2839 return attribute.value();
2840 }
2841 return QStringView();
2842}
2843
2844/*!\overload
2845
2846 Returns the value of the attribute with qualified name \a
2847 qualifiedName , or an empty string reference if the attribute is not
2848 defined. A qualified name is the raw name of an attribute in the XML
2849 data. It consists of the namespace prefix, followed by colon,
2850 followed by the attribute's local name. Since the namespace prefix
2851 is not unique (the same prefix can point to different namespaces and
2852 different prefixes can point to the same namespace), you shouldn't
2853 use qualified names, but a resolved namespaceUri and the attribute's
2854 local name.
2855
2856 \note In Qt versions prior to 6.6, this function was implemented as an
2857 overload set accepting QString and QLatin1StringView only.
2858
2859 */
2860QStringView QXmlStreamAttributes::value(QAnyStringView qualifiedName) const noexcept
2861{
2862 for (const QXmlStreamAttribute &attribute : *this) {
2863 if (attribute.qualifiedName() == qualifiedName)
2864 return attribute.value();
2865 }
2866 return QStringView();
2867}
2868
2869/*!Appends a new attribute with \a name in the namespace
2870 described with \a namespaceUri, and value \a value. The \a
2871 namespaceUri can be empty.
2872 */
2873void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value)
2874{
2875 append(QXmlStreamAttribute(namespaceUri, name, value));
2876}
2877
2878/*!\overload
2879 Appends a new attribute with qualified name \a qualifiedName and
2880 value \a value.
2881 */
2882void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value)
2883{
2884 append(QXmlStreamAttribute(qualifiedName, value));
2885}
2886
2887#if QT_CONFIG(xmlstreamreader)
2888
2889/*! \fn bool QXmlStreamReader::isStartDocument() const
2890 Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false.
2891*/
2892/*! \fn bool QXmlStreamReader::isEndDocument() const
2893 Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false.
2894*/
2895/*! \fn bool QXmlStreamReader::isStartElement() const
2896 Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false.
2897*/
2898/*! \fn bool QXmlStreamReader::isEndElement() const
2899 Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false.
2900*/
2901/*! \fn bool QXmlStreamReader::isCharacters() const
2902 Returns \c true if tokenType() equals \l Characters; otherwise returns \c false.
2903
2904 \sa isWhitespace(), isCDATA()
2905*/
2906/*! \fn bool QXmlStreamReader::isComment() const
2907 Returns \c true if tokenType() equals \l Comment; otherwise returns \c false.
2908*/
2909/*! \fn bool QXmlStreamReader::isDTD() const
2910 Returns \c true if tokenType() equals \l DTD; otherwise returns \c false.
2911*/
2912/*! \fn bool QXmlStreamReader::isEntityReference() const
2913 Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false.
2914*/
2915/*! \fn bool QXmlStreamReader::isProcessingInstruction() const
2916 Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false.
2917*/
2918
2919/*! Returns \c true if the reader reports characters that only consist
2920 of white-space; otherwise returns \c false.
2921
2922 \sa isCharacters(), text()
2923*/
2924bool QXmlStreamReader::isWhitespace() const
2925{
2926 Q_D(const QXmlStreamReader);
2927 return d->type == QXmlStreamReader::Characters && d->isWhitespace;
2928}
2929
2930/*! Returns \c true if the reader reports characters that stem from a
2931 CDATA section; otherwise returns \c false.
2932
2933 \sa isCharacters(), text()
2934*/
2935bool QXmlStreamReader::isCDATA() const
2936{
2937 Q_D(const QXmlStreamReader);
2938 return d->type == QXmlStreamReader::Characters && d->isCDATA;
2939}
2940
2941
2942
2943/*!
2944 Returns \c true if this document has been declared standalone in the
2945 XML declaration; otherwise returns \c false.
2946
2947 If no XML declaration has been parsed, this function returns \c false.
2948
2949 \sa hasStandaloneDeclaration()
2950 */
2951bool QXmlStreamReader::isStandaloneDocument() const
2952{
2953 Q_D(const QXmlStreamReader);
2954 return d->standalone;
2955}
2956
2957/*!
2958 \since 6.6
2959
2960 Returns \c true if this document has an explicit standalone
2961 declaration (can be 'yes' or 'no'); otherwise returns \c false;
2962
2963 If no XML declaration has been parsed, this function returns \c false.
2964
2965 \sa isStandaloneDocument()
2966 */
2967bool QXmlStreamReader::hasStandaloneDeclaration() const
2968{
2969 Q_D(const QXmlStreamReader);
2970 return d->hasStandalone;
2971}
2972
2973/*!
2974 \since 4.4
2975
2976 If the tokenType() is \l StartDocument, this function returns the
2977 version string as specified in the XML declaration.
2978 Otherwise an empty string is returned.
2979 */
2980QStringView QXmlStreamReader::documentVersion() const
2981{
2982 Q_D(const QXmlStreamReader);
2983 if (d->type == QXmlStreamReader::StartDocument)
2984 return d->documentVersion;
2985 return QStringView();
2986}
2987
2988/*!
2989 \since 4.4
2990
2991 If the tokenType() is \l StartDocument, this function returns the
2992 encoding string as specified in the XML declaration.
2993 Otherwise an empty string is returned.
2994 */
2995QStringView QXmlStreamReader::documentEncoding() const
2996{
2997 Q_D(const QXmlStreamReader);
2998 if (d->type == QXmlStreamReader::StartDocument)
2999 return d->documentEncoding;
3000 return QStringView();
3001}
3002
3003#endif // feature xmlstreamreader
3004
3005/*!
3006 \class QXmlStreamWriter
3007 \inmodule QtCore
3008 \since 4.3
3009 \reentrant
3010
3011 \brief The QXmlStreamWriter class provides an XML 1.0 writer with a
3012 simple streaming API.
3013
3014 \ingroup xml-tools
3015 \ingroup qtserialization
3016
3017 QXmlStreamWriter is the counterpart to QXmlStreamReader for writing
3018 XML.
3019 It is compliant with the XML 1.0 specification and writes documents
3020 using XML 1.0 syntax, escaping rules, and character validity
3021 constraints.
3022 \note XML 1.1 is not supported. While version strings may be set
3023 manually in the output, documents requiring features specific to
3024 XML 1.1, such as additional control characters cannot be produced
3025 using this class.
3026
3027 Like its related class, it operates on a QIODevice specified
3028 with setDevice(). The API is simple and straightforward: for every
3029 XML token or event you want to write, the writer provides a
3030 specialized function.
3031
3032 You start a document with writeStartDocument() and end it with
3033 writeEndDocument(). This will implicitly close all remaining open
3034 tags.
3035
3036 Element tags are opened with writeStartElement() followed by
3037 writeAttribute() or writeAttributes(), element content, and then
3038 writeEndElement(). A shorter form writeEmptyElement() can be used
3039 to write empty elements, followed by writeAttributes().
3040
3041 Element content consists of either characters, entity references or
3042 nested elements. It is written with writeCharacters(), which also
3043 takes care of escaping all forbidden characters and character
3044 sequences, writeEntityReference(), or subsequent calls to
3045 writeStartElement(). A convenience method writeTextElement() can be
3046 used for writing terminal elements that contain nothing but text.
3047
3048 The following abridged code snippet shows the basic use of the class
3049 to write formatted XML with indentation:
3050
3051 \snippet qxmlstreamwriter/main.cpp start stream
3052 \dots
3053 \snippet qxmlstreamwriter/main.cpp write element
3054 \dots
3055 \snippet qxmlstreamwriter/main.cpp finish stream
3056
3057 QXmlStreamWriter takes care of prefixing namespaces, all you have to
3058 do is specify the \c namespaceUri when writing elements or
3059 attributes. If you must conform to certain prefixes, you can force
3060 the writer to use them by declaring the namespaces manually with
3061 either writeNamespace() or writeDefaultNamespace(). Alternatively,
3062 you can bypass the stream writer's namespace support and use
3063 overloaded methods that take a qualified name instead. The namespace
3064 \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the
3065 prefix \e xml.
3066
3067 The stream writer can automatically format the generated XML data by
3068 adding line-breaks and indentation to empty sections between
3069 elements, making the XML data more readable for humans and easier to
3070 work with for most source code management systems. The feature can
3071 be turned on with the \l autoFormatting property, and customized
3072 with the \l autoFormattingIndent property.
3073
3074 Other functions are writeCDATA(), writeComment(),
3075 writeProcessingInstruction(), and writeDTD(). Chaining of XML
3076 streams is supported with writeCurrentToken().
3077
3078 QXmlStreamWriter always encodes XML in UTF-8.
3079
3080 If an error occurs while writing, \l hasError() will return true.
3081 However, by default, data that was already buffered at the time the error
3082 occurred, or data written from within the same operation, may still be
3083 written to the underlying device. This applies to \l Error::Encoding,
3084 \l Error::InvalidCharacter, and user-raised \l Error::Custom.
3085 To avoid this and ensure no data is written after an error, use the
3086 \l stopWritingOnError property. When this property is enabled,
3087 the first error stops output immediately and the writer ignores all
3088 subsequent write operations.
3089 Applications should treat the error state as terminal and avoid further
3090 use of the writer after an error.
3091
3092 The \l{QXmlStream Bookmarks Example} illustrates how to use a
3093 stream writer to write an XML bookmark file (XBEL) that
3094 was previously read in by a QXmlStreamReader.
3095
3096*/
3097
3098/*!
3099 \enum QXmlStreamWriter::Error
3100
3101 This enum specifies the different error cases that can occur
3102 when writing XML with QXmlStreamWriter.
3103
3104 \value None No error has occurred.
3105
3106 \value IO An I/O error occurred while writing to the
3107 device.
3108
3109 \value Encoding An encoding error occurred while converting
3110 characters to the output format.
3111
3112 \value InvalidCharacter A character not permitted in XML 1.0
3113 was encountered while writing.
3114
3115 \value Custom A custom error has been raised with
3116 \l raiseError().
3117
3118 \since 6.10
3119*/
3120
3121#if QT_CONFIG(xmlstreamwriter)
3122
3123class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
3124{
3125 QXmlStreamWriter *q_ptr;
3126 Q_DECLARE_PUBLIC(QXmlStreamWriter)
3127public:
3128 enum class StartElementOption {
3129 KeepEverything = 0, // write out every attribute, namespace, &c.
3130 OmitNamespaceDeclarations = 1,
3131 };
3132
3133 QXmlStreamWriterPrivate(QXmlStreamWriter *q);
3134 ~QXmlStreamWriterPrivate() {
3135 if (deleteDevice)
3136 delete device;
3137 }
3138
3139 void raiseError(QXmlStreamWriter::Error error);
3140 void raiseError(QXmlStreamWriter::Error error, QAnyStringView message);
3141 void write(QAnyStringView s);
3142 void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
3143 bool finishStartElement(bool contents = true);
3144 void writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
3145 StartElementOption option = StartElementOption::KeepEverything);
3146 QIODevice *device = nullptr;
3147 QString *stringDevice = nullptr;
3148 uint deleteDevice :1;
3149 uint inStartElement :1;
3150 uint inEmptyElement :1;
3151 uint lastWasStartElement :1;
3152 uint wroteSomething :1;
3153 uint autoFormatting :1;
3154 uint didWriteStartDocument :1;
3155 uint didWriteAnyToken :1;
3156 uint stopWritingOnError :1;
3157 std::string autoFormattingIndent = std::string(4, ' ');
3158 NamespaceDeclaration emptyNamespace;
3159 qsizetype lastNamespaceDeclaration = 1;
3160 QXmlStreamWriter::Error error = QXmlStreamWriter::Error::None;
3161 QString errorString;
3162
3163 NamespaceDeclaration &addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix);
3164 NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
3165 void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration);
3166
3167 int namespacePrefixCount = 0;
3168
3169 void indent(int level);
3170private:
3171 void doWriteToDevice(QStringView s);
3172 void doWriteToDevice(QUtf8StringView s);
3173 void doWriteToDevice(QLatin1StringView s);
3174};
3175
3176
3177QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
3178 : q_ptr(q), deleteDevice(false), inStartElement(false),
3179 inEmptyElement(false), lastWasStartElement(false),
3180 wroteSomething(false), autoFormatting(false),
3181 didWriteStartDocument(false), didWriteAnyToken(false),
3182 stopWritingOnError(false)
3183{
3184}
3185
3186void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode)
3187{
3188 error = errorCode;
3189 switch (error) {
3190 case QXmlStreamWriter::Error::IO:
3191 errorString = QXmlStream::tr("An I/O error occurred while writing");
3192 break;
3193 case QXmlStreamWriter::Error::Encoding:
3194 errorString = QXmlStream::tr("An encoding error occurred while writing");
3195 break;
3196 case QXmlStreamWriter::Error::InvalidCharacter:
3197 errorString = QXmlStream::tr("Encountered an invalid XML 1.0 character while writing");
3198 break;
3199 case QXmlStreamWriter::Error::Custom:
3200 errorString = QXmlStream::tr("An error occurred while writing");
3201 break;
3202 case QXmlStreamWriter::Error::None:
3203 errorString.clear();
3204 break;
3205 }
3206}
3207
3208void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode, QAnyStringView message)
3209{
3210 error = errorCode;
3211 errorString = message.toString();
3212}
3213
3214void QXmlStreamWriterPrivate::write(QAnyStringView s)
3215{
3216 if (stopWritingOnError && (error != QXmlStreamWriter::Error::None))
3217 return;
3218 if (device) {
3219 if (error == QXmlStreamWriter::Error::IO)
3220 return;
3221
3222 s.visit([&] (auto s) { doWriteToDevice(s); });
3223 } else if (stringDevice) {
3224 s.visit([&] (auto s) { stringDevice->append(s); });
3225 } else {
3226 qWarning("QXmlStreamWriter: No device");
3227 }
3228}
3229
3230void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
3231{
3232 struct NextResult {
3233 char32_t value;
3234 bool encodingError;
3235 };
3236 struct NextLatin1 {
3237 NextResult operator()(const char *&it, const char *) const
3238 { return {uchar(*it++), false}; }
3239 };
3240 struct NextUtf8 {
3241 NextResult operator()(const char *&it, const char *end) const
3242 {
3243 // We can have '\0' in the text, and it should be reported as
3244 // Error::InvalidCharacter, not as Error::Encoding
3245 constexpr char32_t invalidValue = 0xFFFFFFFF;
3246 static_assert(invalidValue > QChar::LastValidCodePoint);
3247 auto i = reinterpret_cast<const qchar8_t *>(it);
3248 const auto old_i = i;
3249 const auto e = reinterpret_cast<const qchar8_t *>(end);
3250 const char32_t result = QUtf8Functions::nextUcs4FromUtf8(i, e, invalidValue);
3251 it += i - old_i;
3252 return result == invalidValue ? NextResult{U'\0', true}
3253 : NextResult{result, false};
3254 }
3255 };
3256 struct NextUtf16 {
3257 NextResult operator()(const QChar *&it, const QChar *end) const
3258 {
3259 QStringIterator decoder(it, end);
3260 // We can have '\0' in the text, and it should be reported as
3261 // Error::InvalidCharacter, not as Error::Encoding
3262 constexpr char32_t invalidValue = 0xFFFFFFFF;
3263 static_assert(invalidValue > QChar::LastValidCodePoint);
3264 char32_t result = decoder.next(invalidValue);
3265 it = decoder.position();
3266 return result == invalidValue ? NextResult{U'\0', true}
3267 : NextResult{result, false};
3268 }
3269 };
3270
3271 QString escaped;
3272 escaped.reserve(s.size());
3273 s.visit([&] (auto s) {
3274 using View = decltype(s);
3275 using Decoder = std::conditional_t<std::is_same_v<View, QLatin1StringView>, NextLatin1,
3276 std::conditional_t<std::is_same_v<View, QUtf8StringView>, NextUtf8, NextUtf16>>;
3277
3278 auto it = s.begin();
3279 const auto end = s.end();
3280 Decoder decoder;
3281
3282 while (it != end) {
3283 QLatin1StringView replacement;
3284 auto mark = it;
3285
3286 while (it != end) {
3287 auto next_it = it;
3288 const auto decoded = decoder(next_it, end);
3289 switch (decoded.value) {
3290 case u'<':
3291 replacement = "&lt;"_L1;
3292 break;
3293 case u'>':
3294 replacement = "&gt;"_L1;
3295 break;
3296 case u'&':
3297 replacement = "&amp;"_L1;
3298 break;
3299 case u'\"':
3300 replacement = "&quot;"_L1;
3301 break;
3302 case u'\t':
3303 if (escapeWhitespace)
3304 replacement = "&#9;"_L1;
3305 break;
3306 case u'\n':
3307 if (escapeWhitespace)
3308 replacement = "&#10;"_L1;
3309 break;
3310 case u'\r':
3311 if (escapeWhitespace)
3312 replacement = "&#13;"_L1;
3313 break;
3314 case u'\v':
3315 case u'\f':
3316 raiseError(QXmlStreamWriter::Error::InvalidCharacter);
3317 if (stopWritingOnError)
3318 return;
3319 replacement = ""_L1;
3320 Q_ASSERT(!replacement.isNull());
3321 break;
3322 default:
3323 if (decoded.value > 0x1F)
3324 break;
3325 // ASCII control characters
3326 Q_FALLTHROUGH();
3327 case 0xFFFE:
3328 case 0xFFFF:
3329 raiseError(decoded.encodingError
3330 ? QXmlStreamWriter::Error::Encoding
3331 : QXmlStreamWriter::Error::InvalidCharacter);
3332 if (stopWritingOnError)
3333 return;
3334 replacement = ""_L1;
3335 Q_ASSERT(!replacement.isNull());
3336 break;
3337 }
3338 if (!replacement.isNull())
3339 break;
3340 it = next_it;
3341 }
3342
3343 escaped.append(View{mark, it});
3344 escaped.append(replacement);
3345 if (it != end)
3346 ++it;
3347 }
3348 } );
3349
3350 write(escaped);
3351}
3352
3353void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) {
3354 if (namespaceDeclaration.prefix.isEmpty()) {
3355 write(" xmlns=\"");
3356 write(namespaceDeclaration.namespaceUri);
3357 write("\"");
3358 } else {
3359 write(" xmlns:");
3360 write(namespaceDeclaration.prefix);
3361 write("=\"");
3362 write(namespaceDeclaration.namespaceUri);
3363 write("\"");
3364 }
3365 didWriteAnyToken = true;
3366}
3367
3368bool QXmlStreamWriterPrivate::finishStartElement(bool contents)
3369{
3370 bool hadSomethingWritten = wroteSomething;
3371 wroteSomething = contents;
3372 if (!inStartElement)
3373 return hadSomethingWritten;
3374
3375 if (inEmptyElement) {
3376 write("/>");
3377 QXmlStreamWriterPrivate::Tag tag = tagStack_pop();
3378 lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3379 lastWasStartElement = false;
3380 } else {
3381 write(">");
3382 }
3383 inStartElement = inEmptyElement = false;
3384 lastNamespaceDeclaration = namespaceDeclarations.size();
3385 didWriteAnyToken = true;
3386 return hadSomethingWritten;
3387}
3388
3389QXmlStreamPrivateTagStack::NamespaceDeclaration &
3390QXmlStreamWriterPrivate::addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
3391{
3392 const bool prefixIsXml = prefix == "xml"_L1;
3393 const bool namespaceUriIsXml = namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1;
3394 if (prefixIsXml && !namespaceUriIsXml) {
3395 qWarning("Reserved prefix 'xml' must not be bound to a different namespace name "
3396 "than 'http://www.w3.org/XML/1998/namespace'");
3397 } else if (!prefixIsXml && namespaceUriIsXml) {
3398 const QString prefixString = prefix.toString();
3399 qWarning("The prefix '%ls' must not be bound to namespace name "
3400 "'http://www.w3.org/XML/1998/namespace' which 'xml' is already bound to",
3401 qUtf16Printable(prefixString));
3402 }
3403 if (namespaceUri == "http://www.w3.org/2000/xmlns/"_L1) {
3404 const QString prefixString = prefix.toString();
3405 qWarning("The prefix '%ls' must not be bound to namespace name "
3406 "'http://www.w3.org/2000/xmlns/'",
3407 qUtf16Printable(prefixString));
3408 }
3409 auto &namespaceDeclaration = namespaceDeclarations.push();
3410 namespaceDeclaration.prefix = addToStringStorage(prefix);
3411 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3412 return namespaceDeclaration;
3413}
3414
3415QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(QAnyStringView namespaceUri, bool writeDeclaration, bool noDefault)
3416{
3417 for (NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
3418 if (namespaceDeclaration.namespaceUri == namespaceUri) {
3419 if (!noDefault || !namespaceDeclaration.prefix.isEmpty())
3420 return namespaceDeclaration;
3421 }
3422 }
3423 if (namespaceUri.isEmpty())
3424 return emptyNamespace;
3425 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
3426 if (namespaceUri.isEmpty()) {
3427 namespaceDeclaration.prefix.clear();
3428 } else {
3429 QString s;
3430 int n = ++namespacePrefixCount;
3431 forever {
3432 s = u'n' + QString::number(n++);
3433 qsizetype j = namespaceDeclarations.size() - 2;
3434 while (j >= 0 && namespaceDeclarations.at(j).prefix != s)
3435 --j;
3436 if (j < 0)
3437 break;
3438 }
3439 namespaceDeclaration.prefix = addToStringStorage(s);
3440 }
3441 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3442 if (writeDeclaration)
3443 writeNamespaceDeclaration(namespaceDeclaration);
3444 return namespaceDeclaration;
3445}
3446
3447
3448
3449void QXmlStreamWriterPrivate::indent(int level)
3450{
3451 if (didWriteStartDocument || didWriteAnyToken)
3452 write("\n");
3453 for (int i = 0; i < level; ++i)
3454 write(autoFormattingIndent);
3455}
3456
3457void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
3458{
3459 constexpr qsizetype MaxChunkSize = 512;
3460 char buffer [3 * MaxChunkSize];
3461 QStringEncoder::State state;
3462 while (!s.isEmpty()) {
3463 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3464 char *end = QUtf8::convertFromUnicode(buffer, s.first(chunkSize), &state);
3465 doWriteToDevice(QUtf8StringView{buffer, end});
3466 s = s.sliced(chunkSize);
3467 }
3468 if (state.remainingChars > 0)
3469 raiseError(QXmlStreamWriter::Error::Encoding);
3470}
3471
3472void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
3473{
3474 QByteArrayView bytes = s;
3475 if (device->write(bytes.data(), bytes.size()) != bytes.size())
3476 raiseError(QXmlStreamWriter::Error::IO);
3477}
3478
3479void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
3480{
3481 constexpr qsizetype MaxChunkSize = 512;
3482 char buffer [2 * MaxChunkSize];
3483 while (!s.isEmpty()) {
3484 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3485 char *end = QUtf8::convertFromLatin1(buffer, s.first(chunkSize));
3486 doWriteToDevice(QUtf8StringView{buffer, end});
3487 s = s.sliced(chunkSize);
3488 }
3489}
3490
3491/*!
3492 Constructs a stream writer.
3493
3494 \sa setDevice()
3495 */
3496QXmlStreamWriter::QXmlStreamWriter()
3497 : d_ptr(new QXmlStreamWriterPrivate(this))
3498{
3499}
3500
3501/*!
3502 Constructs a stream writer that writes into \a device;
3503 */
3504QXmlStreamWriter::QXmlStreamWriter(QIODevice *device)
3505 : d_ptr(new QXmlStreamWriterPrivate(this))
3506{
3507 Q_D(QXmlStreamWriter);
3508 d->device = device;
3509}
3510
3511/*! Constructs a stream writer that writes into \a array. This is the
3512 same as creating an xml writer that operates on a QBuffer device
3513 which in turn operates on \a array.
3514 */
3515QXmlStreamWriter::QXmlStreamWriter(QByteArray *array)
3516 : d_ptr(new QXmlStreamWriterPrivate(this))
3517{
3518 Q_D(QXmlStreamWriter);
3519 d->device = new QBuffer(array);
3520 d->device->open(QIODevice::WriteOnly);
3521 d->deleteDevice = true;
3522}
3523
3524
3525/*! Constructs a stream writer that writes into \a string.
3526 */
3527QXmlStreamWriter::QXmlStreamWriter(QString *string)
3528 : d_ptr(new QXmlStreamWriterPrivate(this))
3529{
3530 Q_D(QXmlStreamWriter);
3531 d->stringDevice = string;
3532}
3533
3534/*!
3535 Destructor.
3536*/
3537QXmlStreamWriter::~QXmlStreamWriter()
3538{
3539}
3540
3541
3542/*!
3543 Sets the current device to \a device. If you want the stream to
3544 write into a QByteArray, you can create a QBuffer device.
3545
3546 \sa device()
3547*/
3548void QXmlStreamWriter::setDevice(QIODevice *device)
3549{
3550 Q_D(QXmlStreamWriter);
3551 if (device == d->device)
3552 return;
3553 d->stringDevice = nullptr;
3554 if (d->deleteDevice) {
3555 delete d->device;
3556 d->deleteDevice = false;
3557 }
3558 d->device = device;
3559}
3560
3561/*!
3562 Returns the current device associated with the QXmlStreamWriter,
3563 or \nullptr if no device has been assigned.
3564
3565 \sa setDevice()
3566*/
3567QIODevice *QXmlStreamWriter::device() const
3568{
3569 Q_D(const QXmlStreamWriter);
3570 return d->device;
3571}
3572
3573/*!
3574 \property QXmlStreamWriter::autoFormatting
3575 \since 4.4
3576 \brief the auto-formatting flag of the stream writer.
3577
3578 This property controls whether or not the stream writer
3579 automatically formats the generated XML data. If enabled, the
3580 writer automatically adds line-breaks and indentation to empty
3581 sections between elements (ignorable whitespace). The main purpose
3582 of auto-formatting is to split the data into several lines, and to
3583 increase readability for a human reader. The indentation depth can
3584 be controlled through the \l autoFormattingIndent property.
3585
3586 By default, auto-formatting is disabled.
3587*/
3588
3589/*!
3590 \since 4.4
3591
3592 Enables auto formatting if \a enable is \c true, otherwise
3593 disables it.
3594
3595 The default value is \c false.
3596 */
3597void QXmlStreamWriter::setAutoFormatting(bool enable)
3598{
3599 Q_D(QXmlStreamWriter);
3600 d->autoFormatting = enable;
3601}
3602
3603/*!
3604 \since 4.4
3605
3606 Returns \c true if auto formatting is enabled, otherwise \c false.
3607 */
3608bool QXmlStreamWriter::autoFormatting() const
3609{
3610 Q_D(const QXmlStreamWriter);
3611 return d->autoFormatting;
3612}
3613
3614/*!
3615 \property QXmlStreamWriter::autoFormattingIndent
3616 \since 4.4
3617
3618 \brief the number of spaces or tabs used for indentation when
3619 auto-formatting is enabled. Positive numbers indicate spaces,
3620 negative numbers tabs.
3621
3622 The default indentation is 4.
3623
3624 \sa autoFormatting
3625*/
3626
3627
3628void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs)
3629{
3630 Q_D(QXmlStreamWriter);
3631 d->autoFormattingIndent.assign(size_t(qAbs(spacesOrTabs)), spacesOrTabs >= 0 ? ' ' : '\t');
3632}
3633
3634int QXmlStreamWriter::autoFormattingIndent() const
3635{
3636 Q_D(const QXmlStreamWriter);
3637 const QLatin1StringView indent(d->autoFormattingIndent);
3638 return indent.count(u' ') - indent.count(u'\t');
3639}
3640
3641/*!
3642 \property QXmlStreamWriter::stopWritingOnError
3643 \since 6.10
3644
3645 \brief The option to stop writing to the device after encountering an error.
3646
3647 If this property is set to \c true, the writer stops writing immediately upon
3648 encountering any error and ignores all subsequent write operations.
3649 When this property is set to \c false, the writer may continue writing
3650 after an error, skipping the invalid write but allowing further output.
3651
3652 Note that this includes \l Error::InvalidCharacter, \l Error::Encoding,
3653 and \l Error::Custom. \l Error::IO is always considered terminal
3654 and stops writing regardless of this setting.
3655
3656 The default value is \c false.
3657 */
3658bool QXmlStreamWriter::stopWritingOnError() const
3659{
3660 Q_D(const QXmlStreamWriter);
3661 return d->stopWritingOnError;
3662}
3663
3664void QXmlStreamWriter::setStopWritingOnError(bool stop)
3665{
3666 Q_D(QXmlStreamWriter);
3667 d->stopWritingOnError = stop;
3668}
3669
3670/*!
3671 Returns \c true if an error occurred while trying to write data.
3672
3673 If the error is \l Error::IO, subsequent writes to the underlying
3674 QIODevice will fail. In other cases malformed data might be written to
3675 the document.
3676
3677 The error status is never reset. Writes happening after the error
3678 occurred may be ignored, even if the error condition is cleared.
3679
3680 \sa error(), errorString(), raiseError()
3681 */
3682bool QXmlStreamWriter::hasError() const
3683{
3684 return error() != QXmlStreamWriter::Error::None;
3685}
3686
3687/*!
3688 Returns the current error state of the writer.
3689
3690 If no error has occurred, this function returns
3691 QXmlStreamWriter::Error::None.
3692
3693 \since 6.10
3694 \sa errorString(), raiseError(), hasError()
3695 */
3696QXmlStreamWriter::Error QXmlStreamWriter::error() const
3697{
3698 Q_D(const QXmlStreamWriter);
3699 return d->error;
3700}
3701
3702/*!
3703 If an error has occurred, returns its associated error message.
3704
3705 The error message is either set internally by QXmlStreamWriter or provided
3706 by the user via raiseError(). If no error has occured, this function returns
3707 a null string.
3708
3709 \since 6.10
3710 \sa error(), raiseError(), hasError()
3711 */
3712QString QXmlStreamWriter::errorString() const
3713{
3714 Q_D(const QXmlStreamWriter);
3715 return d->errorString;
3716}
3717
3718/*!
3719 Raises a custom error with the given \a message.
3720
3721 This function is for manual indication that an error has occurred during
3722 writing, such as an application level validation failure.
3723
3724 \since 6.10
3725 \sa errorString(), error(), hasError()
3726 */
3727void QXmlStreamWriter::raiseError(QAnyStringView message)
3728{
3729 Q_D(QXmlStreamWriter);
3730 d->raiseError(QXmlStreamWriter::Error::Custom, message);
3731}
3732
3733/*!
3734 \overload
3735 Writes an attribute with \a qualifiedName and \a value.
3736
3737
3738 This function can only be called after writeStartElement() before
3739 any content is written, or after writeEmptyElement().
3740
3741 \note In Qt versions prior to 6.5, this function took QString, not
3742 QAnyStringView.
3743 */
3744void QXmlStreamWriter::writeAttribute(QAnyStringView qualifiedName, QAnyStringView value)
3745{
3746 Q_D(QXmlStreamWriter);
3747 Q_ASSERT(d->inStartElement);
3748 Q_ASSERT(count(qualifiedName, ':') <= 1);
3749 d->write(" ");
3750 d->write(qualifiedName);
3751 d->write("=\"");
3752 d->writeEscaped(value, true);
3753 d->write("\"");
3754 d->didWriteAnyToken = true;
3755}
3756
3757/*! Writes an attribute with \a name and \a value, prefixed for
3758 the specified \a namespaceUri. If the namespace has not been
3759 declared yet, QXmlStreamWriter will generate a namespace declaration
3760 for it.
3761
3762 This function can only be called after writeStartElement() before
3763 any content is written, or after writeEmptyElement().
3764
3765 \note In Qt versions prior to 6.5, this function took QString, not
3766 QAnyStringView.
3767 */
3768void QXmlStreamWriter::writeAttribute(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView value)
3769{
3770 Q_D(QXmlStreamWriter);
3771 Q_ASSERT(d->inStartElement);
3772 Q_ASSERT(!contains(name, ':'));
3773 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true);
3774 d->write(" ");
3775 if (!namespaceDeclaration.prefix.isEmpty()) {
3776 d->write(namespaceDeclaration.prefix);
3777 d->write(":");
3778 }
3779 d->write(name);
3780 d->write("=\"");
3781 d->writeEscaped(value, true);
3782 d->write("\"");
3783 d->didWriteAnyToken = true;
3784}
3785
3786/*!
3787 \overload
3788
3789 Writes the \a attribute.
3790
3791 This function can only be called after writeStartElement() before
3792 any content is written, or after writeEmptyElement().
3793 */
3794void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute)
3795{
3796 if (attribute.namespaceUri().isEmpty())
3797 writeAttribute(attribute.qualifiedName(), attribute.value());
3798 else
3799 writeAttribute(attribute.namespaceUri(), attribute.name(), attribute.value());
3800}
3801
3802
3803/*! Writes the attribute vector \a attributes. If a namespace
3804 referenced in an attribute not been declared yet, QXmlStreamWriter
3805 will generate a namespace declaration for it.
3806
3807 This function can only be called after writeStartElement() before
3808 any content is written, or after writeEmptyElement().
3809
3810 \sa writeAttribute(), writeNamespace()
3811 */
3812void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
3813{
3814 Q_D(QXmlStreamWriter);
3815 Q_ASSERT(d->inStartElement);
3816 Q_UNUSED(d);
3817 for (const auto &attr : attributes)
3818 writeAttribute(attr);
3819}
3820
3821
3822/*! Writes \a text as CDATA section. If \a text contains the
3823 forbidden character sequence "]]>", it is split into different CDATA
3824 sections.
3825
3826 This function mainly exists for completeness. Normally you should
3827 not need use it, because writeCharacters() automatically escapes all
3828 non-content characters.
3829
3830 \note In Qt versions prior to 6.5, this function took QString, not
3831 QAnyStringView.
3832 */
3833void QXmlStreamWriter::writeCDATA(QAnyStringView text)
3834{
3835 Q_D(QXmlStreamWriter);
3836 d->finishStartElement();
3837 d->write("<![CDATA[");
3838 while (!text.isEmpty()) {
3839 const auto idx = indexOf(text, "]]>"_L1);
3840 if (idx < 0)
3841 break; // no forbidden sequence found
3842 d->write(text.first(idx));
3843 d->write("]]" // text[idx, idx + 2)
3844 "]]><![CDATA[" // escape sequence to separate ]] and >
3845 ">"); // text[idx + 2, idx + 3)
3846 text = text.sliced(idx + 3); // skip over "]]>"
3847 }
3848 d->write(text); // write remainder
3849 d->write("]]>");
3850}
3851
3852
3853/*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity
3854 references "&lt;", "&amp;, and "&quot;". To avoid the forbidden sequence
3855 "]]>", ">" is also escaped as "&gt;".
3856
3857 \sa writeEntityReference()
3858
3859 \note In Qt versions prior to 6.5, this function took QString, not
3860 QAnyStringView.
3861 */
3862void QXmlStreamWriter::writeCharacters(QAnyStringView text)
3863{
3864 Q_D(QXmlStreamWriter);
3865 d->finishStartElement();
3866 d->writeEscaped(text);
3867}
3868
3869
3870/*! Writes \a text as XML comment, where \a text must not contain the
3871 forbidden sequence \c{--} or end with \c{-}. Note that XML does not
3872 provide any way to escape \c{-} in a comment.
3873
3874 \note In Qt versions prior to 6.5, this function took QString, not
3875 QAnyStringView.
3876 */
3877void QXmlStreamWriter::writeComment(QAnyStringView text)
3878{
3879 Q_D(QXmlStreamWriter);
3880 Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
3881 if (!d->finishStartElement(false) && d->autoFormatting)
3882 d->indent(d->tagStack.size());
3883 d->write("<!--");
3884 d->write(text);
3885 d->write("-->");
3886 d->inStartElement = d->lastWasStartElement = false;
3887}
3888
3889
3890/*! Writes a DTD section. The \a dtd represents the entire
3891 doctypedecl production from the XML 1.0 specification.
3892
3893 \note In Qt versions prior to 6.5, this function took QString, not
3894 QAnyStringView.
3895 */
3896void QXmlStreamWriter::writeDTD(QAnyStringView dtd)
3897{
3898 Q_D(QXmlStreamWriter);
3899 d->finishStartElement();
3900 if (d->autoFormatting)
3901 d->write("\n");
3902 d->write(dtd);
3903 if (d->autoFormatting)
3904 d->write("\n");
3905}
3906
3907
3908
3909/*! \overload
3910 Writes an empty element with qualified name \a qualifiedName.
3911 Subsequent calls to writeAttribute() will add attributes to this element.
3912
3913 \note In Qt versions prior to 6.5, this function took QString, not
3914 QAnyStringView.
3915*/
3916void QXmlStreamWriter::writeEmptyElement(QAnyStringView qualifiedName)
3917{
3918 Q_D(QXmlStreamWriter);
3919 Q_ASSERT(count(qualifiedName, ':') <= 1);
3920 d->writeStartElement({}, qualifiedName);
3921 d->inEmptyElement = true;
3922}
3923
3924
3925/*! Writes an empty element with \a name, prefixed for the specified
3926 \a namespaceUri. If the namespace has not been declared,
3927 QXmlStreamWriter will generate a namespace declaration for it.
3928 Subsequent calls to writeAttribute() will add attributes to this element.
3929
3930 \sa writeNamespace()
3931
3932 \note In Qt versions prior to 6.5, this function took QString, not
3933 QAnyStringView.
3934 */
3935void QXmlStreamWriter::writeEmptyElement(QAnyStringView namespaceUri, QAnyStringView name)
3936{
3937 Q_D(QXmlStreamWriter);
3938 Q_ASSERT(!contains(name, ':'));
3939 d->writeStartElement(namespaceUri, name);
3940 d->inEmptyElement = true;
3941}
3942
3943
3944/*!\overload
3945 Writes a text element with \a qualifiedName and \a text.
3946
3947
3948 This is a convenience function equivalent to:
3949 \snippet code/src_corelib_xml_qxmlstream.cpp 1
3950
3951 \note In Qt versions prior to 6.5, this function took QString, not
3952 QAnyStringView.
3953*/
3954void QXmlStreamWriter::writeTextElement(QAnyStringView qualifiedName, QAnyStringView text)
3955{
3956 writeStartElement(qualifiedName);
3957 writeCharacters(text);
3958 writeEndElement();
3959}
3960
3961/*! Writes a text element with \a name, prefixed for the specified \a
3962 namespaceUri, and \a text. If the namespace has not been
3963 declared, QXmlStreamWriter will generate a namespace declaration
3964 for it.
3965
3966
3967 This is a convenience function equivalent to:
3968 \snippet code/src_corelib_xml_qxmlstream.cpp 2
3969
3970 \note In Qt versions prior to 6.5, this function took QString, not
3971 QAnyStringView.
3972*/
3973void QXmlStreamWriter::writeTextElement(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView text)
3974{
3975 writeStartElement(namespaceUri, name);
3976 writeCharacters(text);
3977 writeEndElement();
3978}
3979
3980
3981/*!
3982 Closes all remaining open start elements and writes a newline.
3983
3984 \sa writeStartDocument()
3985 */
3986void QXmlStreamWriter::writeEndDocument()
3987{
3988 Q_D(QXmlStreamWriter);
3989 while (d->tagStack.size())
3990 writeEndElement();
3991 if (d->didWriteStartDocument || d->didWriteAnyToken)
3992 d->write("\n");
3993}
3994
3995/*!
3996 Closes the previous start element.
3997
3998 \sa writeStartElement()
3999 */
4000void QXmlStreamWriter::writeEndElement()
4001{
4002 Q_D(QXmlStreamWriter);
4003 Q_ASSERT(d->didWriteAnyToken);
4004 if (d->tagStack.isEmpty())
4005 return;
4006
4007 // shortcut: if nothing was written, close as empty tag
4008 if (d->inStartElement && !d->inEmptyElement) {
4009 d->write("/>");
4010 d->lastWasStartElement = d->inStartElement = false;
4011 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
4012 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
4013 return;
4014 }
4015
4016 if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting)
4017 d->indent(d->tagStack.size()-1);
4018 if (d->tagStack.isEmpty())
4019 return;
4020 d->lastWasStartElement = false;
4021 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
4022 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
4023 d->write("</");
4024 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
4025 d->write(tag.namespaceDeclaration.prefix);
4026 d->write(":");
4027 }
4028 d->write(tag.name);
4029 d->write(">");
4030}
4031
4032
4033
4034/*!
4035 Writes the entity reference \a name to the stream, as "&\a{name};".
4036
4037 \note In Qt versions prior to 6.5, this function took QString, not
4038 QAnyStringView.
4039 */
4040void QXmlStreamWriter::writeEntityReference(QAnyStringView name)
4041{
4042 Q_D(QXmlStreamWriter);
4043 d->finishStartElement();
4044 d->write("&");
4045 d->write(name);
4046 d->write(";");
4047}
4048
4049
4050/*! Writes a namespace declaration for \a namespaceUri with \a
4051 prefix. If \a prefix is empty, QXmlStreamWriter assigns a unique
4052 prefix consisting of the letter 'n' followed by a number.
4053
4054 If writeStartElement() or writeEmptyElement() was called, the
4055 declaration applies to the current element; otherwise it applies to
4056 the next child element.
4057
4058 Note that the prefix \e xml is both predefined and reserved for
4059 \e http://www.w3.org/XML/1998/namespace, which in turn cannot be
4060 bound to any other prefix. The prefix \e xmlns and its URI
4061 \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism
4062 itself and thus completely forbidden in declarations.
4063
4064 \note In Qt versions prior to 6.5, this function took QString, not
4065 QAnyStringView.
4066 */
4067void QXmlStreamWriter::writeNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
4068{
4069 Q_D(QXmlStreamWriter);
4070 Q_ASSERT(prefix != "xmlns"_L1);
4071 if (prefix.isEmpty()) {
4072 d->findNamespace(namespaceUri, d->inStartElement);
4073 } else {
4074 auto &namespaceDeclaration = d->addExtraNamespace(namespaceUri, prefix);
4075 if (d->inStartElement)
4076 d->writeNamespaceDeclaration(namespaceDeclaration);
4077 }
4078}
4079
4080
4081/*! Writes a default namespace declaration for \a namespaceUri.
4082
4083 If writeStartElement() or writeEmptyElement() was called, the
4084 declaration applies to the current element; otherwise it applies to
4085 the next child element.
4086
4087 Note that the namespaces \e http://www.w3.org/XML/1998/namespace
4088 (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to
4089 \e xml) by definition cannot be declared as default.
4090
4091 \note In Qt versions prior to 6.5, this function took QString, not
4092 QAnyStringView.
4093 */
4094void QXmlStreamWriter::writeDefaultNamespace(QAnyStringView namespaceUri)
4095{
4096 Q_D(QXmlStreamWriter);
4097 Q_ASSERT(namespaceUri != "http://www.w3.org/XML/1998/namespace"_L1);
4098 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
4099 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
4100 namespaceDeclaration.prefix.clear();
4101 namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri);
4102 if (d->inStartElement)
4103 d->writeNamespaceDeclaration(namespaceDeclaration);
4104}
4105
4106
4107/*!
4108 Writes an XML processing instruction with \a target and \a data,
4109 where \a data must not contain the sequence "?>".
4110
4111 \note In Qt versions prior to 6.5, this function took QString, not
4112 QAnyStringView.
4113 */
4114void QXmlStreamWriter::writeProcessingInstruction(QAnyStringView target, QAnyStringView data)
4115{
4116 Q_D(QXmlStreamWriter);
4117 Q_ASSERT(!contains(data, "?>"_L1));
4118 if (!d->finishStartElement(false) && d->autoFormatting)
4119 d->indent(d->tagStack.size());
4120 d->write("<?");
4121 d->write(target);
4122 if (!data.isNull()) {
4123 d->write(" ");
4124 d->write(data);
4125 }
4126 d->write("?>");
4127 d->didWriteAnyToken = true;
4128}
4129
4130
4131
4132/*!\overload
4133
4134 Writes a document start with XML version number "1.0".
4135
4136 \sa writeEndDocument()
4137 \since 4.5
4138 */
4139void QXmlStreamWriter::writeStartDocument()
4140{
4141 writeStartDocument("1.0"_L1);
4142}
4143
4144
4145/*!
4146 Writes a document start with the XML version number \a version.
4147
4148 \note This function does not validate the version string and
4149 allows setting it manually. However, QXmlStreamWriter only
4150 supports XML 1.0. Setting a version string
4151 other than "1.0" does not change the writer's behavior or
4152 escaping rules. It is the caller's responsibility to ensure
4153 consistency between the declared version and the actual content.
4154
4155
4156 \note In Qt versions prior to 6.5, this function took QString, not
4157 QAnyStringView.
4158
4159 \sa writeEndDocument()
4160 */
4161void QXmlStreamWriter::writeStartDocument(QAnyStringView version)
4162{
4163 Q_D(QXmlStreamWriter);
4164 d->finishStartElement(false);
4165 d->write("<?xml version=\"");
4166 d->write(version);
4167 if (d->device) // stringDevice does not get any encoding
4168 d->write("\" encoding=\"UTF-8");
4169 d->write("\"?>");
4170 d->didWriteStartDocument = true;
4171}
4172
4173/*!
4174 \since 4.5
4175 Writes a document start with the XML version number \a version
4176 and a standalone attribute \a standalone.
4177
4178 \note This function does not validate the version string and
4179 allows setting it manually. However, QXmlStreamWriter only
4180 supports XML 1.0. Setting a version string
4181 other than "1.0" does not change the writer's behavior or
4182 escaping rules. It is the caller's responsibility to ensure
4183 consistency between the declared version and the actual content.
4184
4185
4186 \note In Qt versions prior to 6.5, this function took QString, not
4187 QAnyStringView.
4188
4189 \sa writeEndDocument()
4190 */
4191void QXmlStreamWriter::writeStartDocument(QAnyStringView version, bool standalone)
4192{
4193 Q_D(QXmlStreamWriter);
4194 d->finishStartElement(false);
4195 d->write("<?xml version=\"");
4196 d->write(version);
4197 if (d->device) // stringDevice does not get any encoding
4198 d->write("\" encoding=\"UTF-8");
4199 if (standalone)
4200 d->write("\" standalone=\"yes\"?>");
4201 else
4202 d->write("\" standalone=\"no\"?>");
4203 d->didWriteStartDocument = true;
4204}
4205
4206
4207/*!\overload
4208
4209 Writes a start element with \a qualifiedName. Subsequent calls to
4210 writeAttribute() will add attributes to this element.
4211
4212 \sa writeEndElement(), writeEmptyElement()
4213
4214 \note In Qt versions prior to 6.5, this function took QString, not
4215 QAnyStringView.
4216 */
4217void QXmlStreamWriter::writeStartElement(QAnyStringView qualifiedName)
4218{
4219 Q_D(QXmlStreamWriter);
4220 Q_ASSERT(count(qualifiedName, ':') <= 1);
4221 d->writeStartElement({}, qualifiedName);
4222}
4223
4224
4225/*! Writes a start element with \a name, prefixed for the specified
4226 \a namespaceUri. If the namespace has not been declared yet,
4227 QXmlStreamWriter will generate a namespace declaration for
4228 it. Subsequent calls to writeAttribute() will add attributes to this
4229 element.
4230
4231 \sa writeNamespace(), writeEndElement(), writeEmptyElement()
4232
4233 \note In Qt versions prior to 6.5, this function took QString, not
4234 QAnyStringView.
4235 */
4236void QXmlStreamWriter::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
4237{
4238 Q_D(QXmlStreamWriter);
4239 Q_ASSERT(!contains(name, ':'));
4240 d->writeStartElement(namespaceUri, name);
4241}
4242
4243void QXmlStreamWriterPrivate::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
4244 StartElementOption option)
4245{
4246 if (!finishStartElement(false) && autoFormatting)
4247 indent(tagStack.size());
4248
4249 Tag &tag = tagStack_push();
4250 tag.name = addToStringStorage(name);
4251 tag.namespaceDeclaration = findNamespace(namespaceUri);
4252 write("<");
4253 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
4254 write(tag.namespaceDeclaration.prefix);
4255 write(":");
4256 }
4257 write(tag.name);
4258 inStartElement = lastWasStartElement = true;
4259
4260 if (option != StartElementOption::OmitNamespaceDeclarations) {
4261 for (qsizetype i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i)
4262 writeNamespaceDeclaration(namespaceDeclarations[i]);
4263 }
4264 tag.namespaceDeclarationsSize = lastNamespaceDeclaration;
4265 didWriteAnyToken = true;
4266}
4267
4268#if QT_CONFIG(xmlstreamreader)
4269/*! Writes the current state of the \a reader. All possible valid
4270 states are supported.
4271
4272 The purpose of this function is to support chained processing of XML data.
4273
4274 \sa QXmlStreamReader::tokenType()
4275 */
4276void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
4277{
4278 Q_D(QXmlStreamWriter);
4279 switch (reader.tokenType()) {
4280 case QXmlStreamReader::NoToken:
4281 break;
4282 case QXmlStreamReader::StartDocument:
4283 writeStartDocument();
4284 break;
4285 case QXmlStreamReader::EndDocument:
4286 writeEndDocument();
4287 break;
4288 case QXmlStreamReader::StartElement: {
4289 // Namespaces must be added before writeStartElement is called so new prefixes are found
4290 QList<QXmlStreamPrivateTagStack::NamespaceDeclaration> extraNamespaces;
4291 const QXmlStreamNamespaceDeclarations nsDeclarations = reader.namespaceDeclarations();
4292 for (const auto &namespaceDeclaration : nsDeclarations) {
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