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