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