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