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