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 forever {
2461 switch (readNext()) {
2462 case Characters:
2463 case EntityReference:
2464 result.insert(result.size(), d->text);
2465 break;
2466 case EndElement:
2467 return result;
2468 case ProcessingInstruction:
2469 case Comment:
2470 break;
2471 case StartElement:
2472 if (behaviour == SkipChildElements) {
2473 skipCurrentElement();
2474 break;
2475 } else if (behaviour == IncludeChildElements) {
2476 result += readElementText(behaviour);
2477 break;
2478 }
2479 Q_FALLTHROUGH();
2480 default:
2481 if (d->error || behaviour == ErrorOnUnexpectedElement) {
2482 if (!d->error)
2483 d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data."));
2484 return result;
2485 }
2486 }
2487 }
2488 }
2489 return QString();
2490}
2491
2492/*! Raises a custom error with an optional error \a message.
2493
2494 \sa error(), errorString()
2495 */
2496void QXmlStreamReader::raiseError(const QString& message)
2497{
2498 Q_D(QXmlStreamReader);
2499 d->raiseError(CustomError, message);
2500}
2501
2502/*!
2503 Returns the error message that was set with raiseError().
2504
2505 \sa error(), lineNumber(), columnNumber(), characterOffset()
2506 */
2507QString QXmlStreamReader::errorString() const
2508{
2509 Q_D(const QXmlStreamReader);
2510 if (d->type == QXmlStreamReader::Invalid)
2511 return d->errorString;
2512 return QString();
2513}
2514
2515/*! Returns the type of the current error, or NoError if no error occurred.
2516
2517 \sa errorString(), raiseError()
2518 */
2519QXmlStreamReader::Error QXmlStreamReader::error() const
2520{
2521 Q_D(const QXmlStreamReader);
2522 if (d->type == QXmlStreamReader::Invalid)
2523 return d->error;
2524 return NoError;
2525}
2526
2527/*!
2528 Returns the target of a ProcessingInstruction.
2529 */
2530QStringView QXmlStreamReader::processingInstructionTarget() const
2531{
2532 Q_D(const QXmlStreamReader);
2533 return d->processingInstructionTarget;
2534}
2535
2536/*!
2537 Returns the data of a ProcessingInstruction.
2538 */
2539QStringView QXmlStreamReader::processingInstructionData() const
2540{
2541 Q_D(const QXmlStreamReader);
2542 return d->processingInstructionData;
2543}
2544
2545
2546
2547/*!
2548 Returns the local name of a StartElement, EndElement, or an EntityReference.
2549
2550 \sa namespaceUri(), qualifiedName()
2551 */
2552QStringView QXmlStreamReader::name() const
2553{
2554 Q_D(const QXmlStreamReader);
2555 return d->name;
2556}
2557
2558/*!
2559 Returns the namespaceUri of a StartElement or EndElement.
2560
2561 \sa name(), qualifiedName()
2562 */
2563QStringView QXmlStreamReader::namespaceUri() const
2564{
2565 Q_D(const QXmlStreamReader);
2566 return d->namespaceUri;
2567}
2568
2569/*!
2570 Returns the qualified name of a StartElement or EndElement;
2571
2572 A qualified name is the raw name of an element in the XML data. It
2573 consists of the namespace prefix, followed by colon, followed by the
2574 element's local name. Since the namespace prefix is not unique (the
2575 same prefix can point to different namespaces and different prefixes
2576 can point to the same namespace), you shouldn't use qualifiedName(),
2577 but the resolved namespaceUri() and the attribute's local name().
2578
2579 \sa name(), prefix(), namespaceUri()
2580 */
2581QStringView QXmlStreamReader::qualifiedName() const
2582{
2583 Q_D(const QXmlStreamReader);
2584 return d->qualifiedName;
2585}
2586
2587
2588
2589/*!
2590 \since 4.4
2591
2592 Returns the prefix of a StartElement or EndElement.
2593
2594 \sa name(), qualifiedName()
2595*/
2596QStringView QXmlStreamReader::prefix() const
2597{
2598 Q_D(const QXmlStreamReader);
2599 return d->prefix;
2600}
2601
2602/*!
2603 Returns the attributes of a StartElement.
2604 */
2605QXmlStreamAttributes QXmlStreamReader::attributes() const
2606{
2607 Q_D(const QXmlStreamReader);
2608 return d->attributes;
2609}
2610
2611#endif // feature xmlstreamreader
2612
2613/*!
2614 \class QXmlStreamAttribute
2615 \inmodule QtCore
2616 \since 4.3
2617 \reentrant
2618 \brief The QXmlStreamAttribute class represents a single XML attribute.
2619
2620 \ingroup xml-tools
2621
2622 \compares equality
2623
2624 An attribute consists of an optionally empty namespaceUri(), a
2625 name(), a value(), and an isDefault() attribute.
2626
2627 The raw XML attribute name is returned as qualifiedName().
2628*/
2629
2630/*!
2631 Creates an empty attribute.
2632 */
2633QXmlStreamAttribute::QXmlStreamAttribute()
2634{
2635 m_isDefault = false;
2636}
2637
2638/*! Constructs an attribute in the namespace described with \a
2639 namespaceUri with \a name and value \a value.
2640
2641 The attribute will have isDefault() == \c{false}.
2642 */
2643QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)
2644 : m_isDefault(false)
2645{
2646 m_namespaceUri = namespaceUri;
2647 m_name = m_qualifiedName = name;
2648 m_value = value;
2649}
2650
2651/*!
2652 Constructs an attribute with qualified name \a qualifiedName and value \a value.
2653
2654 The attribute will have isDefault() == \c{false}.
2655 */
2656QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value)
2657 : m_isDefault(false)
2658{
2659 qsizetype colon = qualifiedName.indexOf(u':');
2660 m_name = qualifiedName.mid(colon + 1);
2661 m_qualifiedName = qualifiedName;
2662 m_value = value;
2663}
2664
2665/*! \fn QStringView QXmlStreamAttribute::namespaceUri() const
2666
2667 Returns the attribute's resolved namespaceUri, or an empty string
2668 reference if the attribute does not have a defined namespace.
2669 */
2670/*! \fn QStringView QXmlStreamAttribute::name() const
2671 Returns the attribute's local name.
2672 */
2673/*! \fn QStringView QXmlStreamAttribute::qualifiedName() const
2674 Returns the attribute's qualified name.
2675
2676 A qualified name is the raw name of an attribute in the XML
2677 data. It consists of the namespace prefix(), followed by colon,
2678 followed by the attribute's local name(). Since the namespace prefix
2679 is not unique (the same prefix can point to different namespaces
2680 and different prefixes can point to the same namespace), you
2681 shouldn't use qualifiedName(), but the resolved namespaceUri() and
2682 the attribute's local name().
2683 */
2684/*!
2685 \fn QStringView QXmlStreamAttribute::prefix() const
2686 \since 4.4
2687 Returns the attribute's namespace prefix.
2688
2689 \sa name(), qualifiedName()
2690
2691*/
2692
2693/*! \fn QStringView QXmlStreamAttribute::value() const
2694 Returns the attribute's value.
2695 */
2696
2697/*! \fn bool QXmlStreamAttribute::isDefault() const
2698
2699 Returns \c true if the parser added this attribute with a default
2700 value following an ATTLIST declaration in the DTD; otherwise
2701 returns \c false.
2702*/
2703/*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &lhs, const QXmlStreamAttribute &rhs)
2704
2705 Compares \a lhs attribute with \a rhs and returns \c true if they are
2706 equal; otherwise returns \c false.
2707 */
2708/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &lhs, const QXmlStreamAttribute &rhs)
2709
2710 Compares \a lhs attribute with \a rhs and returns \c true if they are
2711 not equal; otherwise returns \c false.
2712 */
2713
2714/*!
2715 \class QXmlStreamAttributes
2716 \inmodule QtCore
2717 \since 4.3
2718 \reentrant
2719 \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute.
2720
2721 Attributes are returned by a QXmlStreamReader in
2722 \l{QXmlStreamReader::attributes()} {attributes()} when the reader
2723 reports a \l {QXmlStreamReader::StartElement}{start element}. The
2724 class can also be used with a QXmlStreamWriter as an argument to
2725 \l {QXmlStreamWriter::writeAttributes()}{writeAttributes()}.
2726
2727 The convenience function value() loops over the vector and returns
2728 an attribute value for a given namespaceUri and an attribute's
2729 name.
2730
2731 New attributes can be added with append().
2732
2733 \ingroup xml-tools
2734*/
2735
2736/*!
2737 \fn QXmlStreamAttributes::QXmlStreamAttributes()
2738
2739 A constructor for QXmlStreamAttributes.
2740*/
2741
2742/*!
2743 \typedef QXmlStreamNotationDeclarations
2744 \relates QXmlStreamNotationDeclaration
2745
2746 Synonym for QList<QXmlStreamNotationDeclaration>.
2747*/
2748
2749
2750/*!
2751 \class QXmlStreamNotationDeclaration
2752 \inmodule QtCore
2753 \since 4.3
2754 \reentrant
2755 \brief The QXmlStreamNotationDeclaration class represents a DTD notation declaration.
2756
2757 \ingroup xml-tools
2758
2759 \compares equality
2760
2761 An notation declaration consists of a name(), a systemId(), and a publicId().
2762*/
2763
2764/*!
2765 Creates an empty notation declaration.
2766*/
2767QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration()
2768{
2769}
2770
2771/*! \fn QStringView QXmlStreamNotationDeclaration::name() const
2772
2773Returns the notation name.
2774*/
2775/*! \fn QStringView QXmlStreamNotationDeclaration::systemId() const
2776
2777Returns the system identifier.
2778*/
2779/*! \fn QStringView QXmlStreamNotationDeclaration::publicId() const
2780
2781Returns the public identifier.
2782*/
2783
2784/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &lhs, const QXmlStreamNotationDeclaration &rhs)
2785
2786 Compares \a lhs notation declaration with \a rhs and returns \c true
2787 if they are equal; otherwise returns \c false.
2788 */
2789/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &lhs, const QXmlStreamNotationDeclaration &rhs)
2790
2791 Compares \a lhs notation declaration with \a rhs and returns \c true
2792 if they are not equal; otherwise returns \c false.
2793 */
2794
2795/*!
2796 \typedef QXmlStreamNamespaceDeclarations
2797 \relates QXmlStreamNamespaceDeclaration
2798
2799 Synonym for QList<QXmlStreamNamespaceDeclaration>.
2800*/
2801
2802/*!
2803 \class QXmlStreamNamespaceDeclaration
2804 \inmodule QtCore
2805 \since 4.3
2806 \reentrant
2807 \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration.
2808
2809 \ingroup xml-tools
2810
2811 \compares equality
2812
2813 An namespace declaration consists of a prefix() and a namespaceUri().
2814*/
2815/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &lhs, const QXmlStreamNamespaceDeclaration &rhs)
2816
2817 Compares \a lhs namespace declaration with \a rhs and returns \c true
2818 if they are equal; otherwise returns \c false.
2819 */
2820/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &lhs, const QXmlStreamNamespaceDeclaration &rhs)
2821
2822 Compares \a lhs namespace declaration with \a rhs and returns \c true
2823 if they are not equal; otherwise returns \c false.
2824 */
2825
2826/*!
2827 Creates an empty namespace declaration.
2828*/
2829QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration()
2830{
2831}
2832
2833/*!
2834 \since 4.4
2835
2836 Creates a namespace declaration with \a prefix and \a namespaceUri.
2837*/
2838QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri)
2839{
2840 m_prefix = prefix;
2841 m_namespaceUri = namespaceUri;
2842}
2843
2844/*! \fn QStringView QXmlStreamNamespaceDeclaration::prefix() const
2845
2846Returns the prefix.
2847*/
2848/*! \fn QStringView QXmlStreamNamespaceDeclaration::namespaceUri() const
2849
2850Returns the namespaceUri.
2851*/
2852
2853
2854
2855
2856/*!
2857 \typedef QXmlStreamEntityDeclarations
2858 \relates QXmlStreamEntityDeclaration
2859
2860 Synonym for QList<QXmlStreamEntityDeclaration>.
2861*/
2862
2863/*!
2864 \class QXmlString
2865 \inmodule QtCore
2866 \since 6.0
2867 \internal
2868*/
2869
2870/*!
2871 \class QXmlStreamEntityDeclaration
2872 \inmodule QtCore
2873 \since 4.3
2874 \reentrant
2875 \brief The QXmlStreamEntityDeclaration class represents a DTD entity declaration.
2876
2877 \ingroup xml-tools
2878
2879 \compares equality
2880 An entity declaration consists of a name(), a notationName(), a
2881 systemId(), a publicId(), and a value().
2882*/
2883
2884/*!
2885 Creates an empty entity declaration.
2886*/
2887QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration()
2888{
2889}
2890
2891/*! \fn QStringView QXmlStreamEntityDeclaration::name() const
2892
2893Returns the entity name.
2894*/
2895/*! \fn QStringView QXmlStreamEntityDeclaration::notationName() const
2896
2897Returns the notation name.
2898*/
2899/*! \fn QStringView QXmlStreamEntityDeclaration::systemId() const
2900
2901Returns the system identifier.
2902*/
2903/*! \fn QStringView QXmlStreamEntityDeclaration::publicId() const
2904
2905Returns the public identifier.
2906*/
2907/*! \fn QStringView QXmlStreamEntityDeclaration::value() const
2908
2909Returns the entity's value.
2910*/
2911
2912/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &lhs, const QXmlStreamEntityDeclaration &rhs)
2913
2914 Compares \a lhs entity declaration with \a rhs and returns \c true if
2915 they are equal; otherwise returns \c false.
2916 */
2917/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &lhs, const QXmlStreamEntityDeclaration &rhs)
2918
2919 Compares \a lhs entity declaration with \a rhs and returns \c true if
2920 they are not equal; otherwise returns \c false.
2921 */
2922
2923/*! Returns the value of the attribute \a name in the namespace
2924 described with \a namespaceUri, or an empty string reference if the
2925 attribute is not defined. The \a namespaceUri can be empty.
2926
2927 \note In Qt versions prior to 6.6, this function was implemented as an
2928 overload set accepting combinations of QString and QLatin1StringView only.
2929 */
2930QStringView QXmlStreamAttributes::value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
2931{
2932 for (const QXmlStreamAttribute &attribute : *this) {
2933 if (attribute.name() == name && attribute.namespaceUri() == namespaceUri)
2934 return attribute.value();
2935 }
2936 return QStringView();
2937}
2938
2939/*!\overload
2940
2941 Returns the value of the attribute with qualified name \a
2942 qualifiedName , or an empty string reference if the attribute is not
2943 defined. A qualified name is the raw name of an attribute in the XML
2944 data. It consists of the namespace prefix, followed by colon,
2945 followed by the attribute's local name. Since the namespace prefix
2946 is not unique (the same prefix can point to different namespaces and
2947 different prefixes can point to the same namespace), you shouldn't
2948 use qualified names, but a resolved namespaceUri and the attribute's
2949 local name.
2950
2951 \note In Qt versions prior to 6.6, this function was implemented as an
2952 overload set accepting QString and QLatin1StringView only.
2953
2954 */
2955QStringView QXmlStreamAttributes::value(QAnyStringView qualifiedName) const noexcept
2956{
2957 for (const QXmlStreamAttribute &attribute : *this) {
2958 if (attribute.qualifiedName() == qualifiedName)
2959 return attribute.value();
2960 }
2961 return QStringView();
2962}
2963
2964/*!Appends a new attribute with \a name in the namespace
2965 described with \a namespaceUri, and value \a value. The \a
2966 namespaceUri can be empty.
2967 */
2968void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value)
2969{
2970 append(QXmlStreamAttribute(namespaceUri, name, value));
2971}
2972
2973/*!\overload
2974 Appends a new attribute with qualified name \a qualifiedName and
2975 value \a value.
2976 */
2977void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value)
2978{
2979 append(QXmlStreamAttribute(qualifiedName, value));
2980}
2981
2982#if QT_CONFIG(xmlstreamreader)
2983
2984/*! \fn bool QXmlStreamReader::isStartDocument() const
2985 Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false.
2986*/
2987/*! \fn bool QXmlStreamReader::isEndDocument() const
2988 Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false.
2989*/
2990/*! \fn bool QXmlStreamReader::isStartElement() const
2991 Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false.
2992*/
2993/*! \fn bool QXmlStreamReader::isEndElement() const
2994 Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false.
2995*/
2996/*! \fn bool QXmlStreamReader::isCharacters() const
2997 Returns \c true if tokenType() equals \l Characters; otherwise returns \c false.
2998
2999 \sa isWhitespace(), isCDATA()
3000*/
3001/*! \fn bool QXmlStreamReader::isComment() const
3002 Returns \c true if tokenType() equals \l Comment; otherwise returns \c false.
3003*/
3004/*! \fn bool QXmlStreamReader::isDTD() const
3005 Returns \c true if tokenType() equals \l DTD; otherwise returns \c false.
3006*/
3007/*! \fn bool QXmlStreamReader::isEntityReference() const
3008 Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false.
3009*/
3010/*! \fn bool QXmlStreamReader::isProcessingInstruction() const
3011 Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false.
3012*/
3013
3014/*! Returns \c true if the reader reports characters that only consist
3015 of white-space; otherwise returns \c false.
3016
3017 \sa isCharacters(), text()
3018*/
3019bool QXmlStreamReader::isWhitespace() const
3020{
3021 Q_D(const QXmlStreamReader);
3022 return d->type == QXmlStreamReader::Characters && d->isWhitespace;
3023}
3024
3025/*! Returns \c true if the reader reports characters that stem from a
3026 CDATA section; otherwise returns \c false.
3027
3028 \sa isCharacters(), text()
3029*/
3030bool QXmlStreamReader::isCDATA() const
3031{
3032 Q_D(const QXmlStreamReader);
3033 return d->type == QXmlStreamReader::Characters && d->isCDATA;
3034}
3035
3036
3037
3038/*!
3039 Returns \c true if this document has been declared standalone in the
3040 XML declaration; otherwise returns \c false.
3041
3042 If no XML declaration has been parsed, this function returns \c false.
3043
3044 \sa hasStandaloneDeclaration()
3045 */
3046bool QXmlStreamReader::isStandaloneDocument() const
3047{
3048 Q_D(const QXmlStreamReader);
3049 return d->standalone;
3050}
3051
3052/*!
3053 \since 6.6
3054
3055 Returns \c true if this document has an explicit standalone
3056 declaration (can be 'yes' or 'no'); otherwise returns \c false;
3057
3058 If no XML declaration has been parsed, this function returns \c false.
3059
3060 \sa isStandaloneDocument()
3061 */
3062bool QXmlStreamReader::hasStandaloneDeclaration() const
3063{
3064 Q_D(const QXmlStreamReader);
3065 return d->hasStandalone;
3066}
3067
3068/*!
3069 \since 4.4
3070
3071 If the tokenType() is \l StartDocument, this function returns the
3072 version string as specified in the XML declaration.
3073 Otherwise an empty string is returned.
3074 */
3075QStringView QXmlStreamReader::documentVersion() const
3076{
3077 Q_D(const QXmlStreamReader);
3078 if (d->type == QXmlStreamReader::StartDocument)
3079 return d->documentVersion;
3080 return QStringView();
3081}
3082
3083/*!
3084 \since 4.4
3085
3086 If the tokenType() is \l StartDocument, this function returns the
3087 encoding string as specified in the XML declaration.
3088 Otherwise an empty string is returned.
3089 */
3090QStringView QXmlStreamReader::documentEncoding() const
3091{
3092 Q_D(const QXmlStreamReader);
3093 if (d->type == QXmlStreamReader::StartDocument)
3094 return d->documentEncoding;
3095 return QStringView();
3096}
3097
3098#endif // feature xmlstreamreader
3099
3100/*!
3101 \class QXmlStreamWriter
3102 \inmodule QtCore
3103 \since 4.3
3104 \reentrant
3105
3106 \brief The QXmlStreamWriter class provides an XML 1.0 writer with a
3107 simple streaming API.
3108
3109 \ingroup xml-tools
3110 \ingroup qtserialization
3111
3112 QXmlStreamWriter is the counterpart to QXmlStreamReader for writing
3113 XML.
3114 It is compliant with the XML 1.0 specification and writes documents
3115 using XML 1.0 syntax, escaping rules, and character validity
3116 constraints.
3117 \note XML 1.1 is not supported. While version strings may be set
3118 manually in the output, documents requiring features specific to
3119 XML 1.1, such as additional control characters cannot be produced
3120 using this class.
3121
3122 Like its related class, it operates on a QIODevice specified
3123 with setDevice(). The API is simple and straightforward: for every
3124 XML token or event you want to write, the writer provides a
3125 specialized function.
3126
3127 You start a document with writeStartDocument() and end it with
3128 writeEndDocument(). This will implicitly close all remaining open
3129 tags.
3130
3131 Element tags are opened with writeStartElement() followed by
3132 writeAttribute() or writeAttributes(), element content, and then
3133 writeEndElement(). A shorter form writeEmptyElement() can be used
3134 to write empty elements, followed by writeAttributes().
3135
3136 Element content consists of either characters, entity references or
3137 nested elements. It is written with writeCharacters(), which also
3138 takes care of escaping all forbidden characters and character
3139 sequences, writeEntityReference(), or subsequent calls to
3140 writeStartElement(). A convenience method writeTextElement() can be
3141 used for writing terminal elements that contain nothing but text.
3142
3143 The following abridged code snippet shows the basic use of the class
3144 to write formatted XML with indentation:
3145
3146 \snippet qxmlstreamwriter/main.cpp start stream
3147 \dots
3148 \snippet qxmlstreamwriter/main.cpp write element
3149 \dots
3150 \snippet qxmlstreamwriter/main.cpp finish stream
3151
3152 QXmlStreamWriter takes care of prefixing namespaces, all you have to
3153 do is specify the \c namespaceUri when writing elements or
3154 attributes. If you must conform to certain prefixes, you can force
3155 the writer to use them by declaring the namespaces manually with
3156 either writeNamespace() or writeDefaultNamespace(). Alternatively,
3157 you can bypass the stream writer's namespace support and use
3158 overloaded methods that take a qualified name instead. The namespace
3159 \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the
3160 prefix \e xml.
3161
3162 The stream writer can automatically format the generated XML data by
3163 adding line-breaks and indentation to empty sections between
3164 elements, making the XML data more readable for humans and easier to
3165 work with for most source code management systems. The feature can
3166 be turned on with the \l autoFormatting property, and customized
3167 with the \l autoFormattingIndent property.
3168
3169 Other functions are writeCDATA(), writeComment(),
3170 writeProcessingInstruction(), and writeDTD(). Chaining of XML
3171 streams is supported with writeCurrentToken().
3172
3173 QXmlStreamWriter always encodes XML in UTF-8.
3174
3175 If an error occurs while writing, \l hasError() will return true.
3176 However, by default, data that was already buffered at the time the error
3177 occurred, or data written from within the same operation, may still be
3178 written to the underlying device. This applies to \l Error::Encoding,
3179 \l Error::InvalidCharacter, and user-raised \l Error::Custom.
3180 To avoid this and ensure no data is written after an error, use the
3181 \l stopWritingOnError property. When this property is enabled,
3182 the first error stops output immediately and the writer ignores all
3183 subsequent write operations.
3184 Applications should treat the error state as terminal and avoid further
3185 use of the writer after an error.
3186
3187 The \l{QXmlStream Bookmarks Example} illustrates how to use a
3188 stream writer to write an XML bookmark file (XBEL) that
3189 was previously read in by a QXmlStreamReader.
3190
3191*/
3192
3193/*!
3194 \enum QXmlStreamWriter::Error
3195
3196 This enum specifies the different error cases that can occur
3197 when writing XML with QXmlStreamWriter.
3198
3199 \value None No error has occurred.
3200
3201 \value IO An I/O error occurred while writing to the
3202 device.
3203
3204 \value Encoding An encoding error occurred while converting
3205 characters to the output format.
3206
3207 \value InvalidCharacter A character not permitted in XML 1.0
3208 was encountered while writing.
3209
3210 \value Custom A custom error has been raised with
3211 \l raiseError().
3212
3213 \since 6.10
3214*/
3215
3216#if QT_CONFIG(xmlstreamwriter)
3217
3218class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
3219{
3220 QXmlStreamWriter *q_ptr;
3221 Q_DECLARE_PUBLIC(QXmlStreamWriter)
3222public:
3223 enum class StartElementOption {
3224 KeepEverything = 0, // write out every attribute, namespace, &c.
3225 OmitNamespaceDeclarations = 1,
3226 };
3227
3228 QXmlStreamWriterPrivate(QXmlStreamWriter *q);
3229 ~QXmlStreamWriterPrivate() {
3230 if (deleteDevice)
3231 delete device;
3232 }
3233
3234 void raiseError(QXmlStreamWriter::Error error);
3235 void raiseError(QXmlStreamWriter::Error error, QAnyStringView message);
3236 void write(QAnyStringView s);
3237 void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
3238 bool finishStartElement(bool contents = true);
3239 void writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
3240 StartElementOption option = StartElementOption::KeepEverything);
3241 QIODevice *device = nullptr;
3242 QString *stringDevice = nullptr;
3243 uint deleteDevice :1;
3244 uint inStartElement :1;
3245 uint inEmptyElement :1;
3246 uint lastWasStartElement :1;
3247 uint wroteSomething :1;
3248 uint autoFormatting :1;
3249 uint didWriteStartDocument :1;
3250 uint didWriteAnyToken :1;
3251 uint stopWritingOnError :1;
3252 std::string autoFormattingIndent = std::string(4, ' ');
3253 NamespaceDeclaration emptyNamespace;
3254 qsizetype lastNamespaceDeclaration = 1;
3255 QXmlStreamWriter::Error error = QXmlStreamWriter::Error::None;
3256 QString errorString;
3257
3258 NamespaceDeclaration &addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix);
3259 NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
3260 void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration);
3261
3262 int namespacePrefixCount = 0;
3263
3264 void indent(int level);
3265private:
3266 void doWriteToDevice(QStringView s);
3267 void doWriteToDevice(QUtf8StringView s);
3268 void doWriteToDevice(QLatin1StringView s);
3269};
3270
3271
3272QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
3273 : q_ptr(q), deleteDevice(false), inStartElement(false),
3274 inEmptyElement(false), lastWasStartElement(false),
3275 wroteSomething(false), autoFormatting(false),
3276 didWriteStartDocument(false), didWriteAnyToken(false),
3277 stopWritingOnError(false)
3278{
3279}
3280
3281void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode)
3282{
3283 error = errorCode;
3284 switch (error) {
3285 case QXmlStreamWriter::Error::IO:
3286 errorString = QXmlStream::tr("An I/O error occurred while writing");
3287 break;
3288 case QXmlStreamWriter::Error::Encoding:
3289 errorString = QXmlStream::tr("An encoding error occurred while writing");
3290 break;
3291 case QXmlStreamWriter::Error::InvalidCharacter:
3292 errorString = QXmlStream::tr("Encountered an invalid XML 1.0 character while writing");
3293 break;
3294 case QXmlStreamWriter::Error::Custom:
3295 errorString = QXmlStream::tr("An error occurred while writing");
3296 break;
3297 case QXmlStreamWriter::Error::None:
3298 errorString.clear();
3299 break;
3300 }
3301}
3302
3303void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode, QAnyStringView message)
3304{
3305 error = errorCode;
3306 errorString = message.toString();
3307}
3308
3309void QXmlStreamWriterPrivate::write(QAnyStringView s)
3310{
3311 if (stopWritingOnError && (error != QXmlStreamWriter::Error::None))
3312 return;
3313 if (device) {
3314 if (error == QXmlStreamWriter::Error::IO)
3315 return;
3316
3317 s.visit([&] (auto s) { doWriteToDevice(s); });
3318 } else if (stringDevice) {
3319 s.visit([&] (auto s) { stringDevice->append(s); });
3320 } else {
3321 qWarning("QXmlStreamWriter: No device");
3322 }
3323}
3324
3325void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
3326{
3327 struct NextResult {
3328 char32_t value;
3329 bool encodingError;
3330 };
3331 struct NextLatin1 {
3332 NextResult operator()(const char *&it, const char *) const
3333 { return {uchar(*it++), false}; }
3334 };
3335 struct NextUtf8 {
3336 NextResult operator()(const char *&it, const char *end) const
3337 {
3338 // We can have '\0' in the text, and it should be reported as
3339 // Error::InvalidCharacter, not as Error::Encoding
3340 constexpr char32_t invalidValue = 0xFFFFFFFF;
3341 static_assert(invalidValue > QChar::LastValidCodePoint);
3342 auto i = reinterpret_cast<const qchar8_t *>(it);
3343 const auto old_i = i;
3344 const auto e = reinterpret_cast<const qchar8_t *>(end);
3345 const char32_t result = QUtf8Functions::nextUcs4FromUtf8(i, e, invalidValue);
3346 it += i - old_i;
3347 return result == invalidValue ? NextResult{U'\0', true}
3348 : NextResult{result, false};
3349 }
3350 };
3351 struct NextUtf16 {
3352 NextResult operator()(const QChar *&it, const QChar *end) const
3353 {
3354 QStringIterator decoder(it, end);
3355 // We can have '\0' in the text, and it should be reported as
3356 // Error::InvalidCharacter, not as Error::Encoding
3357 constexpr char32_t invalidValue = 0xFFFFFFFF;
3358 static_assert(invalidValue > QChar::LastValidCodePoint);
3359 char32_t result = decoder.next(invalidValue);
3360 it = decoder.position();
3361 return result == invalidValue ? NextResult{U'\0', true}
3362 : NextResult{result, false};
3363 }
3364 };
3365
3366 QString escaped;
3367 escaped.reserve(s.size());
3368 s.visit([&] (auto s) {
3369 using View = decltype(s);
3370 using Decoder = std::conditional_t<std::is_same_v<View, QLatin1StringView>, NextLatin1,
3371 std::conditional_t<std::is_same_v<View, QUtf8StringView>, NextUtf8, NextUtf16>>;
3372
3373 auto it = s.begin();
3374 const auto end = s.end();
3375 Decoder decoder;
3376
3377 while (it != end) {
3378 QLatin1StringView replacement;
3379 auto mark = it;
3380
3381 while (it != end) {
3382 auto next_it = it;
3383 const auto decoded = decoder(next_it, end);
3384 switch (decoded.value) {
3385 case u'<':
3386 replacement = "&lt;"_L1;
3387 break;
3388 case u'>':
3389 replacement = "&gt;"_L1;
3390 break;
3391 case u'&':
3392 replacement = "&amp;"_L1;
3393 break;
3394 case u'\"':
3395 replacement = "&quot;"_L1;
3396 break;
3397 case u'\t':
3398 if (escapeWhitespace)
3399 replacement = "&#9;"_L1;
3400 break;
3401 case u'\n':
3402 if (escapeWhitespace)
3403 replacement = "&#10;"_L1;
3404 break;
3405 case u'\r':
3406 if (escapeWhitespace)
3407 replacement = "&#13;"_L1;
3408 break;
3409 case u'\v':
3410 case u'\f':
3411 raiseError(QXmlStreamWriter::Error::InvalidCharacter);
3412 if (stopWritingOnError)
3413 return;
3414 replacement = ""_L1;
3415 Q_ASSERT(!replacement.isNull());
3416 break;
3417 default:
3418 if (decoded.value > 0x1F)
3419 break;
3420 // ASCII control characters
3421 Q_FALLTHROUGH();
3422 case 0xFFFE:
3423 case 0xFFFF:
3424 raiseError(decoded.encodingError
3425 ? QXmlStreamWriter::Error::Encoding
3426 : QXmlStreamWriter::Error::InvalidCharacter);
3427 if (stopWritingOnError)
3428 return;
3429 replacement = ""_L1;
3430 Q_ASSERT(!replacement.isNull());
3431 break;
3432 }
3433 if (!replacement.isNull())
3434 break;
3435 it = next_it;
3436 }
3437
3438 escaped.append(View{mark, it});
3439 escaped.append(replacement);
3440 if (it != end)
3441 ++it;
3442 }
3443 } );
3444
3445 write(escaped);
3446}
3447
3448void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) {
3449 if (namespaceDeclaration.prefix.isEmpty()) {
3450 write(" xmlns=\"");
3451 write(namespaceDeclaration.namespaceUri);
3452 write("\"");
3453 } else {
3454 write(" xmlns:");
3455 write(namespaceDeclaration.prefix);
3456 write("=\"");
3457 write(namespaceDeclaration.namespaceUri);
3458 write("\"");
3459 }
3460 didWriteAnyToken = true;
3461}
3462
3463bool QXmlStreamWriterPrivate::finishStartElement(bool contents)
3464{
3465 bool hadSomethingWritten = wroteSomething;
3466 wroteSomething = contents;
3467 if (!inStartElement)
3468 return hadSomethingWritten;
3469
3470 if (inEmptyElement) {
3471 write("/>");
3472 QXmlStreamWriterPrivate::Tag tag = tagStack_pop();
3473 lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3474 lastWasStartElement = false;
3475 } else {
3476 write(">");
3477 }
3478 inStartElement = inEmptyElement = false;
3479 lastNamespaceDeclaration = namespaceDeclarations.size();
3480 didWriteAnyToken = true;
3481 return hadSomethingWritten;
3482}
3483
3484QXmlStreamPrivateTagStack::NamespaceDeclaration &
3485QXmlStreamWriterPrivate::addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
3486{
3487 const bool prefixIsXml = prefix == "xml"_L1;
3488 const bool namespaceUriIsXml = namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1;
3489 if (prefixIsXml && !namespaceUriIsXml) {
3490 qWarning("Reserved prefix 'xml' must not be bound to a different namespace name "
3491 "than 'http://www.w3.org/XML/1998/namespace'");
3492 } else if (!prefixIsXml && namespaceUriIsXml) {
3493 const QString prefixString = prefix.toString();
3494 qWarning("The prefix '%ls' must not be bound to namespace name "
3495 "'http://www.w3.org/XML/1998/namespace' which 'xml' is already bound to",
3496 qUtf16Printable(prefixString));
3497 }
3498 if (namespaceUri == "http://www.w3.org/2000/xmlns/"_L1) {
3499 const QString prefixString = prefix.toString();
3500 qWarning("The prefix '%ls' must not be bound to namespace name "
3501 "'http://www.w3.org/2000/xmlns/'",
3502 qUtf16Printable(prefixString));
3503 }
3504 auto &namespaceDeclaration = namespaceDeclarations.push();
3505 namespaceDeclaration.prefix = addToStringStorage(prefix);
3506 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3507 return namespaceDeclaration;
3508}
3509
3510QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(QAnyStringView namespaceUri, bool writeDeclaration, bool noDefault)
3511{
3512 for (NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
3513 if (namespaceDeclaration.namespaceUri == namespaceUri) {
3514 if (!noDefault || !namespaceDeclaration.prefix.isEmpty())
3515 return namespaceDeclaration;
3516 }
3517 }
3518 if (namespaceUri.isEmpty())
3519 return emptyNamespace;
3520 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
3521 if (namespaceUri.isEmpty()) {
3522 namespaceDeclaration.prefix.clear();
3523 } else {
3524 QString s;
3525 int n = ++namespacePrefixCount;
3526 forever {
3527 s = u'n' + QString::number(n++);
3528 qsizetype j = namespaceDeclarations.size() - 2;
3529 while (j >= 0 && namespaceDeclarations.at(j).prefix != s)
3530 --j;
3531 if (j < 0)
3532 break;
3533 }
3534 namespaceDeclaration.prefix = addToStringStorage(s);
3535 }
3536 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3537 if (writeDeclaration)
3538 writeNamespaceDeclaration(namespaceDeclaration);
3539 return namespaceDeclaration;
3540}
3541
3542
3543
3544void QXmlStreamWriterPrivate::indent(int level)
3545{
3546 if (didWriteStartDocument || didWriteAnyToken)
3547 write("\n");
3548 for (int i = 0; i < level; ++i)
3549 write(autoFormattingIndent);
3550}
3551
3552void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
3553{
3554 constexpr qsizetype MaxChunkSize = 512;
3555 char buffer [3 * MaxChunkSize];
3556 QStringEncoder::State state;
3557 while (!s.isEmpty()) {
3558 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3559 char *end = QUtf8::convertFromUnicode(buffer, s.first(chunkSize), &state);
3560 doWriteToDevice(QUtf8StringView{buffer, end});
3561 s = s.sliced(chunkSize);
3562 }
3563 if (state.remainingChars > 0)
3564 raiseError(QXmlStreamWriter::Error::Encoding);
3565}
3566
3567void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
3568{
3569 QByteArrayView bytes = s;
3570 if (device->write(bytes.data(), bytes.size()) != bytes.size())
3571 raiseError(QXmlStreamWriter::Error::IO);
3572}
3573
3574void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
3575{
3576 constexpr qsizetype MaxChunkSize = 512;
3577 char buffer [2 * MaxChunkSize];
3578 while (!s.isEmpty()) {
3579 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3580 char *end = QUtf8::convertFromLatin1(buffer, s.first(chunkSize));
3581 doWriteToDevice(QUtf8StringView{buffer, end});
3582 s = s.sliced(chunkSize);
3583 }
3584}
3585
3586/*!
3587 Constructs a stream writer.
3588
3589 \sa setDevice()
3590 */
3591QXmlStreamWriter::QXmlStreamWriter()
3592 : d_ptr(new QXmlStreamWriterPrivate(this))
3593{
3594}
3595
3596/*!
3597 Constructs a stream writer that writes into \a device;
3598 */
3599QXmlStreamWriter::QXmlStreamWriter(QIODevice *device)
3600 : d_ptr(new QXmlStreamWriterPrivate(this))
3601{
3602 Q_D(QXmlStreamWriter);
3603 d->device = device;
3604}
3605
3606/*! Constructs a stream writer that writes into \a array. This is the
3607 same as creating an xml writer that operates on a QBuffer device
3608 which in turn operates on \a array.
3609 */
3610QXmlStreamWriter::QXmlStreamWriter(QByteArray *array)
3611 : d_ptr(new QXmlStreamWriterPrivate(this))
3612{
3613 Q_D(QXmlStreamWriter);
3614 d->device = new QBuffer(array);
3615 d->device->open(QIODevice::WriteOnly);
3616 d->deleteDevice = true;
3617}
3618
3619
3620/*! Constructs a stream writer that writes into \a string.
3621 */
3622QXmlStreamWriter::QXmlStreamWriter(QString *string)
3623 : d_ptr(new QXmlStreamWriterPrivate(this))
3624{
3625 Q_D(QXmlStreamWriter);
3626 d->stringDevice = string;
3627}
3628
3629/*!
3630 Destructor.
3631*/
3632QXmlStreamWriter::~QXmlStreamWriter()
3633{
3634}
3635
3636
3637/*!
3638 Sets the current device to \a device. If you want the stream to
3639 write into a QByteArray, you can create a QBuffer device.
3640
3641 \sa device()
3642*/
3643void QXmlStreamWriter::setDevice(QIODevice *device)
3644{
3645 Q_D(QXmlStreamWriter);
3646 if (device == d->device)
3647 return;
3648 d->stringDevice = nullptr;
3649 if (d->deleteDevice) {
3650 delete d->device;
3651 d->deleteDevice = false;
3652 }
3653 d->device = device;
3654}
3655
3656/*!
3657 Returns the current device associated with the QXmlStreamWriter,
3658 or \nullptr if no device has been assigned.
3659
3660 \sa setDevice()
3661*/
3662QIODevice *QXmlStreamWriter::device() const
3663{
3664 Q_D(const QXmlStreamWriter);
3665 return d->device;
3666}
3667
3668/*!
3669 \property QXmlStreamWriter::autoFormatting
3670 \since 4.4
3671 \brief the auto-formatting flag of the stream writer.
3672
3673 This property controls whether or not the stream writer
3674 automatically formats the generated XML data. If enabled, the
3675 writer automatically adds line-breaks and indentation to empty
3676 sections between elements (ignorable whitespace). The main purpose
3677 of auto-formatting is to split the data into several lines, and to
3678 increase readability for a human reader. The indentation depth can
3679 be controlled through the \l autoFormattingIndent property.
3680
3681 By default, auto-formatting is disabled.
3682*/
3683
3684/*!
3685 \since 4.4
3686
3687 Enables auto formatting if \a enable is \c true, otherwise
3688 disables it.
3689
3690 The default value is \c false.
3691 */
3692void QXmlStreamWriter::setAutoFormatting(bool enable)
3693{
3694 Q_D(QXmlStreamWriter);
3695 d->autoFormatting = enable;
3696}
3697
3698/*!
3699 \since 4.4
3700
3701 Returns \c true if auto formatting is enabled, otherwise \c false.
3702 */
3703bool QXmlStreamWriter::autoFormatting() const
3704{
3705 Q_D(const QXmlStreamWriter);
3706 return d->autoFormatting;
3707}
3708
3709/*!
3710 \property QXmlStreamWriter::autoFormattingIndent
3711 \since 4.4
3712
3713 \brief the number of spaces or tabs used for indentation when
3714 auto-formatting is enabled. Positive numbers indicate spaces,
3715 negative numbers tabs.
3716
3717 The default indentation is 4.
3718
3719 \sa autoFormatting
3720*/
3721
3722
3723void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs)
3724{
3725 Q_D(QXmlStreamWriter);
3726 d->autoFormattingIndent.assign(size_t(qAbs(spacesOrTabs)), spacesOrTabs >= 0 ? ' ' : '\t');
3727}
3728
3729int QXmlStreamWriter::autoFormattingIndent() const
3730{
3731 Q_D(const QXmlStreamWriter);
3732 const QLatin1StringView indent(d->autoFormattingIndent);
3733 return indent.count(u' ') - indent.count(u'\t');
3734}
3735
3736/*!
3737 \property QXmlStreamWriter::stopWritingOnError
3738 \since 6.10
3739
3740 \brief The option to stop writing to the device after encountering an error.
3741
3742 If this property is set to \c true, the writer stops writing immediately upon
3743 encountering any error and ignores all subsequent write operations.
3744 When this property is set to \c false, the writer may continue writing
3745 after an error, skipping the invalid write but allowing further output.
3746
3747 Note that this includes \l Error::InvalidCharacter, \l Error::Encoding,
3748 and \l Error::Custom. \l Error::IO is always considered terminal
3749 and stops writing regardless of this setting.
3750
3751 The default value is \c false.
3752 */
3753bool QXmlStreamWriter::stopWritingOnError() const
3754{
3755 Q_D(const QXmlStreamWriter);
3756 return d->stopWritingOnError;
3757}
3758
3759void QXmlStreamWriter::setStopWritingOnError(bool stop)
3760{
3761 Q_D(QXmlStreamWriter);
3762 d->stopWritingOnError = stop;
3763}
3764
3765/*!
3766 Returns \c true if an error occurred while trying to write data.
3767
3768 If the error is \l Error::IO, subsequent writes to the underlying
3769 QIODevice will fail. In other cases malformed data might be written to
3770 the document.
3771
3772 The error status is never reset. Writes happening after the error
3773 occurred may be ignored, even if the error condition is cleared.
3774
3775 \sa error(), errorString(), raiseError()
3776 */
3777bool QXmlStreamWriter::hasError() const
3778{
3779 return error() != QXmlStreamWriter::Error::None;
3780}
3781
3782/*!
3783 Returns the current error state of the writer.
3784
3785 If no error has occurred, this function returns
3786 QXmlStreamWriter::Error::None.
3787
3788 \since 6.10
3789 \sa errorString(), raiseError(), hasError()
3790 */
3791QXmlStreamWriter::Error QXmlStreamWriter::error() const
3792{
3793 Q_D(const QXmlStreamWriter);
3794 return d->error;
3795}
3796
3797/*!
3798 If an error has occurred, returns its associated error message.
3799
3800 The error message is either set internally by QXmlStreamWriter or provided
3801 by the user via raiseError(). If no error has occured, this function returns
3802 a null string.
3803
3804 \since 6.10
3805 \sa error(), raiseError(), hasError()
3806 */
3807QString QXmlStreamWriter::errorString() const
3808{
3809 Q_D(const QXmlStreamWriter);
3810 return d->errorString;
3811}
3812
3813/*!
3814 Raises a custom error with the given \a message.
3815
3816 This function is for manual indication that an error has occurred during
3817 writing, such as an application level validation failure.
3818
3819 \since 6.10
3820 \sa errorString(), error(), hasError()
3821 */
3822void QXmlStreamWriter::raiseError(QAnyStringView message)
3823{
3824 Q_D(QXmlStreamWriter);
3825 d->raiseError(QXmlStreamWriter::Error::Custom, message);
3826}
3827
3828/*!
3829 \overload
3830 Writes an attribute with \a qualifiedName and \a value.
3831
3832
3833 This function can only be called after writeStartElement() before
3834 any content is written, or after writeEmptyElement().
3835
3836 \note In Qt versions prior to 6.5, this function took QString, not
3837 QAnyStringView.
3838 */
3839void QXmlStreamWriter::writeAttribute(QAnyStringView qualifiedName, QAnyStringView value)
3840{
3841 Q_D(QXmlStreamWriter);
3842 Q_ASSERT(d->inStartElement);
3843 Q_ASSERT(count(qualifiedName, ':') <= 1);
3844 d->write(" ");
3845 d->write(qualifiedName);
3846 d->write("=\"");
3847 d->writeEscaped(value, true);
3848 d->write("\"");
3849 d->didWriteAnyToken = true;
3850}
3851
3852/*! Writes an attribute with \a name and \a value, prefixed for
3853 the specified \a namespaceUri. If the namespace has not been
3854 declared yet, QXmlStreamWriter will generate a namespace declaration
3855 for it.
3856
3857 This function can only be called after writeStartElement() before
3858 any content is written, or after writeEmptyElement().
3859
3860 \note In Qt versions prior to 6.5, this function took QString, not
3861 QAnyStringView.
3862 */
3863void QXmlStreamWriter::writeAttribute(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView value)
3864{
3865 Q_D(QXmlStreamWriter);
3866 Q_ASSERT(d->inStartElement);
3867 Q_ASSERT(!contains(name, ':'));
3868 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true);
3869 d->write(" ");
3870 if (!namespaceDeclaration.prefix.isEmpty()) {
3871 d->write(namespaceDeclaration.prefix);
3872 d->write(":");
3873 }
3874 d->write(name);
3875 d->write("=\"");
3876 d->writeEscaped(value, true);
3877 d->write("\"");
3878 d->didWriteAnyToken = true;
3879}
3880
3881/*!
3882 \overload
3883
3884 Writes the \a attribute.
3885
3886 This function can only be called after writeStartElement() before
3887 any content is written, or after writeEmptyElement().
3888 */
3889void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute)
3890{
3891 if (attribute.namespaceUri().isEmpty())
3892 writeAttribute(attribute.qualifiedName(), attribute.value());
3893 else
3894 writeAttribute(attribute.namespaceUri(), attribute.name(), attribute.value());
3895}
3896
3897
3898/*! Writes the attribute vector \a attributes. If a namespace
3899 referenced in an attribute not been declared yet, QXmlStreamWriter
3900 will generate a namespace declaration for it.
3901
3902 This function can only be called after writeStartElement() before
3903 any content is written, or after writeEmptyElement().
3904
3905 \sa writeAttribute(), writeNamespace()
3906 */
3907void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
3908{
3909 Q_D(QXmlStreamWriter);
3910 Q_ASSERT(d->inStartElement);
3911 Q_UNUSED(d);
3912 for (const auto &attr : attributes)
3913 writeAttribute(attr);
3914}
3915
3916
3917/*! Writes \a text as CDATA section. If \a text contains the
3918 forbidden character sequence "]]>", it is split into different CDATA
3919 sections.
3920
3921 This function mainly exists for completeness. Normally you should
3922 not need use it, because writeCharacters() automatically escapes all
3923 non-content characters.
3924
3925 \note In Qt versions prior to 6.5, this function took QString, not
3926 QAnyStringView.
3927 */
3928void QXmlStreamWriter::writeCDATA(QAnyStringView text)
3929{
3930 Q_D(QXmlStreamWriter);
3931 d->finishStartElement();
3932 d->write("<![CDATA[");
3933 while (!text.isEmpty()) {
3934 const auto idx = indexOf(text, "]]>"_L1);
3935 if (idx < 0)
3936 break; // no forbidden sequence found
3937 d->write(text.first(idx));
3938 d->write("]]" // text[idx, idx + 2)
3939 "]]><![CDATA[" // escape sequence to separate ]] and >
3940 ">"); // text[idx + 2, idx + 3)
3941 text = text.sliced(idx + 3); // skip over "]]>"
3942 }
3943 d->write(text); // write remainder
3944 d->write("]]>");
3945}
3946
3947
3948/*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity
3949 references "&lt;", "&amp;, and "&quot;". To avoid the forbidden sequence
3950 "]]>", ">" is also escaped as "&gt;".
3951
3952 \sa writeEntityReference()
3953
3954 \note In Qt versions prior to 6.5, this function took QString, not
3955 QAnyStringView.
3956 */
3957void QXmlStreamWriter::writeCharacters(QAnyStringView text)
3958{
3959 Q_D(QXmlStreamWriter);
3960 d->finishStartElement();
3961 d->writeEscaped(text);
3962}
3963
3964
3965/*! Writes \a text as XML comment, where \a text must not contain the
3966 forbidden sequence \c{--} or end with \c{-}. Note that XML does not
3967 provide any way to escape \c{-} in a comment.
3968
3969 \note In Qt versions prior to 6.5, this function took QString, not
3970 QAnyStringView.
3971 */
3972void QXmlStreamWriter::writeComment(QAnyStringView text)
3973{
3974 Q_D(QXmlStreamWriter);
3975 Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
3976 if (!d->finishStartElement(false) && d->autoFormatting)
3977 d->indent(d->tagStack.size());
3978 d->write("<!--");
3979 d->write(text);
3980 d->write("-->");
3981 d->inStartElement = d->lastWasStartElement = false;
3982}
3983
3984
3985/*! Writes a DTD section. The \a dtd represents the entire
3986 doctypedecl production from the XML 1.0 specification.
3987
3988 \note In Qt versions prior to 6.5, this function took QString, not
3989 QAnyStringView.
3990 */
3991void QXmlStreamWriter::writeDTD(QAnyStringView dtd)
3992{
3993 Q_D(QXmlStreamWriter);
3994 d->finishStartElement();
3995 if (d->autoFormatting)
3996 d->write("\n");
3997 d->write(dtd);
3998 if (d->autoFormatting)
3999 d->write("\n");
4000}
4001
4002
4003
4004/*! \overload
4005 Writes an empty element with qualified name \a qualifiedName.
4006 Subsequent calls to writeAttribute() will add attributes to this element.
4007
4008 \note In Qt versions prior to 6.5, this function took QString, not
4009 QAnyStringView.
4010*/
4011void QXmlStreamWriter::writeEmptyElement(QAnyStringView qualifiedName)
4012{
4013 Q_D(QXmlStreamWriter);
4014 Q_ASSERT(count(qualifiedName, ':') <= 1);
4015 d->writeStartElement({}, qualifiedName);
4016 d->inEmptyElement = true;
4017}
4018
4019
4020/*! Writes an empty element with \a name, prefixed for the specified
4021 \a namespaceUri. If the namespace has not been declared,
4022 QXmlStreamWriter will generate a namespace declaration for it.
4023 Subsequent calls to writeAttribute() will add attributes to this element.
4024
4025 \sa writeNamespace()
4026
4027 \note In Qt versions prior to 6.5, this function took QString, not
4028 QAnyStringView.
4029 */
4030void QXmlStreamWriter::writeEmptyElement(QAnyStringView namespaceUri, QAnyStringView name)
4031{
4032 Q_D(QXmlStreamWriter);
4033 Q_ASSERT(!contains(name, ':'));
4034 d->writeStartElement(namespaceUri, name);
4035 d->inEmptyElement = true;
4036}
4037
4038
4039/*!\overload
4040 Writes a text element with \a qualifiedName and \a text.
4041
4042
4043 This is a convenience function equivalent to:
4044 \snippet code/src_corelib_xml_qxmlstream.cpp 1
4045
4046 \note In Qt versions prior to 6.5, this function took QString, not
4047 QAnyStringView.
4048*/
4049void QXmlStreamWriter::writeTextElement(QAnyStringView qualifiedName, QAnyStringView text)
4050{
4051 writeStartElement(qualifiedName);
4052 writeCharacters(text);
4053 writeEndElement();
4054}
4055
4056/*! Writes a text element with \a name, prefixed for the specified \a
4057 namespaceUri, and \a text. If the namespace has not been
4058 declared, QXmlStreamWriter will generate a namespace declaration
4059 for it.
4060
4061
4062 This is a convenience function equivalent to:
4063 \snippet code/src_corelib_xml_qxmlstream.cpp 2
4064
4065 \note In Qt versions prior to 6.5, this function took QString, not
4066 QAnyStringView.
4067*/
4068void QXmlStreamWriter::writeTextElement(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView text)
4069{
4070 writeStartElement(namespaceUri, name);
4071 writeCharacters(text);
4072 writeEndElement();
4073}
4074
4075
4076/*!
4077 Closes all remaining open start elements and writes a newline.
4078
4079 \sa writeStartDocument()
4080 */
4081void QXmlStreamWriter::writeEndDocument()
4082{
4083 Q_D(QXmlStreamWriter);
4084 while (d->tagStack.size())
4085 writeEndElement();
4086 if (d->didWriteStartDocument || d->didWriteAnyToken)
4087 d->write("\n");
4088}
4089
4090/*!
4091 Closes the previous start element.
4092
4093 \sa writeStartElement()
4094 */
4095void QXmlStreamWriter::writeEndElement()
4096{
4097 Q_D(QXmlStreamWriter);
4098 Q_ASSERT(d->didWriteAnyToken);
4099 if (d->tagStack.isEmpty())
4100 return;
4101
4102 // shortcut: if nothing was written, close as empty tag
4103 if (d->inStartElement && !d->inEmptyElement) {
4104 d->write("/>");
4105 d->lastWasStartElement = d->inStartElement = false;
4106 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
4107 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
4108 return;
4109 }
4110
4111 if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting)
4112 d->indent(d->tagStack.size()-1);
4113 if (d->tagStack.isEmpty())
4114 return;
4115 d->lastWasStartElement = false;
4116 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
4117 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
4118 d->write("</");
4119 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
4120 d->write(tag.namespaceDeclaration.prefix);
4121 d->write(":");
4122 }
4123 d->write(tag.name);
4124 d->write(">");
4125}
4126
4127
4128
4129/*!
4130 Writes the entity reference \a name to the stream, as "&\a{name};".
4131
4132 \note In Qt versions prior to 6.5, this function took QString, not
4133 QAnyStringView.
4134 */
4135void QXmlStreamWriter::writeEntityReference(QAnyStringView name)
4136{
4137 Q_D(QXmlStreamWriter);
4138 d->finishStartElement();
4139 d->write("&");
4140 d->write(name);
4141 d->write(";");
4142}
4143
4144
4145/*! Writes a namespace declaration for \a namespaceUri with \a
4146 prefix. If \a prefix is empty, QXmlStreamWriter assigns a unique
4147 prefix consisting of the letter 'n' followed by a number.
4148
4149 If writeStartElement() or writeEmptyElement() was called, the
4150 declaration applies to the current element; otherwise it applies to
4151 the next child element.
4152
4153 Note that the prefix \e xml is both predefined and reserved for
4154 \e http://www.w3.org/XML/1998/namespace, which in turn cannot be
4155 bound to any other prefix. The prefix \e xmlns and its URI
4156 \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism
4157 itself and thus completely forbidden in declarations.
4158
4159 \note In Qt versions prior to 6.5, this function took QString, not
4160 QAnyStringView.
4161 */
4162void QXmlStreamWriter::writeNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
4163{
4164 Q_D(QXmlStreamWriter);
4165 Q_ASSERT(prefix != "xmlns"_L1);
4166 if (prefix.isEmpty()) {
4167 d->findNamespace(namespaceUri, d->inStartElement);
4168 } else {
4169 auto &namespaceDeclaration = d->addExtraNamespace(namespaceUri, prefix);
4170 if (d->inStartElement)
4171 d->writeNamespaceDeclaration(namespaceDeclaration);
4172 }
4173}
4174
4175
4176/*! Writes a default namespace declaration for \a namespaceUri.
4177
4178 If writeStartElement() or writeEmptyElement() was called, the
4179 declaration applies to the current element; otherwise it applies to
4180 the next child element.
4181
4182 Note that the namespaces \e http://www.w3.org/XML/1998/namespace
4183 (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to
4184 \e xml) by definition cannot be declared as default.
4185
4186 \note In Qt versions prior to 6.5, this function took QString, not
4187 QAnyStringView.
4188 */
4189void QXmlStreamWriter::writeDefaultNamespace(QAnyStringView namespaceUri)
4190{
4191 Q_D(QXmlStreamWriter);
4192 Q_ASSERT(namespaceUri != "http://www.w3.org/XML/1998/namespace"_L1);
4193 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
4194 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
4195 namespaceDeclaration.prefix.clear();
4196 namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri);
4197 if (d->inStartElement)
4198 d->writeNamespaceDeclaration(namespaceDeclaration);
4199}
4200
4201
4202/*!
4203 Writes an XML processing instruction with \a target and \a data,
4204 where \a data must not contain the sequence "?>".
4205
4206 \note In Qt versions prior to 6.5, this function took QString, not
4207 QAnyStringView.
4208 */
4209void QXmlStreamWriter::writeProcessingInstruction(QAnyStringView target, QAnyStringView data)
4210{
4211 Q_D(QXmlStreamWriter);
4212 Q_ASSERT(!contains(data, "?>"_L1));
4213 if (!d->finishStartElement(false) && d->autoFormatting)
4214 d->indent(d->tagStack.size());
4215 d->write("<?");
4216 d->write(target);
4217 if (!data.isNull()) {
4218 d->write(" ");
4219 d->write(data);
4220 }
4221 d->write("?>");
4222 d->didWriteAnyToken = true;
4223}
4224
4225
4226
4227/*!\overload
4228
4229 Writes a document start with XML version number "1.0".
4230
4231 \sa writeEndDocument()
4232 \since 4.5
4233 */
4234void QXmlStreamWriter::writeStartDocument()
4235{
4236 writeStartDocument("1.0"_L1);
4237}
4238
4239
4240/*!
4241 Writes a document start with the XML version number \a version.
4242
4243 \note This function does not validate the version string and
4244 allows setting it manually. However, QXmlStreamWriter only
4245 supports XML 1.0. Setting a version string
4246 other than "1.0" does not change the writer's behavior or
4247 escaping rules. It is the caller's responsibility to ensure
4248 consistency between the declared version and the actual content.
4249
4250
4251 \note In Qt versions prior to 6.5, this function took QString, not
4252 QAnyStringView.
4253
4254 \sa writeEndDocument()
4255 */
4256void QXmlStreamWriter::writeStartDocument(QAnyStringView version)
4257{
4258 Q_D(QXmlStreamWriter);
4259 d->finishStartElement(false);
4260 d->write("<?xml version=\"");
4261 d->write(version);
4262 if (d->device) // stringDevice does not get any encoding
4263 d->write("\" encoding=\"UTF-8");
4264 d->write("\"?>");
4265 d->didWriteStartDocument = true;
4266}
4267
4268/*!
4269 \since 4.5
4270 Writes a document start with the XML version number \a version
4271 and a standalone attribute \a standalone.
4272
4273 \note This function does not validate the version string and
4274 allows setting it manually. However, QXmlStreamWriter only
4275 supports XML 1.0. Setting a version string
4276 other than "1.0" does not change the writer's behavior or
4277 escaping rules. It is the caller's responsibility to ensure
4278 consistency between the declared version and the actual content.
4279
4280
4281 \note In Qt versions prior to 6.5, this function took QString, not
4282 QAnyStringView.
4283
4284 \sa writeEndDocument()
4285 */
4286void QXmlStreamWriter::writeStartDocument(QAnyStringView version, bool standalone)
4287{
4288 Q_D(QXmlStreamWriter);
4289 d->finishStartElement(false);
4290 d->write("<?xml version=\"");
4291 d->write(version);
4292 if (d->device) // stringDevice does not get any encoding
4293 d->write("\" encoding=\"UTF-8");
4294 if (standalone)
4295 d->write("\" standalone=\"yes\"?>");
4296 else
4297 d->write("\" standalone=\"no\"?>");
4298 d->didWriteStartDocument = true;
4299}
4300
4301
4302/*!\overload
4303
4304 Writes a start element with \a qualifiedName. Subsequent calls to
4305 writeAttribute() will add attributes to this element.
4306
4307 \sa writeEndElement(), writeEmptyElement()
4308
4309 \note In Qt versions prior to 6.5, this function took QString, not
4310 QAnyStringView.
4311 */
4312void QXmlStreamWriter::writeStartElement(QAnyStringView qualifiedName)
4313{
4314 Q_D(QXmlStreamWriter);
4315 Q_ASSERT(count(qualifiedName, ':') <= 1);
4316 d->writeStartElement({}, qualifiedName);
4317}
4318
4319
4320/*! Writes a start element with \a name, prefixed for the specified
4321 \a namespaceUri. If the namespace has not been declared yet,
4322 QXmlStreamWriter will generate a namespace declaration for
4323 it. Subsequent calls to writeAttribute() will add attributes to this
4324 element.
4325
4326 \sa writeNamespace(), writeEndElement(), writeEmptyElement()
4327
4328 \note In Qt versions prior to 6.5, this function took QString, not
4329 QAnyStringView.
4330 */
4331void QXmlStreamWriter::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
4332{
4333 Q_D(QXmlStreamWriter);
4334 Q_ASSERT(!contains(name, ':'));
4335 d->writeStartElement(namespaceUri, name);
4336}
4337
4338void QXmlStreamWriterPrivate::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
4339 StartElementOption option)
4340{
4341 if (!finishStartElement(false) && autoFormatting)
4342 indent(tagStack.size());
4343
4344 Tag &tag = tagStack_push();
4345 tag.name = addToStringStorage(name);
4346 tag.namespaceDeclaration = findNamespace(namespaceUri);
4347 write("<");
4348 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
4349 write(tag.namespaceDeclaration.prefix);
4350 write(":");
4351 }
4352 write(tag.name);
4353 inStartElement = lastWasStartElement = true;
4354
4355 if (option != StartElementOption::OmitNamespaceDeclarations) {
4356 for (qsizetype i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i)
4357 writeNamespaceDeclaration(namespaceDeclarations[i]);
4358 }
4359 tag.namespaceDeclarationsSize = lastNamespaceDeclaration;
4360 didWriteAnyToken = true;
4361}
4362
4363#if QT_CONFIG(xmlstreamreader)
4364/*! Writes the current state of the \a reader. All possible valid
4365 states are supported.
4366
4367 The purpose of this function is to support chained processing of XML data.
4368
4369 \sa QXmlStreamReader::tokenType()
4370 */
4371void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
4372{
4373 Q_D(QXmlStreamWriter);
4374 switch (reader.tokenType()) {
4375 case QXmlStreamReader::NoToken:
4376 break;
4377 case QXmlStreamReader::StartDocument:
4378 writeStartDocument();
4379 break;
4380 case QXmlStreamReader::EndDocument:
4381 writeEndDocument();
4382 break;
4383 case QXmlStreamReader::StartElement: {
4384 // Namespaces must be added before writeStartElement is called so new prefixes are found
4385 QList<QXmlStreamPrivateTagStack::NamespaceDeclaration> extraNamespaces;
4386 const QXmlStreamNamespaceDeclarations nsDeclarations = reader.namespaceDeclarations();
4387 for (const auto &namespaceDeclaration : nsDeclarations) {
4388 auto &extraNamespace = d->addExtraNamespace(namespaceDeclaration.namespaceUri(),
4389 namespaceDeclaration.prefix());
4390 extraNamespaces.append(extraNamespace);
4391 }
4392 d->writeStartElement(
4393 reader.namespaceUri(), reader.name(),
4394 QXmlStreamWriterPrivate::StartElementOption::OmitNamespaceDeclarations);
4395 // Namespace declarations are written afterwards
4396 for (const auto &extraNamespace : std::as_const(extraNamespaces))
4397 d->writeNamespaceDeclaration(extraNamespace);
4398 writeAttributes(reader.attributes());
4399 } break;
4400 case QXmlStreamReader::EndElement:
4401 writeEndElement();
4402 break;
4403 case QXmlStreamReader::Characters:
4404 if (reader.isCDATA())
4405 writeCDATA(reader.text());
4406 else
4407 writeCharacters(reader.text());
4408 break;
4409 case QXmlStreamReader::Comment:
4410 writeComment(reader.text());
4411 break;
4412 case QXmlStreamReader::DTD:
4413 writeDTD(reader.text());
4414 break;
4415 case QXmlStreamReader::EntityReference:
4416 writeEntityReference(reader.name());
4417 break;
4418 case QXmlStreamReader::ProcessingInstruction:
4419 writeProcessingInstruction(reader.processingInstructionTarget(),
4420 reader.processingInstructionData());
4421 break;
4422 default:
4423 Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid);
4424 qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state.");
4425 break;
4426 }
4427}
4428#endif // feature xmlstreamreader
4429#endif // feature xmlstreamwriter
4430
4431#if QT_CONFIG(xmlstreamreader)
4432static constexpr bool isTokenAllowedInContext(QXmlStreamReader::TokenType type,
4433 QXmlStreamReaderPrivate::XmlContext ctxt)
4434{
4435 switch (type) {
4436 case QXmlStreamReader::StartDocument:
4437 case QXmlStreamReader::DTD:
4438 return ctxt == QXmlStreamReaderPrivate::XmlContext::Prolog;
4439
4440 case QXmlStreamReader::StartElement:
4441 case QXmlStreamReader::EndElement:
4442 case QXmlStreamReader::Characters:
4443 case QXmlStreamReader::EntityReference:
4444 case QXmlStreamReader::EndDocument:
4445 return ctxt == QXmlStreamReaderPrivate::XmlContext::Body;
4446
4447 case QXmlStreamReader::Comment:
4448 case QXmlStreamReader::ProcessingInstruction:
4449 return true;
4450
4451 case QXmlStreamReader::NoToken:
4452 case QXmlStreamReader::Invalid:
4453 return false;
4454 }
4455
4456 // GCC 8.x does not treat __builtin_unreachable() as constexpr
4457#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
4458 Q_UNREACHABLE_RETURN(false);
4459#else
4460 return false;
4461#endif
4462}
4463
4464/*!
4465 \internal
4466 \brief QXmlStreamReader::isValidToken
4467 \return \c true if \param type is a valid token type.
4468 \return \c false if \param type is an unexpected token,
4469 which indicates a non-well-formed or invalid XML stream.
4470 */
4471bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type)
4472{
4473 // Don't change currentContext, if Invalid or NoToken occur in the prolog
4474 if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken)
4475 return false;
4476
4477 // If a token type gets rejected in the body, there is no recovery
4478 const bool result = isTokenAllowedInContext(type, currentContext);
4479 if (result || currentContext == XmlContext::Body)
4480 return result;
4481
4482 // First non-Prolog token observed => switch context to body and check again.
4483 currentContext = XmlContext::Body;
4484 return isTokenAllowedInContext(type, currentContext);
4485}
4486
4487/*!
4488 \internal
4489 Checks token type and raises an error, if it is invalid
4490 in the current context (prolog/body).
4491 */
4492void QXmlStreamReaderPrivate::checkToken()
4493{
4494 Q_Q(QXmlStreamReader);
4495
4496 // The token type must be consumed, to keep track if the body has been reached.
4497 const XmlContext context = currentContext;
4498 const bool ok = isValidToken(type);
4499
4500 // Do nothing if an error has been raised already (going along with an unexpected token)
4501 if (error != QXmlStreamReader::Error::NoError)
4502 return;
4503
4504 if (!ok) {
4505 raiseError(QXmlStreamReader::UnexpectedElementError,
4506 QXmlStream::tr("Unexpected token type %1 in %2.")
4507 .arg(q->tokenString(), contextString(context)));
4508 return;
4509 }
4510
4511 if (type != QXmlStreamReader::DTD)
4512 return;
4513
4514 // Raise error on multiple DTD tokens
4515 if (foundDTD) {
4516 raiseError(QXmlStreamReader::UnexpectedElementError,
4517 QXmlStream::tr("Found second DTD token in %1.").arg(contextString(context)));
4518 } else {
4519 foundDTD = true;
4520 }
4521}
4522
4523/*!
4524 \fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView qualifiedName) const
4525
4526 Returns \c true if this QXmlStreamAttributes has an attribute whose
4527 qualified name is \a qualifiedName; otherwise returns \c false.
4528
4529 Note that this is not namespace aware. For instance, if this
4530 QXmlStreamAttributes contains an attribute whose lexical name is "xlink:href"
4531 this doesn't tell that an attribute named \c href in the XLink namespace is
4532 present, since the \c xlink prefix can be bound to any namespace. Use the
4533 overload that takes a namespace URI and a local name as parameter, for
4534 namespace aware code.
4535*/
4536
4537/*!
4538 \fn bool QXmlStreamAttributes::hasAttribute(QAnyStringView namespaceUri,
4539 QAnyStringView name) const
4540 \overload
4541
4542 Returns \c true if this QXmlStreamAttributes has an attribute whose
4543 namespace URI and name correspond to \a namespaceUri and \a name;
4544 otherwise returns \c false.
4545*/
4546
4547#endif // feature xmlstreamreader
4548
4549QT_END_NAMESPACE
4550
4551#endif // feature xmlstream