Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include "QtCore/qxmlstream.h"
5
6#if QT_CONFIG(xmlstream)
7
8#include "qxmlutils_p.h"
9#include <qdebug.h>
10#include <qfile.h>
11#include <stdio.h>
12#include <qstringconverter.h>
13#include <qstack.h>
14#include <qbuffer.h>
15#include <qscopeguard.h>
16#include <qcoreapplication.h>
17
18#include <private/qoffsetstringarray_p.h>
19#include <private/qtools_p.h>
20
21#include <iterator>
22#include "qxmlstream_p.h"
23#include "qxmlstreamparser_p.h"
24#include <private/qstringconverter_p.h>
25#include <private/qstringiterator_p.h>
26
28
29using namespace QtPrivate;
30using namespace Qt::StringLiterals;
31using namespace QtMiscUtils;
32
33enum { StreamEOF = ~0U };
34
35namespace {
36template <typename Range>
37auto reversed(Range &r)
38{
39 struct R {
40 Range *r;
41 auto begin() { return std::make_reverse_iterator(std::end(*r)); }
42 auto end() { return std::make_reverse_iterator(std::begin(*r)); }
43 };
44
45 return R{&r};
46}
47
48template <typename Range>
49void reversed(const Range &&) = delete;
50
51// implementation of missing QUtf8StringView methods for ASCII-only needles:
52auto transform(QLatin1StringView haystack, char needle)
53{
54 struct R { QLatin1StringView haystack; char16_t needle; };
55 return R{haystack, uchar(needle)};
56}
57
58auto transform(QStringView haystack, char needle)
59{
60 struct R { QStringView haystack; char16_t needle; };
61 return R{haystack, uchar(needle)};
62}
63
64auto transform(QUtf8StringView haystack, char needle)
65{
66 struct R { QByteArrayView haystack; char needle; };
67 return R{haystack, needle};
68}
69
71{
72 struct R { QLatin1StringView haystack; QLatin1StringView needle; };
73 return R{haystack, needle};
74}
75
76auto transform(QStringView haystack, QLatin1StringView needle)
77{
78 struct R { QStringView haystack; QLatin1StringView needle; };
79 return R{haystack, needle};
80}
81
82auto transform(QUtf8StringView haystack, QLatin1StringView needle)
83{
84 struct R { QLatin1StringView haystack; QLatin1StringView needle; };
85 return R{QLatin1StringView{QByteArrayView{haystack}}, needle};
86}
87
88#define WRAP(method, Needle) \
89 auto method (QAnyStringView s, Needle needle) noexcept \
90 { \
91 return s.visit([needle](auto s) { \
92 auto r = transform(s, needle); \
93 return r.haystack. method (r.needle); \
94 }); \
95 } \
96 /*end*/
97
98WRAP(count, char)
99WRAP(contains, char)
101WRAP(endsWith, char)
103
104} // unnamed namespace
105
208QXmlStreamEntityResolver::~QXmlStreamEntityResolver()
209{
210}
211
217QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/)
218{
219 return QString();
220}
221
222
231QString QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/)
232{
233 return QString();
234}
235
236#if QT_CONFIG(xmlstreamreader)
237
239{
240 if (entityResolver)
241 return entityResolver->resolveUndeclaredEntity(name);
242 return QString();
243}
244
245
246
259void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver)
260{
261 Q_D(QXmlStreamReader);
262 d->entityResolver = resolver;
263}
264
272QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const
273{
274 Q_D(const QXmlStreamReader);
275 return d->entityResolver;
276}
277
278
279
430QXmlStreamReader::QXmlStreamReader()
431 : d_ptr(new QXmlStreamReaderPrivate(this))
432{
433}
434
439QXmlStreamReader::QXmlStreamReader(QIODevice *device)
440 : d_ptr(new QXmlStreamReaderPrivate(this))
441{
442 setDevice(device);
443}
444
463QXmlStreamReader::QXmlStreamReader(QAnyStringView data)
464 : d_ptr(new QXmlStreamReaderPrivate(this))
465{
466 Q_D(QXmlStreamReader);
467 data.visit([d](auto data) {
468 if constexpr (std::is_same_v<decltype(data), QStringView>) {
469 d->dataBuffer = data.toUtf8();
471 d->lockEncoding = true;
472 } else if constexpr (std::is_same_v<decltype(data), QLatin1StringView>) {
473 // Conversion to a QString is required, to avoid breaking
474 // pre-existing (before porting to QAnyStringView) behavior.
475 d->dataBuffer = QString::fromLatin1(data).toUtf8();
477 d->lockEncoding = true;
478 } else {
479 d->dataBuffer = QByteArray(data.data(), data.size());
480 }
481 });
482}
483
490QXmlStreamReader::QXmlStreamReader(const QByteArray &data, PrivateConstructorTag)
491 : d_ptr(new QXmlStreamReaderPrivate(this))
492{
493 Q_D(QXmlStreamReader);
494 d->dataBuffer = data;
495}
496
500QXmlStreamReader::~QXmlStreamReader()
501{
502 Q_D(QXmlStreamReader);
503 if (d->deleteDevice)
504 delete d->device;
505}
506
519void QXmlStreamReader::setDevice(QIODevice *device)
520{
521 Q_D(QXmlStreamReader);
522 if (d->deleteDevice) {
523 delete d->device;
524 d->deleteDevice = false;
525 }
526 d->device = device;
527 d->init();
528
529}
530
537QIODevice *QXmlStreamReader::device() const
538{
539 Q_D(const QXmlStreamReader);
540 return d->device;
541}
542
563void QXmlStreamReader::addData(QAnyStringView data)
564{
565 Q_D(QXmlStreamReader);
566 data.visit([this, d](auto data) {
567 if constexpr (std::is_same_v<decltype(data), QStringView>) {
568 d->lockEncoding = true;
569 if (!d->decoder.isValid())
571 addDataImpl(data.toUtf8());
572 } else if constexpr (std::is_same_v<decltype(data), QLatin1StringView>) {
573 // Conversion to a QString is required, to avoid breaking
574 // pre-existing (before porting to QAnyStringView) behavior.
575 if (!d->decoder.isValid())
577 addDataImpl(QString::fromLatin1(data).toUtf8());
578 } else {
579 addDataImpl(QByteArray(data.data(), data.size()));
580 }
581 });
582}
583
590void QXmlStreamReader::addDataImpl(const QByteArray &data)
591{
592 Q_D(QXmlStreamReader);
593 if (d->device) {
594 qWarning("QXmlStreamReader: addData() with device()");
595 return;
596 }
597 d->dataBuffer += data;
598}
599
606void QXmlStreamReader::clear()
607{
608 Q_D(QXmlStreamReader);
609 d->init();
610 if (d->device) {
611 if (d->deleteDevice)
612 delete d->device;
613 d->device = nullptr;
614 }
615}
616
632bool QXmlStreamReader::atEnd() const
633{
634 Q_D(const QXmlStreamReader);
635 if (d->atEnd
636 && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError)
637 || (d->type == QXmlStreamReader::EndDocument))) {
638 if (d->device)
639 return d->device->atEnd();
640 else
641 return !d->dataBuffer.size();
642 }
643 return (d->atEnd || d->type == QXmlStreamReader::Invalid);
644}
645
646
665QXmlStreamReader::TokenType QXmlStreamReader::readNext()
666{
667 Q_D(QXmlStreamReader);
668 if (d->type != Invalid) {
669 if (!d->hasCheckedStartDocument)
670 if (!d->checkStartDocument())
671 return d->type; // synthetic StartDocument or error
672 d->parse();
673 if (d->atEnd && d->type != EndDocument && d->type != Invalid)
674 d->raiseError(PrematureEndOfDocumentError);
675 else if (!d->atEnd && d->type == EndDocument)
676 d->raiseWellFormedError(QXmlStream::tr("Extra content at end of document."));
677 } else if (d->error == PrematureEndOfDocumentError) {
678 // resume error
679 d->type = NoToken;
680 d->atEnd = false;
681 d->token = -1;
682 return readNext();
683 }
684 d->checkToken();
685 return d->type;
686}
687
688
699QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const
700{
701 Q_D(const QXmlStreamReader);
702 return d->type;
703}
704
722bool QXmlStreamReader::readNextStartElement()
723{
724 while (readNext() != Invalid) {
725 if (isEndElement() || isEndDocument())
726 return false;
727 else if (isStartElement())
728 return true;
729 }
730 return false;
731}
732
744void QXmlStreamReader::skipCurrentElement()
745{
746 int depth = 1;
747 while (depth && readNext() != Invalid) {
748 if (isEndElement())
749 --depth;
750 else if (isStartElement())
751 ++depth;
752 }
753}
754
755static constexpr auto QXmlStreamReader_tokenTypeString = qOffsetStringArray(
756 "NoToken",
757 "Invalid",
758 "StartDocument",
759 "EndDocument",
760 "StartElement",
761 "EndElement",
762 "Characters",
763 "Comment",
764 "DTD",
765 "EntityReference",
766 "ProcessingInstruction"
767);
768
769static constexpr auto QXmlStreamReader_XmlContextString = qOffsetStringArray(
770 "Prolog",
771 "Body"
772);
773
786void QXmlStreamReader::setNamespaceProcessing(bool enable)
787{
788 Q_D(QXmlStreamReader);
789 d->namespaceProcessing = enable;
790}
791
792bool QXmlStreamReader::namespaceProcessing() const
793{
794 Q_D(const QXmlStreamReader);
795 return d->namespaceProcessing;
796}
797
802QString QXmlStreamReader::tokenString() const
803{
804 Q_D(const QXmlStreamReader);
805 return QLatin1StringView(QXmlStreamReader_tokenTypeString.at(d->type));
806}
807
812static constexpr QLatin1StringView contextString(QXmlStreamReaderPrivate::XmlContext ctxt)
813{
814 return QLatin1StringView(QXmlStreamReader_XmlContextString.at(static_cast<int>(ctxt)));
815}
816
817#endif // feature xmlstreamreader
818
820{
821 tagStack.reserve(16);
822 tagStackStringStorage.reserve(32);
823 tagStackStringStorageSize = 0;
824 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
825 namespaceDeclaration.prefix = addToStringStorage(u"xml");
826 namespaceDeclaration.namespaceUri = addToStringStorage(u"http://www.w3.org/XML/1998/namespace");
827 initialTagStackStringStorageSize = tagStackStringStorageSize;
828 tagsDone = false;
829}
830
831#if QT_CONFIG(xmlstreamreader)
832
834 :q_ptr(q)
835{
836 device = nullptr;
837 deleteDevice = false;
838 stack_size = 64;
839 sym_stack = nullptr;
840 state_stack = nullptr;
841 reallocateStack();
842 entityResolver = nullptr;
843 init();
844#define ADD_PREDEFINED(n, v) \
845 do { \
846 Entity e = Entity::createLiteral(n##_L1, v##_L1); \
847 entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \
848 } while (false)
849 ADD_PREDEFINED("lt", "<");
850 ADD_PREDEFINED("gt", ">");
851 ADD_PREDEFINED("amp", "&");
852 ADD_PREDEFINED("apos", "'");
853 ADD_PREDEFINED("quot", "\"");
854#undef ADD_PREDEFINED
855}
856
858{
859 scanDtd = false;
860 lastAttributeIsCData = false;
861 token = -1;
862 token_char = 0;
863 isEmptyElement = false;
864 isWhitespace = true;
865 isCDATA = false;
866 standalone = false;
867 hasStandalone = false;
868 tos = 0;
869 resumeReduction = 0;
870 state_stack[tos++] = 0;
871 state_stack[tos] = 0;
872 putStack.clear();
873 putStack.reserve(32);
875 textBuffer.reserve(256);
876 tagStack.clear();
877 tagsDone = false;
878 attributes.clear();
879 attributes.reserve(16);
881 readBufferPos = 0;
882 nbytesread = 0;
884 attributeStack.clear();
885 attributeStack.reserve(16);
886 entityParser.reset();
888 normalizeLiterals = false;
889 hasSeenTag = false;
890 atEnd = false;
891 inParseEntity = false;
894 hasExternalDtdSubset = false;
895 lockEncoding = false;
896 namespaceProcessing = true;
899 readBuffer.clear();
901
902 type = QXmlStreamReader::NoToken;
903 error = QXmlStreamReader::NoError;
905 foundDTD = false;
906}
907
908/*
909 Well-formed requires that we verify entity values. We do this with a
910 standard parser.
911 */
913{
914 Q_Q(QXmlStreamReader);
915
916 if (value.isEmpty())
917 return;
918
919
920 if (!entityParser)
921 entityParser = std::make_unique<QXmlStreamReaderPrivate>(q);
922 else
923 entityParser->init();
924 entityParser->inParseEntity = true;
925 entityParser->readBuffer = value;
926 entityParser->injectToken(PARSE_ENTITY);
927 while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid)
928 entityParser->parse();
929 if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size())
930 raiseWellFormedError(QXmlStream::tr("Invalid entity value."));
931
932}
933
935{
936 stack_size <<= 1;
937 sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
939 state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
941}
942
943
945{
946 free(sym_stack);
947 free(state_stack);
948}
949
950
952{
953 uint peekc = peekChar();
954 if (peekc == '\n') {
955 if (putStack.size())
956 putStack.pop();
957 else
959 return peekc;
960 }
961 if (peekc == StreamEOF) {
962 putChar('\r');
963 return 0;
964 }
965 return '\n';
966}
967
973{
974 uint c;
975 if (putStack.size()) {
976 c = atEnd ? StreamEOF : putStack.pop();
977 } else {
978 if (readBufferPos < readBuffer.size())
979 c = readBuffer.at(readBufferPos++).unicode();
980 else
981 c = getChar_helper();
982 }
983
984 return c;
985}
986
988{
989 uint c;
990 if (putStack.size()) {
991 c = putStack.top();
992 } else if (readBufferPos < readBuffer.size()) {
993 c = readBuffer.at(readBufferPos).unicode();
994 } else {
995 if ((c = getChar_helper()) != StreamEOF)
997 }
998
999 return c;
1000}
1001
1014bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject)
1015{
1016 const qsizetype pos = textBuffer.size();
1017 const auto oldLineNumber = lineNumber;
1018
1019 uint c;
1020 while ((c = getChar()) != StreamEOF) {
1021 /* First, we do the validation & normalization. */
1022 switch (c) {
1023 case '\r':
1024 if ((c = filterCarriageReturn()) == 0)
1025 break;
1026 Q_FALLTHROUGH();
1027 case '\n':
1028 ++lineNumber;
1030 Q_FALLTHROUGH();
1031 case '\t':
1032 textBuffer += QChar(c);
1033 continue;
1034 default:
1035 if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) {
1036 raiseWellFormedError(QXmlStream::tr("Invalid XML character."));
1037 lineNumber = oldLineNumber;
1038 return false;
1039 }
1040 textBuffer += QChar(c);
1041 }
1042
1043
1044 /* Second, attempt to lookup str. */
1045 if (c == uint(*str)) {
1046 if (!*(str + 1)) {
1047 if (tokenToInject >= 0)
1048 injectToken(tokenToInject);
1049 return true;
1050 } else {
1051 if (scanString(str + 1, tokenToInject, false))
1052 return true;
1053 }
1054 }
1055 }
1058 lineNumber = oldLineNumber;
1059 return false;
1060}
1061
1062bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace)
1063{
1064 qsizetype n = 0;
1065 while (str[n]) {
1066 uint c = getChar();
1067 if (c != ushort(str[n])) {
1068 if (c != StreamEOF)
1069 putChar(c);
1070 while (n--) {
1071 putChar(ushort(str[n]));
1072 }
1073 return false;
1074 }
1075 ++n;
1076 }
1078 if (requireSpace) {
1079 const qsizetype s = fastScanSpace();
1080 if (!s || atEnd) {
1081 qsizetype pos = textBuffer.size() - n - s;
1084 return false;
1085 }
1086 }
1087 if (tokenToInject >= 0)
1088 injectToken(tokenToInject);
1089 return true;
1090}
1091
1093{
1094 switch (peekChar()) {
1095 case '[':
1096 return scanString(spell[CDATA_START], CDATA_START, false);
1097 case 'D':
1098 return scanString(spell[DOCTYPE], DOCTYPE);
1099 case 'A':
1100 return scanString(spell[ATTLIST], ATTLIST);
1101 case 'N':
1103 case 'E':
1105 return true;
1106 return scanString(spell[ENTITY], ENTITY);
1107
1108 default:
1109 ;
1110 };
1111 return false;
1112}
1113
1115{
1116 switch (peekChar()) {
1117 case 'S':
1118 return scanString(spell[SYSTEM], SYSTEM);
1119 case 'P':
1120 return scanString(spell[PUBLIC], PUBLIC);
1121 default:
1122 ;
1123 }
1124 return false;
1125}
1126
1128{
1129 if (fastScanSpace()) {
1130 if (scanString(spell[NDATA], NDATA))
1131 return true;
1132 putChar(' ');
1133 }
1134 return false;
1135}
1136
1138{
1139 switch (peekChar()) {
1140 case 'R':
1141 return scanString(spell[REQUIRED], REQUIRED, false);
1142 case 'I':
1143 return scanString(spell[IMPLIED], IMPLIED, false);
1144 case 'F':
1145 return scanString(spell[FIXED], FIXED, false);
1146 default:
1147 ;
1148 }
1149 return false;
1150}
1151
1153{
1154 switch (peekChar()) {
1155 case 'C':
1156 return scanString(spell[CDATA], CDATA);
1157 case 'I':
1158 if (scanString(spell[ID], ID))
1159 return true;
1160 if (scanString(spell[IDREF], IDREF))
1161 return true;
1162 return scanString(spell[IDREFS], IDREFS);
1163 case 'E':
1165 return true;
1167 case 'N':
1169 return true;
1171 return true;
1173 default:
1174 ;
1175 }
1176 return false;
1177}
1178
1192{
1193 qsizetype n = 0;
1194 uint c;
1195 while ((c = getChar()) != StreamEOF) {
1196 switch (ushort(c)) {
1197 case 0xfffe:
1198 case 0xffff:
1199 case 0:
1200 /* The putChar() call is necessary so the parser re-gets
1201 * the character from the input source, when raising an error. */
1202 putChar(c);
1203 return n;
1204 case '\r':
1205 if (filterCarriageReturn() == 0)
1206 return n;
1207 Q_FALLTHROUGH();
1208 case '\n':
1209 ++lineNumber;
1211 Q_FALLTHROUGH();
1212 case ' ':
1213 case '\t':
1215 textBuffer += u' ';
1216 else
1217 textBuffer += QChar(c);
1218 ++n;
1219 break;
1220 case '&':
1221 case '<':
1222 case '\"':
1223 case '\'':
1224 if (!(c & 0xff0000)) {
1225 putChar(c);
1226 return n;
1227 }
1228 Q_FALLTHROUGH();
1229 default:
1230 if (c < 0x20) {
1231 putChar(c);
1232 return n;
1233 }
1234 textBuffer += QChar(ushort(c));
1235 ++n;
1236 }
1237 }
1238 return n;
1239}
1240
1242{
1243 qsizetype n = 0;
1244 uint c;
1245 while ((c = getChar()) != StreamEOF) {
1246 switch (c) {
1247 case '\r':
1248 if ((c = filterCarriageReturn()) == 0)
1249 return n;
1250 Q_FALLTHROUGH();
1251 case '\n':
1252 ++lineNumber;
1254 Q_FALLTHROUGH();
1255 case ' ':
1256 case '\t':
1257 textBuffer += QChar(c);
1258 ++n;
1259 break;
1260 default:
1261 putChar(c);
1262 return n;
1263 }
1264 }
1265 return n;
1266}
1267
1275{
1276 qsizetype n = 0;
1277 uint c;
1278 while ((c = getChar()) != StreamEOF) {
1279 switch (ushort(c)) {
1280 case 0xfffe:
1281 case 0xffff:
1282 case 0:
1283 putChar(c);
1284 return n;
1285 case ']': {
1286 isWhitespace = false;
1287 const qsizetype pos = textBuffer.size();
1288 textBuffer += QChar(ushort(c));
1289 ++n;
1290 while ((c = getChar()) == ']') {
1291 textBuffer += QChar(ushort(c));
1292 ++n;
1293 }
1294 if (c == 0) {
1297 } else if (c == '>' && textBuffer.at(textBuffer.size() - 2) == u']') {
1298 raiseWellFormedError(QXmlStream::tr("Sequence ']]>' not allowed in content."));
1299 } else {
1300 putChar(c);
1301 break;
1302 }
1303 return n;
1304 } break;
1305 case '\r':
1306 if ((c = filterCarriageReturn()) == 0)
1307 return n;
1308 Q_FALLTHROUGH();
1309 case '\n':
1310 ++lineNumber;
1312 Q_FALLTHROUGH();
1313 case ' ':
1314 case '\t':
1315 textBuffer += QChar(ushort(c));
1316 ++n;
1317 break;
1318 case '&':
1319 case '<':
1320 if (!(c & 0xff0000)) {
1321 putChar(c);
1322 return n;
1323 }
1324 Q_FALLTHROUGH();
1325 default:
1326 if (c < 0x20) {
1327 putChar(c);
1328 return n;
1329 }
1330 isWhitespace = false;
1331 textBuffer += QChar(ushort(c));
1332 ++n;
1333 }
1334 }
1335 return n;
1336}
1337
1338// Fast scan an XML attribute name (e.g. "xml:lang").
1339inline std::optional<qsizetype> QXmlStreamReaderPrivate::fastScanName(Value *val)
1340{
1341 qsizetype n = 0;
1342 uint c;
1343 while ((c = getChar()) != StreamEOF) {
1344 if (n >= 4096) {
1345 // This is too long to be a sensible name, and
1346 // can exhaust memory, or the range of decltype(*prefix)
1348 return std::nullopt;
1349 }
1350 switch (c) {
1351 case '\n':
1352 case ' ':
1353 case '\t':
1354 case '\r':
1355 case '&':
1356 case '#':
1357 case '\'':
1358 case '\"':
1359 case '<':
1360 case '>':
1361 case '[':
1362 case ']':
1363 case '=':
1364 case '%':
1365 case '/':
1366 case ';':
1367 case '?':
1368 case '!':
1369 case '^':
1370 case '|':
1371 case ',':
1372 case '(':
1373 case ')':
1374 case '+':
1375 case '*':
1376 putChar(c);
1377 if (val && val->prefix == n + 1) {
1378 val->prefix = 0;
1379 putChar(':');
1380 --n;
1381 }
1382 return n;
1383 case ':':
1384 if (val) {
1385 if (val->prefix == 0) {
1386 val->prefix = qint16(n + 2);
1387 } else { // only one colon allowed according to the namespace spec.
1388 putChar(c);
1389 return n;
1390 }
1391 } else {
1392 putChar(c);
1393 return n;
1394 }
1395 Q_FALLTHROUGH();
1396 default:
1397 textBuffer += QChar(ushort(c));
1398 ++n;
1399 }
1400 }
1401
1402 if (val)
1403 val->prefix = 0;
1407 return 0;
1408}
1409
1410enum NameChar { NameBeginning, NameNotBeginning, NotName };
1411
1412static const char Begi = static_cast<char>(NameBeginning);
1413static const char NtBg = static_cast<char>(NameNotBeginning);
1414static const char NotN = static_cast<char>(NotName);
1415
1416static const char nameCharTable[128] =
1417{
1418// 0x00
1419 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1420 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1421// 0x10
1422 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1423 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1424// 0x20 (0x2D is '-', 0x2E is '.')
1425 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1426 NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN,
1427// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':')
1428 NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg,
1429 NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN,
1430// 0x40 (0x41..0x5A are 'A'..'Z')
1431 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1432 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1433// 0x50 (0x5F is '_')
1434 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1435 Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi,
1436// 0x60 (0x61..0x7A are 'a'..'z')
1437 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1438 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1439// 0x70
1440 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1441 Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN
1442};
1443
1444static inline NameChar fastDetermineNameChar(QChar ch)
1445{
1446 ushort uc = ch.unicode();
1447 if (!(uc & ~0x7f)) // uc < 128
1448 return static_cast<NameChar>(nameCharTable[uc]);
1449
1450 QChar::Category cat = ch.category();
1451 // ### some these categories might be slightly wrong
1452 if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other)
1453 || cat == QChar::Number_Letter)
1454 return NameBeginning;
1455 if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other)
1456 || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing))
1457 return NameNotBeginning;
1458 return NotName;
1459}
1460
1462{
1463 qsizetype n = 0;
1464 uint c;
1465 while ((c = getChar()) != StreamEOF) {
1466 if (fastDetermineNameChar(QChar(c)) == NotName) {
1467 putChar(c);
1468 return n;
1469 } else {
1470 ++n;
1471 textBuffer += QChar(c);
1472 }
1473 }
1474
1478
1479 return n;
1480}
1481
1483{
1484 if (from != 0) {
1485 putString(s.mid(from));
1486 return;
1487 }
1488 putStack.reserve(s.size());
1489 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1490 putStack.rawPush() = it->unicode();
1491}
1492
1494{
1495 putStack.reserve(s.size());
1496 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1497 putStack.rawPush() = ((LETTER << 16) | it->unicode());
1498}
1499
1501{
1502 putStack.reserve(s.size());
1503 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1504 char16_t c = it->unicode();
1505 if (c == '\n' || c == '\r')
1506 putStack.rawPush() = ((LETTER << 16) | c);
1507 else
1508 putStack.rawPush() = c;
1509 }
1510}
1512{
1513 putStack.reserve(s.size());
1514 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1515 char16_t c = it->unicode();
1516 if (c == '&' || c == ';')
1517 putStack.rawPush() = c;
1518 else if (c == '\n' || c == '\r')
1519 putStack.rawPush() = ' ';
1520 else
1521 putStack.rawPush() = ((LETTER << 16) | c);
1522 }
1523}
1524
1526{
1527 constexpr qsizetype BUFFER_SIZE = 8192;
1529 readBufferPos = 0;
1530 if (readBuffer.size())
1531 readBuffer.resize(0);
1532 if (decoder.isValid())
1533 nbytesread = 0;
1534 if (device) {
1536 qint64 nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread);
1537 nbytesread += qMax(nbytesreadOrMinus1, qint64{0});
1538 } else {
1539 if (nbytesread)
1541 else
1544 dataBuffer.clear();
1545 }
1546 if (!nbytesread) {
1547 atEnd = true;
1548 return StreamEOF;
1549 }
1550
1551 if (!decoder.isValid()) {
1552 if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus
1553 // one extra for the utf8 codec
1554 atEnd = true;
1555 return StreamEOF;
1556 }
1557 auto encoding = QStringDecoder::encodingForData(rawReadBuffer, char16_t('<'));
1558 if (!encoding)
1559 // assume utf-8
1560 encoding = QStringDecoder::Utf8;
1561 decoder = QStringDecoder(*encoding);
1562 }
1563
1565
1566 if (lockEncoding && decoder.hasError()) {
1567 raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content."));
1568 readBuffer.clear();
1569 return StreamEOF;
1570 }
1571
1572 readBuffer.reserve(1); // keep capacity when calling resize() next time
1573
1574 if (readBufferPos < readBuffer.size()) {
1575 ushort c = readBuffer.at(readBufferPos++).unicode();
1576 return c;
1577 }
1578
1579 atEnd = true;
1580 return StreamEOF;
1581}
1582
1584{
1585 for (const NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
1586 if (namespaceDeclaration.prefix == prefix) {
1587 return namespaceDeclaration.namespaceUri;
1588 }
1589 }
1590
1591#if 1
1593 raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix));
1594#endif
1595
1596 return XmlStringRef();
1597}
1598
1599/*
1600 uses namespaceForPrefix and builds the attribute vector
1601 */
1603{
1604 const auto attributeStackCleaner = qScopeGuard([this](){ attributeStack.clear(); });
1605 const qsizetype n = attributeStack.size();
1606
1607 if (namespaceProcessing) {
1608 for (DtdAttribute &dtdAttribute : dtdAttributes) {
1609 if (!dtdAttribute.isNamespaceAttribute
1610 || dtdAttribute.defaultValue.isNull()
1611 || dtdAttribute.tagName != qualifiedName
1612 || dtdAttribute.attributeQualifiedName.isNull())
1613 continue;
1614 qsizetype i = 0;
1615 while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1616 ++i;
1617 if (i != n)
1618 continue;
1619 if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == "xmlns"_L1) {
1620 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1621 namespaceDeclaration.prefix.clear();
1622
1623 const XmlStringRef ns(dtdAttribute.defaultValue);
1624 if (ns == "http://www.w3.org/2000/xmlns/"_L1 ||
1625 ns == "http://www.w3.org/XML/1998/namespace"_L1)
1626 raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
1627 else
1628 namespaceDeclaration.namespaceUri = ns;
1629 } else if (dtdAttribute.attributePrefix == "xmlns"_L1) {
1630 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1631 XmlStringRef namespacePrefix = dtdAttribute.attributeName;
1632 XmlStringRef namespaceUri = dtdAttribute.defaultValue;
1633 if (((namespacePrefix == "xml"_L1)
1634 ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1))
1635 || namespaceUri == "http://www.w3.org/2000/xmlns/"_L1
1637 || namespacePrefix == "xmlns"_L1)
1638 raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
1639
1640 namespaceDeclaration.prefix = namespacePrefix;
1641 namespaceDeclaration.namespaceUri = namespaceUri;
1642 }
1643 }
1644 }
1645
1646 tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix);
1647
1648 attributes.resize(n);
1649
1650 for (qsizetype i = 0; i < n; ++i) {
1651 QXmlStreamAttribute &attribute = attributes[i];
1652 Attribute &attrib = attributeStack[i];
1653 XmlStringRef prefix(symPrefix(attrib.key));
1654 XmlStringRef name(symString(attrib.key));
1655 XmlStringRef qualifiedName(symName(attrib.key));
1656 XmlStringRef value(symString(attrib.value));
1657
1658 attribute.m_name = name;
1659 attribute.m_qualifiedName = qualifiedName;
1660 attribute.m_value = value;
1661
1662 if (!prefix.isEmpty()) {
1663 XmlStringRef attributeNamespaceUri = namespaceForPrefix(prefix);
1664 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1665 }
1666
1667 for (qsizetype j = 0; j < i; ++j) {
1668 if (attributes[j].name() == attribute.name()
1669 && attributes[j].namespaceUri() == attribute.namespaceUri()
1670 && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName()))
1671 {
1672 raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName()));
1673 return;
1674 }
1675 }
1676 }
1677
1678 for (DtdAttribute &dtdAttribute : dtdAttributes) {
1679 if (dtdAttribute.isNamespaceAttribute
1680 || dtdAttribute.defaultValue.isNull()
1681 || dtdAttribute.tagName != qualifiedName
1682 || dtdAttribute.attributeQualifiedName.isNull())
1683 continue;
1684 qsizetype i = 0;
1685 while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1686 ++i;
1687 if (i != n)
1688 continue;
1689
1690
1691
1692 QXmlStreamAttribute attribute;
1693 attribute.m_name = dtdAttribute.attributeName;
1694 attribute.m_qualifiedName = dtdAttribute.attributeQualifiedName;
1695 attribute.m_value = dtdAttribute.defaultValue;
1696
1697 if (!dtdAttribute.attributePrefix.isEmpty()) {
1698 XmlStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix);
1699 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1700 }
1701 attribute.m_isDefault = true;
1702 attributes.append(std::move(attribute));
1703 }
1704}
1705
1707{
1708 const Tag &tag = tagStack.top();
1709 qsizetype n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize;
1711 for (qsizetype i = 0; i < n; ++i) {
1712 const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i);
1713 QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i];
1714 publicNamespaceDeclaration.m_prefix = namespaceDeclaration.prefix;
1715 publicNamespaceDeclaration.m_namespaceUri = namespaceDeclaration.namespaceUri;
1716 }
1717}
1718
1720{
1722 for (qsizetype i = 0; i < notationDeclarations.size(); ++i) {
1724 QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i];
1725 publicNotationDeclaration.m_name = notationDeclaration.name;
1726 publicNotationDeclaration.m_systemId = notationDeclaration.systemId;
1727 publicNotationDeclaration.m_publicId = notationDeclaration.publicId;
1728
1729 }
1730 notationDeclarations.clear();
1732 for (qsizetype i = 0; i < entityDeclarations.size(); ++i) {
1734 QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i];
1735 publicEntityDeclaration.m_name = entityDeclaration.name;
1736 publicEntityDeclaration.m_notationName = entityDeclaration.notationName;
1737 publicEntityDeclaration.m_systemId = entityDeclaration.systemId;
1738 publicEntityDeclaration.m_publicId = entityDeclaration.publicId;
1739 publicEntityDeclaration.m_value = entityDeclaration.value;
1740 }
1741 entityDeclarations.clear();
1742 parameterEntityHash.clear();
1743}
1744
1746{
1747 bool ok = true;
1748 uint s;
1749 // ### add toXShort to XmlString?
1750 if (sym(symbolIndex).c == 'x')
1751 s = symString(symbolIndex, 1).view().toUInt(&ok, 16);
1752 else
1753 s = symString(symbolIndex).view().toUInt(&ok, 10);
1754
1755 ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff)
1756 || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint));
1757
1758 return ok ? s : 0;
1759}
1760
1761
1763{
1764//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
1765
1766 const char16_t *data = publicId.utf16();
1767 uchar c = 0;
1768 qsizetype i;
1769 for (i = publicId.size() - 1; i >= 0; --i) {
1770 if (data[i] < 256)
1771 switch ((c = data[i])) {
1772 case ' ': case '\n': case '\r': case '-': case '(': case ')':
1773 case '+': case ',': case '.': case '/': case ':': case '=':
1774 case '?': case ';': case '!': case '*': case '#': case '@':
1775 case '$': case '_': case '%': case '\'': case '\"':
1776 continue;
1777 default:
1779 continue;
1780 }
1781 break;
1782 }
1783 if (i >= 0)
1784 raiseWellFormedError(QXmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c))));
1785}
1786
1787/*
1788 Checks whether the document starts with an xml declaration. If it
1789 does, this function returns \c true; otherwise it sets up everything
1790 for a synthetic start document event and returns \c false.
1791 */
1793{
1795
1796 if (scanString(spell[XML], XML))
1797 return true;
1798
1799 type = QXmlStreamReader::StartDocument;
1800 if (atEnd) {
1802 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1803 }
1804 return false;
1805}
1806
1808{
1809 QString err;
1810 if (documentVersion != "1.0"_L1) {
1811 if (documentVersion.view().contains(u' '))
1812 err = QXmlStream::tr("Invalid XML version string.");
1813 else
1814 err = QXmlStream::tr("Unsupported XML version.");
1815 }
1816 qsizetype n = attributeStack.size();
1817
1818 /* We use this bool to ensure that the pesudo attributes are in the
1819 * proper order:
1820 *
1821 * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
1822
1823 for (qsizetype i = 0; err.isNull() && i < n; ++i) {
1824 Attribute &attrib = attributeStack[i];
1825 XmlStringRef prefix(symPrefix(attrib.key));
1826 XmlStringRef key(symString(attrib.key));
1827 XmlStringRef value(symString(attrib.value));
1828
1829 if (prefix.isEmpty() && key == "encoding"_L1) {
1831
1832 if (hasStandalone)
1833 err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding.");
1835 err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value);
1836 else {
1837 QByteArray enc = value.toString().toUtf8();
1838 if (!lockEncoding) {
1839 decoder = QStringDecoder(enc.constData());
1840 if (!decoder.isValid()) {
1841 err = QXmlStream::tr("Encoding %1 is unsupported").arg(value);
1842 } else {
1844 }
1845 }
1846 }
1847 } else if (prefix.isEmpty() && key == "standalone"_L1) {
1848 hasStandalone = true;
1849 if (value == "yes"_L1)
1850 standalone = true;
1851 else if (value == "no"_L1)
1852 standalone = false;
1853 else
1854 err = QXmlStream::tr("Standalone accepts only yes or no.");
1855 } else {
1856 err = QXmlStream::tr("Invalid attribute in XML declaration: %1 = %2").arg(key).arg(value);
1857 }
1858 }
1859
1860 if (!err.isNull())
1862 attributeStack.clear();
1863}
1864
1865
1866void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message)
1867{
1868 this->error = error;
1870 if (errorString.isNull()) {
1871 if (error == QXmlStreamReader::PrematureEndOfDocumentError)
1872 errorString = QXmlStream::tr("Premature end of document.");
1873 else if (error == QXmlStreamReader::CustomError)
1874 errorString = QXmlStream::tr("Invalid document.");
1875 }
1876
1877 type = QXmlStreamReader::Invalid;
1878}
1879
1881{
1882 raiseError(QXmlStreamReader::NotWellFormedError, message);
1883}
1884
1886{
1887 // TODO: add a ImplementationLimitsExceededError and use it instead
1888 raiseError(QXmlStreamReader::NotWellFormedError,
1889 QXmlStream::tr("Length of XML attribute name exceeds implementation limits (4KiB "
1890 "characters)."));
1891}
1892
1894{
1895
1896 if (token == EOF_SYMBOL) {
1897 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1898 return;
1899 }
1900 const int nmax = 4;
1901 QString error_message;
1902 int ers = state_stack[tos];
1903 int nexpected = 0;
1904 int expected[nmax];
1905 if (token != XML_ERROR)
1906 for (int tk = 0; tk < TERMINAL_COUNT; ++tk) {
1907 int k = t_action(ers, tk);
1908 if (k <= 0)
1909 continue;
1910 if (spell[tk]) {
1911 if (nexpected < nmax)
1912 expected[nexpected++] = tk;
1913 }
1914 }
1915
1916 if (nexpected && nexpected < nmax) {
1917 //: '<first option>'
1918 QString exp_str = QXmlStream::tr("'%1'", "expected")
1920 if (nexpected == 2) {
1921 //: <first option>, '<second option>'
1922 exp_str = QXmlStream::tr("%1 or '%2'", "expected")
1923 .arg(exp_str, QLatin1StringView(spell[expected[1]]));
1924 } else if (nexpected > 2) {
1925 int s = 1;
1926 for (; s < nexpected - 1; ++s) {
1927 //: <options so far>, '<next option>'
1928 exp_str = QXmlStream::tr("%1, '%2'", "expected")
1929 .arg(exp_str, QLatin1StringView(spell[expected[s]]));
1930 }
1931 //: <options so far>, or '<final option>'
1932 exp_str = QXmlStream::tr("%1, or '%2'", "expected")
1933 .arg(exp_str, QLatin1StringView(spell[expected[s]]));
1934 }
1935 error_message = QXmlStream::tr("Expected %1, but got '%2'.")
1936 .arg(exp_str, QLatin1StringView(spell[token]));
1937 } else {
1938 error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1StringView(spell[token]));
1939 }
1940
1941 raiseWellFormedError(error_message);
1942}
1943
1946 if (error == QXmlStreamReader::NoError)
1947 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1948}
1949
1954qint64 QXmlStreamReader::lineNumber() const
1955{
1956 Q_D(const QXmlStreamReader);
1957 return d->lineNumber + 1; // in public we start with 1
1958}
1959
1964qint64 QXmlStreamReader::columnNumber() const
1965{
1966 Q_D(const QXmlStreamReader);
1967 return d->characterOffset - d->lastLineStart + d->readBufferPos;
1968}
1969
1974qint64 QXmlStreamReader::characterOffset() const
1975{
1976 Q_D(const QXmlStreamReader);
1977 return d->characterOffset + d->readBufferPos;
1978}
1979
1980
1984QStringView QXmlStreamReader::text() const
1985{
1986 Q_D(const QXmlStreamReader);
1987 return d->text;
1988}
1989
1990
1997QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const
1998{
1999 Q_D(const QXmlStreamReader);
2000 if (d->notationDeclarations.size())
2001 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2003}
2004
2005
2012QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const
2013{
2014 Q_D(const QXmlStreamReader);
2015 if (d->entityDeclarations.size())
2016 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2018}
2019
2027QStringView QXmlStreamReader::dtdName() const
2028{
2029 Q_D(const QXmlStreamReader);
2030 if (d->type == QXmlStreamReader::DTD)
2031 return d->dtdName;
2032 return QStringView();
2033}
2034
2042QStringView QXmlStreamReader::dtdPublicId() const
2043{
2044 Q_D(const QXmlStreamReader);
2045 if (d->type == QXmlStreamReader::DTD)
2046 return d->dtdPublicId;
2047 return QStringView();
2048}
2049
2057QStringView QXmlStreamReader::dtdSystemId() const
2058{
2059 Q_D(const QXmlStreamReader);
2060 if (d->type == QXmlStreamReader::DTD)
2061 return d->dtdSystemId;
2062 return QStringView();
2063}
2064
2074int QXmlStreamReader::entityExpansionLimit() const
2075{
2076 Q_D(const QXmlStreamReader);
2077 return d->entityExpansionLimit;
2078}
2079
2095void QXmlStreamReader::setEntityExpansionLimit(int limit)
2096{
2097 Q_D(QXmlStreamReader);
2098 d->entityExpansionLimit = limit;
2099}
2100
2110QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const
2111{
2112 Q_D(const QXmlStreamReader);
2113 if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement)
2114 const_cast<QXmlStreamReaderPrivate *>(d)->resolvePublicNamespaces();
2116}
2117
2118
2129void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration)
2130{
2131 Q_D(QXmlStreamReader);
2132 QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
2133 namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix());
2134 namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri());
2135}
2136
2144void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations)
2145{
2146 for (const auto &extraNamespaceDeclaration : extraNamespaceDeclarations)
2147 addExtraNamespaceDeclaration(extraNamespaceDeclaration);
2148}
2149
2150
2168QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour)
2169{
2170 Q_D(QXmlStreamReader);
2171 if (isStartElement()) {
2173 forever {
2174 switch (readNext()) {
2175 case Characters:
2176 case EntityReference:
2177 result.insert(result.size(), d->text);
2178 break;
2179 case EndElement:
2180 return result;
2181 case ProcessingInstruction:
2182 case Comment:
2183 break;
2184 case StartElement:
2185 if (behaviour == SkipChildElements) {
2186 skipCurrentElement();
2187 break;
2188 } else if (behaviour == IncludeChildElements) {
2189 result += readElementText(behaviour);
2190 break;
2191 }
2192 Q_FALLTHROUGH();
2193 default:
2194 if (d->error || behaviour == ErrorOnUnexpectedElement) {
2195 if (!d->error)
2196 d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data."));
2197 return result;
2198 }
2199 }
2200 }
2201 }
2202 return QString();
2203}
2204
2209void QXmlStreamReader::raiseError(const QString& message)
2210{
2211 Q_D(QXmlStreamReader);
2212 d->raiseError(CustomError, message);
2213}
2214
2220QString QXmlStreamReader::errorString() const
2221{
2222 Q_D(const QXmlStreamReader);
2223 if (d->type == QXmlStreamReader::Invalid)
2224 return d->errorString;
2225 return QString();
2226}
2227
2232QXmlStreamReader::Error QXmlStreamReader::error() const
2233{
2234 Q_D(const QXmlStreamReader);
2235 if (d->type == QXmlStreamReader::Invalid)
2236 return d->error;
2237 return NoError;
2238}
2239
2243QStringView QXmlStreamReader::processingInstructionTarget() const
2244{
2245 Q_D(const QXmlStreamReader);
2246 return d->processingInstructionTarget;
2247}
2248
2252QStringView QXmlStreamReader::processingInstructionData() const
2253{
2254 Q_D(const QXmlStreamReader);
2255 return d->processingInstructionData;
2256}
2257
2258
2259
2265QStringView QXmlStreamReader::name() const
2266{
2267 Q_D(const QXmlStreamReader);
2268 return d->name;
2269}
2270
2276QStringView QXmlStreamReader::namespaceUri() const
2277{
2278 Q_D(const QXmlStreamReader);
2279 return d->namespaceUri;
2280}
2281
2294QStringView QXmlStreamReader::qualifiedName() const
2295{
2296 Q_D(const QXmlStreamReader);
2297 return d->qualifiedName;
2298}
2299
2300
2301
2309QStringView QXmlStreamReader::prefix() const
2310{
2311 Q_D(const QXmlStreamReader);
2312 return d->prefix;
2313}
2314
2318QXmlStreamAttributes QXmlStreamReader::attributes() const
2319{
2320 Q_D(const QXmlStreamReader);
2321 return d->attributes;
2322}
2323
2324#endif // feature xmlstreamreader
2325
2346QXmlStreamAttribute::QXmlStreamAttribute()
2347{
2348 m_isDefault = false;
2349}
2350
2354QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)
2355{
2356 m_namespaceUri = namespaceUri;
2357 m_name = m_qualifiedName = name;
2358 m_value = value;
2359 m_namespaceUri = namespaceUri;
2360}
2361
2365QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value)
2366{
2367 qsizetype colon = qualifiedName.indexOf(u':');
2368 m_name = qualifiedName.mid(colon + 1);
2369 m_qualifiedName = qualifiedName;
2370 m_value = value;
2371}
2372
2475QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration()
2476{
2477}
2478
2537QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration()
2538{
2539}
2540
2546QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri)
2547{
2548 m_prefix = prefix;
2549 m_namespaceUri = namespaceUri;
2550}
2551
2595QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration()
2596{
2597}
2598
2638QStringView QXmlStreamAttributes::value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
2639{
2640 for (const QXmlStreamAttribute &attribute : *this) {
2641 if (attribute.name() == name && attribute.namespaceUri() == namespaceUri)
2642 return attribute.value();
2643 }
2644 return QStringView();
2645}
2646
2663QStringView QXmlStreamAttributes::value(QAnyStringView qualifiedName) const noexcept
2664{
2665 for (const QXmlStreamAttribute &attribute : *this) {
2666 if (attribute.qualifiedName() == qualifiedName)
2667 return attribute.value();
2668 }
2669 return QStringView();
2670}
2671
2676void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value)
2677{
2678 append(QXmlStreamAttribute(namespaceUri, name, value));
2679}
2680
2685void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value)
2686{
2687 append(QXmlStreamAttribute(qualifiedName, value));
2688}
2689
2690#if QT_CONFIG(xmlstreamreader)
2691
2727bool QXmlStreamReader::isWhitespace() const
2728{
2729 Q_D(const QXmlStreamReader);
2730 return d->type == QXmlStreamReader::Characters && d->isWhitespace;
2731}
2732
2738bool QXmlStreamReader::isCDATA() const
2739{
2740 Q_D(const QXmlStreamReader);
2741 return d->type == QXmlStreamReader::Characters && d->isCDATA;
2742}
2743
2744
2745
2754bool QXmlStreamReader::isStandaloneDocument() const
2755{
2756 Q_D(const QXmlStreamReader);
2757 return d->standalone;
2758}
2759
2770bool QXmlStreamReader::hasStandaloneDeclaration() const
2771{
2772 Q_D(const QXmlStreamReader);
2773 return d->hasStandalone;
2774}
2775
2783QStringView QXmlStreamReader::documentVersion() const
2784{
2785 Q_D(const QXmlStreamReader);
2786 if (d->type == QXmlStreamReader::StartDocument)
2787 return d->documentVersion;
2788 return QStringView();
2789}
2790
2798QStringView QXmlStreamReader::documentEncoding() const
2799{
2800 Q_D(const QXmlStreamReader);
2801 if (d->type == QXmlStreamReader::StartDocument)
2802 return d->documentEncoding;
2803 return QStringView();
2804}
2805
2806#endif // feature xmlstreamreader
2807
2883#if QT_CONFIG(xmlstreamwriter)
2884
2885class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
2886{
2887 QXmlStreamWriter *q_ptr;
2888 Q_DECLARE_PUBLIC(QXmlStreamWriter)
2889public:
2890 enum class StartElementOption {
2891 KeepEverything = 0, // write out every attribute, namespace, &c.
2892 OmitNamespaceDeclarations = 1,
2893 };
2894
2895 QXmlStreamWriterPrivate(QXmlStreamWriter *q);
2896 ~QXmlStreamWriterPrivate() {
2897 if (deleteDevice)
2898 delete device;
2899 }
2900
2901 void write(QAnyStringView s);
2902 void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
2903 bool finishStartElement(bool contents = true);
2905 StartElementOption option = StartElementOption::KeepEverything);
2907 QString *stringDevice;
2908 uint deleteDevice :1;
2909 uint inStartElement :1;
2910 uint inEmptyElement :1;
2911 uint lastWasStartElement :1;
2912 uint wroteSomething :1;
2913 uint hasIoError :1;
2914 uint hasEncodingError :1;
2915 uint autoFormatting :1;
2916 std::string autoFormattingIndent;
2917 NamespaceDeclaration emptyNamespace;
2918 qsizetype lastNamespaceDeclaration;
2919
2920 NamespaceDeclaration &addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix);
2921 NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
2922 void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration);
2923
2924 int namespacePrefixCount;
2925
2926 void indent(int level);
2927private:
2928 void doWriteToDevice(QStringView s);
2929 void doWriteToDevice(QUtf8StringView s);
2930 void doWriteToDevice(QLatin1StringView s);
2931};
2932
2933
2934QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
2935 : autoFormattingIndent(4, ' ')
2936{
2937 q_ptr = q;
2938 device = nullptr;
2939 stringDevice = nullptr;
2940 deleteDevice = false;
2941 inStartElement = inEmptyElement = false;
2942 wroteSomething = false;
2943 hasIoError = false;
2944 hasEncodingError = false;
2945 lastWasStartElement = false;
2946 lastNamespaceDeclaration = 1;
2947 autoFormatting = false;
2948 namespacePrefixCount = 0;
2949}
2950
2951void QXmlStreamWriterPrivate::write(QAnyStringView s)
2952{
2953 if (device) {
2954 if (hasIoError)
2955 return;
2956
2957 s.visit([&] (auto s) { doWriteToDevice(s); });
2958 } else if (stringDevice) {
2959 s.visit([&] (auto s) { stringDevice->append(s); });
2960 } else {
2961 qWarning("QXmlStreamWriter: No device");
2962 }
2963}
2964
2965void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
2966{
2967 struct NextLatin1 {
2968 char32_t operator()(const char *&it, const char *) const
2969 { return uchar(*it++); }
2970 };
2971 struct NextUtf8 {
2972 char32_t operator()(const char *&it, const char *end) const
2973 {
2974 uchar uc = *it++;
2975 char32_t utf32 = 0;
2976 char32_t *output = &utf32;
2977 qsizetype n = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(uc, output, it, end);
2978 return n < 0 ? 0 : utf32;
2979 }
2980 };
2981 struct NextUtf16 {
2982 char32_t operator()(const QChar *&it, const QChar *end) const
2983 {
2984 QStringIterator decoder(it, end);
2985 char32_t result = decoder.next(u'\0');
2986 it = decoder.position();
2987 return result;
2988 }
2989 };
2990
2991 QString escaped;
2992 escaped.reserve(s.size());
2993 s.visit([&] (auto s) {
2994 using View = decltype(s);
2995 using Decoder = std::conditional_t<std::is_same_v<View, QLatin1StringView>, NextLatin1,
2996 std::conditional_t<std::is_same_v<View, QUtf8StringView>, NextUtf8, NextUtf16>>;
2997
2998 auto it = s.begin();
2999 const auto end = s.end();
3000 Decoder decoder;
3001
3002 while (it != end) {
3003 QLatin1StringView replacement;
3004 auto mark = it;
3005
3006 while (it != end) {
3007 auto next_it = it;
3008 char32_t uc = decoder(next_it, end);
3009 if (uc == u'<') {
3010 replacement = "&lt;"_L1;
3011 break;
3012 } else if (uc == u'>') {
3013 replacement = "&gt;"_L1;
3014 break;
3015 } else if (uc == u'&') {
3016 replacement = "&amp;"_L1;
3017 break;
3018 } else if (uc == u'\"') {
3019 replacement = "&quot;"_L1;
3020 break;
3021 } else if (uc == u'\t') {
3022 if (escapeWhitespace) {
3023 replacement = "&#9;"_L1;
3024 break;
3025 }
3026 } else if (uc == u'\n') {
3027 if (escapeWhitespace) {
3028 replacement = "&#10;"_L1;
3029 break;
3030 }
3031 } else if (uc == u'\v' || uc == u'\f') {
3032 hasEncodingError = true;
3033 break;
3034 } else if (uc == u'\r') {
3035 if (escapeWhitespace) {
3036 replacement = "&#13;"_L1;
3037 break;
3038 }
3039 } else if (uc <= u'\x1F' || uc == u'\uFFFE' || uc == u'\uFFFF') {
3040 hasEncodingError = true;
3041 break;
3042 }
3043 it = next_it;
3044 }
3045
3046 escaped.append(View{mark, it});
3047 escaped.append(replacement);
3048 if (it != end)
3049 ++it;
3050 }
3051 } );
3052
3053 write(escaped);
3054}
3055
3056void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) {
3057 if (namespaceDeclaration.prefix.isEmpty()) {
3058 write(" xmlns=\"");
3059 write(namespaceDeclaration.namespaceUri);
3060 write("\"");
3061 } else {
3062 write(" xmlns:");
3063 write(namespaceDeclaration.prefix);
3064 write("=\"");
3065 write(namespaceDeclaration.namespaceUri);
3066 write("\"");
3067 }
3068}
3069
3070bool QXmlStreamWriterPrivate::finishStartElement(bool contents)
3071{
3072 bool hadSomethingWritten = wroteSomething;
3073 wroteSomething = contents;
3074 if (!inStartElement)
3075 return hadSomethingWritten;
3076
3077 if (inEmptyElement) {
3078 write("/>");
3079 QXmlStreamWriterPrivate::Tag tag = tagStack_pop();
3080 lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3081 lastWasStartElement = false;
3082 } else {
3083 write(">");
3084 }
3085 inStartElement = inEmptyElement = false;
3086 lastNamespaceDeclaration = namespaceDeclarations.size();
3087 return hadSomethingWritten;
3088}
3089
3091QXmlStreamWriterPrivate::addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
3092{
3093 const bool prefixIsXml = prefix == "xml"_L1;
3094 const bool namespaceUriIsXml = namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1;
3095 if (prefixIsXml && !namespaceUriIsXml) {
3096 qWarning("Reserved prefix 'xml' must not be bound to a different namespace name "
3097 "than 'http://www.w3.org/XML/1998/namespace'");
3098 } else if (!prefixIsXml && namespaceUriIsXml) {
3099 const QString prefixString = prefix.toString();
3100 qWarning("The prefix '%ls' must not be bound to namespace name "
3101 "'http://www.w3.org/XML/1998/namespace' which 'xml' is already bound to",
3102 qUtf16Printable(prefixString));
3103 }
3104 if (namespaceUri == "http://www.w3.org/2000/xmlns/"_L1) {
3105 const QString prefixString = prefix.toString();
3106 qWarning("The prefix '%ls' must not be bound to namespace name "
3107 "'http://www.w3.org/2000/xmlns/'",
3108 qUtf16Printable(prefixString));
3109 }
3110 auto &namespaceDeclaration = namespaceDeclarations.push();
3111 namespaceDeclaration.prefix = addToStringStorage(prefix);
3112 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3113 return namespaceDeclaration;
3114}
3115
3116QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(QAnyStringView namespaceUri, bool writeDeclaration, bool noDefault)
3117{
3118 for (NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
3119 if (namespaceDeclaration.namespaceUri == namespaceUri) {
3120 if (!noDefault || !namespaceDeclaration.prefix.isEmpty())
3121 return namespaceDeclaration;
3122 }
3123 }
3124 if (namespaceUri.isEmpty())
3125 return emptyNamespace;
3126 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
3127 if (namespaceUri.isEmpty()) {
3128 namespaceDeclaration.prefix.clear();
3129 } else {
3130 QString s;
3131 int n = ++namespacePrefixCount;
3132 forever {
3133 s = u'n' + QString::number(n++);
3134 qsizetype j = namespaceDeclarations.size() - 2;
3135 while (j >= 0 && namespaceDeclarations.at(j).prefix != s)
3136 --j;
3137 if (j < 0)
3138 break;
3139 }
3140 namespaceDeclaration.prefix = addToStringStorage(s);
3141 }
3142 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3143 if (writeDeclaration)
3144 writeNamespaceDeclaration(namespaceDeclaration);
3145 return namespaceDeclaration;
3146}
3147
3148
3149
3150void QXmlStreamWriterPrivate::indent(int level)
3151{
3152 write("\n");
3153 for (int i = 0; i < level; ++i)
3154 write(autoFormattingIndent);
3155}
3156
3157void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
3158{
3159 constexpr qsizetype MaxChunkSize = 512;
3160 char buffer [3 * MaxChunkSize];
3162 while (!s.isEmpty()) {
3163 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3164 char *end = QUtf8::convertFromUnicode(buffer, s.first(chunkSize), &state);
3165 doWriteToDevice(QUtf8StringView{buffer, end});
3166 s = s.sliced(chunkSize);
3167 }
3168 if (state.remainingChars > 0)
3169 hasEncodingError = true;
3170}
3171
3172void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
3173{
3174 QByteArrayView bytes = s;
3175 if (device->write(bytes.data(), bytes.size()) != bytes.size())
3176 hasIoError = true;
3177}
3178
3179void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
3180{
3181 constexpr qsizetype MaxChunkSize = 512;
3182 char buffer [2 * MaxChunkSize];
3183 while (!s.isEmpty()) {
3184 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3185 char *end = QUtf8::convertFromLatin1(buffer, s.first(chunkSize));
3186 doWriteToDevice(QUtf8StringView{buffer, end});
3187 s = s.sliced(chunkSize);
3188 }
3189}
3190
3196QXmlStreamWriter::QXmlStreamWriter()
3197 : d_ptr(new QXmlStreamWriterPrivate(this))
3198{
3199}
3200
3204QXmlStreamWriter::QXmlStreamWriter(QIODevice *device)
3205 : d_ptr(new QXmlStreamWriterPrivate(this))
3206{
3207 Q_D(QXmlStreamWriter);
3208 d->device = device;
3209}
3210
3215QXmlStreamWriter::QXmlStreamWriter(QByteArray *array)
3216 : d_ptr(new QXmlStreamWriterPrivate(this))
3217{
3218 Q_D(QXmlStreamWriter);
3219 d->device = new QBuffer(array);
3220 d->device->open(QIODevice::WriteOnly);
3221 d->deleteDevice = true;
3222}
3223
3224
3228QXmlStreamWriter::QXmlStreamWriter(QString *string)
3229 : d_ptr(new QXmlStreamWriterPrivate(this))
3230{
3231 Q_D(QXmlStreamWriter);
3232 d->stringDevice = string;
3233}
3234
3238QXmlStreamWriter::~QXmlStreamWriter()
3239{
3240}
3241
3242
3249void QXmlStreamWriter::setDevice(QIODevice *device)
3250{
3251 Q_D(QXmlStreamWriter);
3252 if (device == d->device)
3253 return;
3254 d->stringDevice = nullptr;
3255 if (d->deleteDevice) {
3256 delete d->device;
3257 d->deleteDevice = false;
3258 }
3259 d->device = device;
3260}
3261
3268QIODevice *QXmlStreamWriter::device() const
3269{
3270 Q_D(const QXmlStreamWriter);
3271 return d->device;
3272}
3273
3298void QXmlStreamWriter::setAutoFormatting(bool enable)
3299{
3300 Q_D(QXmlStreamWriter);
3301 d->autoFormatting = enable;
3302}
3303
3309bool QXmlStreamWriter::autoFormatting() const
3310{
3311 Q_D(const QXmlStreamWriter);
3312 return d->autoFormatting;
3313}
3314
3329void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs)
3330{
3331 Q_D(QXmlStreamWriter);
3332 d->autoFormattingIndent.assign(size_t(qAbs(spacesOrTabs)), spacesOrTabs >= 0 ? ' ' : '\t');
3333}
3334
3335int QXmlStreamWriter::autoFormattingIndent() const
3336{
3337 Q_D(const QXmlStreamWriter);
3338 const QLatin1StringView indent(d->autoFormattingIndent);
3339 return indent.count(u' ') - indent.count(u'\t');
3340}
3341
3351bool QXmlStreamWriter::hasError() const
3352{
3353 Q_D(const QXmlStreamWriter);
3354 return d->hasIoError || d->hasEncodingError;
3355}
3356
3368void QXmlStreamWriter::writeAttribute(QAnyStringView qualifiedName, QAnyStringView value)
3369{
3370 Q_D(QXmlStreamWriter);
3371 Q_ASSERT(d->inStartElement);
3372 Q_ASSERT(count(qualifiedName, ':') <= 1);
3373 d->write(" ");
3374 d->write(qualifiedName);
3375 d->write("=\"");
3376 d->writeEscaped(value, true);
3377 d->write("\"");
3378}
3379
3391void QXmlStreamWriter::writeAttribute(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView value)
3392{
3393 Q_D(QXmlStreamWriter);
3394 Q_ASSERT(d->inStartElement);
3395 Q_ASSERT(!contains(name, ':'));
3396 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true);
3397 d->write(" ");
3398 if (!namespaceDeclaration.prefix.isEmpty()) {
3399 d->write(namespaceDeclaration.prefix);
3400 d->write(":");
3401 }
3402 d->write(name);
3403 d->write("=\"");
3404 d->writeEscaped(value, true);
3405 d->write("\"");
3406}
3407
3416void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute)
3417{
3418 if (attribute.namespaceUri().isEmpty())
3419 writeAttribute(attribute.qualifiedName(), attribute.value());
3420 else
3421 writeAttribute(attribute.namespaceUri(), attribute.name(), attribute.value());
3422}
3423
3424
3434void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
3435{
3436 Q_D(QXmlStreamWriter);
3437 Q_ASSERT(d->inStartElement);
3438 Q_UNUSED(d);
3439 for (const auto &attr : attributes)
3440 writeAttribute(attr);
3441}
3442
3443
3455void QXmlStreamWriter::writeCDATA(QAnyStringView text)
3456{
3457 Q_D(QXmlStreamWriter);
3458 d->finishStartElement();
3459 d->write("<![CDATA[");
3460 while (!text.isEmpty()) {
3461 const auto idx = indexOf(text, "]]>"_L1);
3462 if (idx < 0)
3463 break; // no forbidden sequence found
3464 d->write(text.first(idx));
3465 d->write("]]" // text[idx, idx + 2)
3466 "]]><![CDATA[" // escape sequence to separate ]] and >
3467 ">"); // text[idx + 2, idx + 3)
3468 text = text.sliced(idx + 3); // skip over "]]>"
3469 }
3470 d->write(text); // write remainder
3471 d->write("]]>");
3472}
3473
3474
3484void QXmlStreamWriter::writeCharacters(QAnyStringView text)
3485{
3486 Q_D(QXmlStreamWriter);
3487 d->finishStartElement();
3488 d->writeEscaped(text);
3489}
3490
3491
3499void QXmlStreamWriter::writeComment(QAnyStringView text)
3500{
3501 Q_D(QXmlStreamWriter);
3502 Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
3503 if (!d->finishStartElement(false) && d->autoFormatting)
3504 d->indent(d->tagStack.size());
3505 d->write("<!--");
3506 d->write(text);
3507 d->write("-->");
3508 d->inStartElement = d->lastWasStartElement = false;
3509}
3510
3511
3518void QXmlStreamWriter::writeDTD(QAnyStringView dtd)
3519{
3520 Q_D(QXmlStreamWriter);
3521 d->finishStartElement();
3522 if (d->autoFormatting)
3523 d->write("\n");
3524 d->write(dtd);
3525 if (d->autoFormatting)
3526 d->write("\n");
3527}
3528
3529
3530
3538void QXmlStreamWriter::writeEmptyElement(QAnyStringView qualifiedName)
3539{
3540 Q_D(QXmlStreamWriter);
3541 Q_ASSERT(count(qualifiedName, ':') <= 1);
3542 d->writeStartElement({}, qualifiedName);
3543 d->inEmptyElement = true;
3544}
3545
3546
3557void QXmlStreamWriter::writeEmptyElement(QAnyStringView namespaceUri, QAnyStringView name)
3558{
3559 Q_D(QXmlStreamWriter);
3560 Q_ASSERT(!contains(name, ':'));
3561 d->writeStartElement(namespaceUri, name);
3562 d->inEmptyElement = true;
3563}
3564
3565
3576void QXmlStreamWriter::writeTextElement(QAnyStringView qualifiedName, QAnyStringView text)
3577{
3578 writeStartElement(qualifiedName);
3581}
3582
3595void QXmlStreamWriter::writeTextElement(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView text)
3596{
3597 writeStartElement(namespaceUri, name);
3600}
3601
3602
3608void QXmlStreamWriter::writeEndDocument()
3609{
3610 Q_D(QXmlStreamWriter);
3611 while (d->tagStack.size())
3613 d->write("\n");
3614}
3615
3621void QXmlStreamWriter::writeEndElement()
3622{
3623 Q_D(QXmlStreamWriter);
3624 if (d->tagStack.isEmpty())
3625 return;
3626
3627 // shortcut: if nothing was written, close as empty tag
3628 if (d->inStartElement && !d->inEmptyElement) {
3629 d->write("/>");
3630 d->lastWasStartElement = d->inStartElement = false;
3631 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
3632 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3633 return;
3634 }
3635
3636 if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting)
3637 d->indent(d->tagStack.size()-1);
3638 if (d->tagStack.isEmpty())
3639 return;
3640 d->lastWasStartElement = false;
3641 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
3642 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3643 d->write("</");
3644 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
3645 d->write(tag.namespaceDeclaration.prefix);
3646 d->write(":");
3647 }
3648 d->write(tag.name);
3649 d->write(">");
3650}
3651
3652
3653
3660void QXmlStreamWriter::writeEntityReference(QAnyStringView name)
3661{
3662 Q_D(QXmlStreamWriter);
3663 d->finishStartElement();
3664 d->write("&");
3665 d->write(name);
3666 d->write(";");
3667}
3668
3669
3687void QXmlStreamWriter::writeNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
3688{
3689 Q_D(QXmlStreamWriter);
3690 Q_ASSERT(prefix != "xmlns"_L1);
3691 if (prefix.isEmpty()) {
3692 d->findNamespace(namespaceUri, d->inStartElement);
3693 } else {
3694 auto &namespaceDeclaration = d->addExtraNamespace(namespaceUri, prefix);
3695 if (d->inStartElement)
3696 d->writeNamespaceDeclaration(namespaceDeclaration);
3697 }
3698}
3699
3700
3714void QXmlStreamWriter::writeDefaultNamespace(QAnyStringView namespaceUri)
3715{
3716 Q_D(QXmlStreamWriter);
3717 Q_ASSERT(namespaceUri != "http://www.w3.org/XML/1998/namespace"_L1);
3718 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
3719 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
3720 namespaceDeclaration.prefix.clear();
3721 namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri);
3722 if (d->inStartElement)
3723 d->writeNamespaceDeclaration(namespaceDeclaration);
3724}
3725
3726
3734void QXmlStreamWriter::writeProcessingInstruction(QAnyStringView target, QAnyStringView data)
3735{
3736 Q_D(QXmlStreamWriter);
3737 Q_ASSERT(!contains(data, "?>"_L1));
3738 if (!d->finishStartElement(false) && d->autoFormatting)
3739 d->indent(d->tagStack.size());
3740 d->write("<?");
3741 d->write(target);
3742 if (!data.isNull()) {
3743 d->write(" ");
3744 d->write(data);
3745 }
3746 d->write("?>");
3747}
3748
3749
3750
3758void QXmlStreamWriter::writeStartDocument()
3759{
3760 writeStartDocument("1.0"_L1);
3761}
3762
3763
3772void QXmlStreamWriter::writeStartDocument(QAnyStringView version)
3773{
3774 Q_D(QXmlStreamWriter);
3775 d->finishStartElement(false);
3776 d->write("<?xml version=\"");
3777 d->write(version);
3778 if (d->device) // stringDevice does not get any encoding
3779 d->write("\" encoding=\"UTF-8");
3780 d->write("\"?>");
3781}
3782
3792void QXmlStreamWriter::writeStartDocument(QAnyStringView version, bool standalone)
3793{
3794 Q_D(QXmlStreamWriter);
3795 d->finishStartElement(false);
3796 d->write("<?xml version=\"");
3797 d->write(version);
3798 if (d->device) // stringDevice does not get any encoding
3799 d->write("\" encoding=\"UTF-8");
3800 if (standalone)
3801 d->write("\" standalone=\"yes\"?>");
3802 else
3803 d->write("\" standalone=\"no\"?>");
3804}
3805
3806
3817void QXmlStreamWriter::writeStartElement(QAnyStringView qualifiedName)
3818{
3819 Q_D(QXmlStreamWriter);
3820 Q_ASSERT(count(qualifiedName, ':') <= 1);
3821 d->writeStartElement({}, qualifiedName);
3822}
3823
3824
3836void QXmlStreamWriter::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
3837{
3838 Q_D(QXmlStreamWriter);
3839 Q_ASSERT(!contains(name, ':'));
3840 d->writeStartElement(namespaceUri, name);
3841}
3842
3843void QXmlStreamWriterPrivate::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name,
3844 StartElementOption option)
3845{
3846 if (!finishStartElement(false) && autoFormatting)
3847 indent(tagStack.size());
3848
3849 Tag &tag = tagStack_push();
3850 tag.name = addToStringStorage(name);
3851 tag.namespaceDeclaration = findNamespace(namespaceUri);
3852 write("<");
3853 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
3854 write(tag.namespaceDeclaration.prefix);
3855 write(":");
3856 }
3857 write(tag.name);
3858 inStartElement = lastWasStartElement = true;
3859
3860 if (option != StartElementOption::OmitNamespaceDeclarations) {
3861 for (qsizetype i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i)
3862 writeNamespaceDeclaration(namespaceDeclarations[i]);
3863 }
3864 tag.namespaceDeclarationsSize = lastNamespaceDeclaration;
3865}
3866
3867#if QT_CONFIG(xmlstreamreader)
3875void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
3876{
3877 Q_D(QXmlStreamWriter);
3878 switch (reader.tokenType()) {
3879 case QXmlStreamReader::NoToken:
3880 break;
3881 case QXmlStreamReader::StartDocument:
3882 writeStartDocument();
3883 break;
3884 case QXmlStreamReader::EndDocument:
3885 writeEndDocument();
3886 break;
3887 case QXmlStreamReader::StartElement: {
3888 // Namespaces must be added before writeStartElement is called so new prefixes are found
3889 QList<QXmlStreamPrivateTagStack::NamespaceDeclaration> extraNamespaces;
3890 for (const auto &namespaceDeclaration : reader.namespaceDeclarations()) {
3891 auto &extraNamespace = d->addExtraNamespace(namespaceDeclaration.namespaceUri(),
3892 namespaceDeclaration.prefix());
3893 extraNamespaces.append(extraNamespace);
3894 }
3895 d->writeStartElement(
3896 reader.namespaceUri(), reader.name(),
3897 QXmlStreamWriterPrivate::StartElementOption::OmitNamespaceDeclarations);
3898 // Namespace declarations are written afterwards
3899 for (const auto &extraNamespace : std::as_const(extraNamespaces))
3900 d->writeNamespaceDeclaration(extraNamespace);
3901 writeAttributes(reader.attributes());
3902 } break;
3903 case QXmlStreamReader::EndElement:
3905 break;
3906 case QXmlStreamReader::Characters:
3907 if (reader.isCDATA())
3908 writeCDATA(reader.text());
3909 else
3911 break;
3912 case QXmlStreamReader::Comment:
3913 writeComment(reader.text());
3914 break;
3915 case QXmlStreamReader::DTD:
3916 writeDTD(reader.text());
3917 break;
3918 case QXmlStreamReader::EntityReference:
3919 writeEntityReference(reader.name());
3920 break;
3921 case QXmlStreamReader::ProcessingInstruction:
3922 writeProcessingInstruction(reader.processingInstructionTarget(),
3923 reader.processingInstructionData());
3924 break;
3925 default:
3926 Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid);
3927 qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state.");
3928 break;
3929 }
3930}
3931
3932static constexpr bool isTokenAllowedInContext(QXmlStreamReader::TokenType type,
3934{
3935 switch (type) {
3936 case QXmlStreamReader::StartDocument:
3937 case QXmlStreamReader::DTD:
3939
3940 case QXmlStreamReader::StartElement:
3941 case QXmlStreamReader::EndElement:
3942 case QXmlStreamReader::Characters:
3943 case QXmlStreamReader::EntityReference:
3944 case QXmlStreamReader::EndDocument:
3946
3947 case QXmlStreamReader::Comment:
3948 case QXmlStreamReader::ProcessingInstruction:
3949 return true;
3950
3951 case QXmlStreamReader::NoToken:
3952 case QXmlStreamReader::Invalid:
3953 return false;
3954 }
3955
3956 // GCC 8.x does not treat __builtin_unreachable() as constexpr
3957#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
3958 Q_UNREACHABLE_RETURN(false);
3959#else
3960 return false;
3961#endif
3962}
3963
3971bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type)
3972{
3973 // Don't change currentContext, if Invalid or NoToken occur in the prolog
3974 if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken)
3975 return false;
3976
3977 // If a token type gets rejected in the body, there is no recovery
3978 const bool result = isTokenAllowedInContext(type, currentContext);
3979 if (result || currentContext == XmlContext::Body)
3980 return result;
3981
3982 // First non-Prolog token observed => switch context to body and check again.
3983 currentContext = XmlContext::Body;
3984 return isTokenAllowedInContext(type, currentContext);
3985}
3986
3993{
3994 Q_Q(QXmlStreamReader);
3995
3996 // The token type must be consumed, to keep track if the body has been reached.
3997 const XmlContext context = currentContext;
3998 const bool ok = isValidToken(type);
3999
4000 // Do nothing if an error has been raised already (going along with an unexpected token)
4001 if (error != QXmlStreamReader::Error::NoError)
4002 return;
4003
4004 if (!ok) {
4005 raiseError(QXmlStreamReader::UnexpectedElementError,
4006 QXmlStream::tr("Unexpected token type %1 in %2.")
4007 .arg(q->tokenString(), contextString(context)));
4008 return;
4009 }
4010
4011 if (type != QXmlStreamReader::DTD)
4012 return;
4013
4014 // Raise error on multiple DTD tokens
4015 if (foundDTD) {
4016 raiseError(QXmlStreamReader::UnexpectedElementError,
4017 QXmlStream::tr("Found second DTD token in %1.").arg(contextString(context)));
4018 } else {
4019 foundDTD = true;
4020 }
4021}
4022
4047#endif // feature xmlstreamreader
4048#endif // feature xmlstreamwriter
4049
4051
4052#endif // feature xmlstream
IOBluetoothDevice * device
\inmodule QtCore
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1218
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
\inmodule QtCore \reentrant
Definition qbuffer.h:16
constexpr qsizetype size() const noexcept
constexpr const_pointer data() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void clear()
Clears the contents of the byte array and makes it null.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\inmodule QtCore
\inmodule QtCore \reentrant
Definition qiodevice.h:34
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
QString text(const QString &key) const
iterator begin()
Definition qset.h:136
bool hasError() const noexcept
Returns true if a conversion could not correctly convert a character.
bool isValid() const noexcept
Returns true if this is a valid string converter that can be used for encoding or decoding text.
static Q_CORE_EXPORT std::optional< Encoding > encodingForData(QByteArrayView data, char16_t expectedFirstCharacter=0) noexcept
Returns the encoding for the content of data if it can be determined.
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
constexpr const storage_type * utf16() const noexcept
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
uint toUInt(bool *ok=nullptr, int base=10) const
Returns the string view converted to an {unsigned int} using base base, which is 10 by default and mu...
Definition qstring.h:1134
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
QString sliced(qsizetype pos) const &
Definition qstring.h:394
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:994
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
QString first(qsizetype n) const &
Definition qstring.h:390
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
void resize(qsizetype size)
Sets the size of the string to size characters.
Definition qstring.cpp:2668
static int t_action(int state, int token)
static const char *const spell[]
qsizetype initialTagStackStringStorageSize
QXmlStreamSimpleStack< NamespaceDeclaration > namespaceDeclarations
QXmlStreamSimpleStack< Tag > tagStack
QXmlStreamReaderPrivate(QXmlStreamReader *q)
void parseEntity(const QString &value)
void raiseError(QXmlStreamReader::Error error, const QString &message=QString())
XmlStringRef documentVersion
void resume(int rule)
bool scanUntil(const char *str, short tokenToInject=-1)
XmlStringRef symPrefix(int index)
void checkPublicLiteral(QStringView publicId)
bool isValidToken(QXmlStreamReader::TokenType type)
qsizetype fastScanLiteralContent()
QXmlStreamEntityDeclarations publicEntityDeclarations
QString resolveUndeclaredEntity(const QString &name)
std::optional< qsizetype > fastScanName(Value *val=nullptr)
QXmlStreamAttributes attributes
void putString(QStringView s, qsizetype from=0)
Value & sym(int index) const
uint resolveCharRef(int symbolIndex)
QXmlStreamSimpleStack< EntityDeclaration > entityDeclarations
qsizetype fastScanNMTOKEN()
void putStringLiteral(QStringView s)
void putReplacement(QStringView s)
std::unique_ptr< QXmlStreamReaderPrivate > entityParser
XmlStringRef symName(int index)
void putReplacementInAttributeValue(QStringView s)
QXmlStreamSimpleStack< Attribute > attributeStack
XmlStringRef namespaceForPrefix(QStringView prefix)
QXmlStreamSimpleStack< uint > putStack
QHash< QStringView, Entity > parameterEntityHash
void raiseWellFormedError(const QString &message)
QXmlStreamNotationDeclarations publicNotationDeclarations
XmlStringRef documentEncoding
QXmlStreamNamespaceDeclarations publicNamespaceDeclarations
QXmlStreamSimpleStack< DtdAttribute > dtdAttributes
qsizetype fastScanContentCharList()
void injectToken(ushort tokenToInject)
XmlStringRef symString(int index)
QXmlStreamReader::Error error
QXmlStreamSimpleStack< NotationDeclaration > notationDeclarations
bool scanString(const char *str, short tokenToInject, bool requireSpace=true)
const T & top() const
void reserve(qsizetype extraCapacity)
qsizetype size() const
static bool isEncName(QStringView encName)
QStringView view() const
#define this
Definition dialogs.cpp:9
QJSValue expected
Definition qjsengine.cpp:12
QString str
[2]
QString text
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
int toUtf8(char16_t u, OutputPtr &dst, InputPtr &src, InputPtr end)
constexpr bool isAsciiLetterOrNumber(char32_t c) noexcept
Definition qtools_p.h:82
constexpr O reversed(O o) noexcept
Definition qcompare.h:53
\macro QT_NO_KEYWORDS >
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool endsWith(QByteArrayView haystack, QByteArrayView needle) noexcept
QImageReader reader("image.png")
[1]
static void * context
static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute)
#define Q_FALLTHROUGH()
AudioChannelLayoutTag tag
DBusConnection const char * rule
DBusConnection const char DBusError * error
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT EGLint attribute
quint32 Tag
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:166
@ Invalid
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
constexpr auto qOffsetStringArray(const char(&...strings)[Nx]) noexcept
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum GLuint GLint level
GLuint64 key
GLboolean r
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLenum type
GLenum target
GLboolean enable
GLuint GLsizei const GLchar * message
GLuint name
GLint first
GLfloat n
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLuint GLfloat * val
GLenum array
GLint limit
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLuint GLenum option
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
GLbitfield GLuint readBuffer
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int void * arg
#define qUtf16Printable(string)
Definition qstring.h:1543
@ NoError
Definition main.cpp:34
#define BUFFER_SIZE
Definition main.cpp:31
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
short qint16
Definition qtypes.h:47
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
QT_BEGIN_NAMESPACE typedef uchar * output
Q_CHECK_PTR(a=new int[80])
gzip write("uncompressed data")
list indexOf("B")
writeEndElement()
writeCharacters(text)
writeStartElement(qualifiedName)
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18
static Q_CORE_EXPORT QByteArray convertFromUnicode(QStringView in)
static Q_CORE_EXPORT char * convertFromLatin1(char *out, QLatin1StringView in)