Qt 6.x
The Qt SDK
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
27
28using namespace QtPrivate;
29using namespace Qt::StringLiterals;
30using namespace QtMiscUtils;
31
32enum { StreamEOF = ~0U };
33
34namespace {
35template <typename Range>
36auto reversed(Range &r)
37{
38 struct R {
39 Range *r;
40 auto begin() { return std::make_reverse_iterator(std::end(*r)); }
41 auto end() { return std::make_reverse_iterator(std::begin(*r)); }
42 };
43
44 return R{&r};
45}
46
47template <typename Range>
48void reversed(const Range &&) = delete;
49
50// implementation of missing QUtf8StringView methods for ASCII-only needles:
51auto transform(QLatin1StringView haystack, char needle)
52{
53 struct R { QLatin1StringView haystack; char16_t needle; };
54 return R{haystack, uchar(needle)};
55}
56
57auto transform(QStringView haystack, char needle)
58{
59 struct R { QStringView haystack; char16_t needle; };
60 return R{haystack, uchar(needle)};
61}
62
63auto transform(QUtf8StringView haystack, char needle)
64{
65 struct R { QByteArrayView haystack; char needle; };
66 return R{haystack, needle};
67}
68
70{
71 struct R { QLatin1StringView haystack; QLatin1StringView needle; };
72 return R{haystack, needle};
73}
74
75auto transform(QStringView haystack, QLatin1StringView needle)
76{
77 struct R { QStringView haystack; QLatin1StringView needle; };
78 return R{haystack, needle};
79}
80
81auto transform(QUtf8StringView haystack, QLatin1StringView needle)
82{
83 struct R { QLatin1StringView haystack; QLatin1StringView needle; };
84 return R{QLatin1StringView{QByteArrayView{haystack}}, needle};
85}
86
87#define WRAP(method, Needle) \
88 auto method (QAnyStringView s, Needle needle) noexcept \
89 { \
90 return s.visit([needle](auto s) { \
91 auto r = transform(s, needle); \
92 return r.haystack. method (r.needle); \
93 }); \
94 } \
95 /*end*/
96
97WRAP(count, char)
98WRAP(contains, char)
100WRAP(endsWith, char)
102
103} // unnamed namespace
104
208{
209}
210
216QString QXmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/)
217{
218 return QString();
219}
220
221
231{
232 return QString();
233}
234
235#if QT_CONFIG(xmlstreamreader)
236
238{
239 if (entityResolver)
241 return QString();
242}
243
244
245
258void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver)
259{
260 Q_D(QXmlStreamReader);
261 d->entityResolver = resolver;
262}
263
271QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const
272{
273 Q_D(const QXmlStreamReader);
274 return d->entityResolver;
275}
276
277
278
429QXmlStreamReader::QXmlStreamReader()
430 : d_ptr(new QXmlStreamReaderPrivate(this))
431{
432}
433
438QXmlStreamReader::QXmlStreamReader(QIODevice *device)
439 : d_ptr(new QXmlStreamReaderPrivate(this))
440{
441 setDevice(device);
442}
443
462QXmlStreamReader::QXmlStreamReader(QAnyStringView data)
463 : d_ptr(new QXmlStreamReaderPrivate(this))
464{
465 Q_D(QXmlStreamReader);
466 data.visit([d](auto data) {
467 if constexpr (std::is_same_v<decltype(data), QStringView>) {
468 d->dataBuffer = data.toUtf8();
470 d->lockEncoding = true;
471 } else if constexpr (std::is_same_v<decltype(data), QLatin1StringView>) {
472 // Conversion to a QString is required, to avoid breaking
473 // pre-existing (before porting to QAnyStringView) behavior.
474 d->dataBuffer = QString::fromLatin1(data).toUtf8();
476 d->lockEncoding = true;
477 } else {
478 d->dataBuffer = QByteArray(data.data(), data.size());
479 }
480 });
481}
482
489QXmlStreamReader::QXmlStreamReader(const QByteArray &data, PrivateConstructorTag)
490 : d_ptr(new QXmlStreamReaderPrivate(this))
491{
492 Q_D(QXmlStreamReader);
493 d->dataBuffer = data;
494}
495
499QXmlStreamReader::~QXmlStreamReader()
500{
501 Q_D(QXmlStreamReader);
502 if (d->deleteDevice)
503 delete d->device;
504}
505
518void QXmlStreamReader::setDevice(QIODevice *device)
519{
520 Q_D(QXmlStreamReader);
521 if (d->deleteDevice) {
522 delete d->device;
523 d->deleteDevice = false;
524 }
525 d->device = device;
526 d->init();
527
528}
529
536QIODevice *QXmlStreamReader::device() const
537{
538 Q_D(const QXmlStreamReader);
539 return d->device;
540}
541
562void QXmlStreamReader::addData(QAnyStringView data)
563{
564 Q_D(QXmlStreamReader);
565 data.visit([this, d](auto data) {
566 if constexpr (std::is_same_v<decltype(data), QStringView>) {
567 d->lockEncoding = true;
568 if (!d->decoder.isValid())
570 addDataImpl(data.toUtf8());
571 } else if constexpr (std::is_same_v<decltype(data), QLatin1StringView>) {
572 // Conversion to a QString is required, to avoid breaking
573 // pre-existing (before porting to QAnyStringView) behavior.
574 if (!d->decoder.isValid())
576 addDataImpl(QString::fromLatin1(data).toUtf8());
577 } else {
578 addDataImpl(QByteArray(data.data(), data.size()));
579 }
580 });
581}
582
589void QXmlStreamReader::addDataImpl(const QByteArray &data)
590{
591 Q_D(QXmlStreamReader);
592 if (d->device) {
593 qWarning("QXmlStreamReader: addData() with device()");
594 return;
595 }
596 d->dataBuffer += data;
597}
598
605void QXmlStreamReader::clear()
606{
607 Q_D(QXmlStreamReader);
608 d->init();
609 if (d->device) {
610 if (d->deleteDevice)
611 delete d->device;
612 d->device = nullptr;
613 }
614}
615
631bool QXmlStreamReader::atEnd() const
632{
633 Q_D(const QXmlStreamReader);
634 if (d->atEnd
635 && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError)
636 || (d->type == QXmlStreamReader::EndDocument))) {
637 if (d->device)
638 return d->device->atEnd();
639 else
640 return !d->dataBuffer.size();
641 }
642 return (d->atEnd || d->type == QXmlStreamReader::Invalid);
643}
644
645
664QXmlStreamReader::TokenType QXmlStreamReader::readNext()
665{
666 Q_D(QXmlStreamReader);
667 if (d->type != Invalid) {
668 if (!d->hasCheckedStartDocument)
669 if (!d->checkStartDocument())
670 return d->type; // synthetic StartDocument or error
671 d->parse();
672 if (d->atEnd && d->type != EndDocument && d->type != Invalid)
673 d->raiseError(PrematureEndOfDocumentError);
674 else if (!d->atEnd && d->type == EndDocument)
675 d->raiseWellFormedError(QXmlStream::tr("Extra content at end of document."));
676 } else if (d->error == PrematureEndOfDocumentError) {
677 // resume error
678 d->type = NoToken;
679 d->atEnd = false;
680 d->token = -1;
681 return readNext();
682 }
683 d->checkToken();
684 return d->type;
685}
686
687
698QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const
699{
700 Q_D(const QXmlStreamReader);
701 return d->type;
702}
703
721bool QXmlStreamReader::readNextStartElement()
722{
723 while (readNext() != Invalid) {
724 if (isEndElement())
725 return false;
726 else if (isStartElement())
727 return true;
728 }
729 return false;
730}
731
743void QXmlStreamReader::skipCurrentElement()
744{
745 int depth = 1;
746 while (depth && readNext() != Invalid) {
747 if (isEndElement())
748 --depth;
749 else if (isStartElement())
750 ++depth;
751 }
752}
753
754static constexpr auto QXmlStreamReader_tokenTypeString = qOffsetStringArray(
755 "NoToken",
756 "Invalid",
757 "StartDocument",
758 "EndDocument",
759 "StartElement",
760 "EndElement",
761 "Characters",
762 "Comment",
763 "DTD",
764 "EntityReference",
765 "ProcessingInstruction"
766);
767
768static constexpr auto QXmlStreamReader_XmlContextString = qOffsetStringArray(
769 "Prolog",
770 "Body"
771);
772
785void QXmlStreamReader::setNamespaceProcessing(bool enable)
786{
787 Q_D(QXmlStreamReader);
788 d->namespaceProcessing = enable;
789}
790
791bool QXmlStreamReader::namespaceProcessing() const
792{
793 Q_D(const QXmlStreamReader);
794 return d->namespaceProcessing;
795}
796
801QString QXmlStreamReader::tokenString() const
802{
803 Q_D(const QXmlStreamReader);
804 return QLatin1StringView(QXmlStreamReader_tokenTypeString.at(d->type));
805}
806
811static constexpr QLatin1StringView contextString(QXmlStreamReaderPrivate::XmlContext ctxt)
812{
813 return QLatin1StringView(QXmlStreamReader_XmlContextString.at(static_cast<int>(ctxt)));
814}
815
816#endif // feature xmlstreamreader
817
819{
820 tagStack.reserve(16);
823 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
824 namespaceDeclaration.prefix = addToStringStorage(u"xml");
825 namespaceDeclaration.namespaceUri = addToStringStorage(u"http://www.w3.org/XML/1998/namespace");
827 tagsDone = false;
828}
829
830#if QT_CONFIG(xmlstreamreader)
831
833 :q_ptr(q)
834{
835 device = nullptr;
836 deleteDevice = false;
837 stack_size = 64;
838 sym_stack = nullptr;
839 state_stack = nullptr;
840 reallocateStack();
841 entityResolver = nullptr;
842 init();
843#define ADD_PREDEFINED(n, v) \
844 do { \
845 Entity e = Entity::createLiteral(n##_L1, v##_L1); \
846 entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \
847 } while (false)
848 ADD_PREDEFINED("lt", "<");
849 ADD_PREDEFINED("gt", ">");
850 ADD_PREDEFINED("amp", "&");
851 ADD_PREDEFINED("apos", "'");
852 ADD_PREDEFINED("quot", "\"");
853#undef ADD_PREDEFINED
854}
855
857{
858 scanDtd = false;
859 lastAttributeIsCData = false;
860 token = -1;
861 token_char = 0;
862 isEmptyElement = false;
863 isWhitespace = true;
864 isCDATA = false;
865 standalone = false;
866 hasStandalone = false;
867 tos = 0;
868 resumeReduction = 0;
869 state_stack[tos++] = 0;
870 state_stack[tos] = 0;
871 putStack.clear();
872 putStack.reserve(32);
874 textBuffer.reserve(256);
875 tagStack.clear();
876 tagsDone = false;
880 readBufferPos = 0;
881 nbytesread = 0;
883 attributeStack.clear();
884 attributeStack.reserve(16);
885 entityParser.reset();
887 normalizeLiterals = false;
888 hasSeenTag = false;
889 atEnd = false;
890 inParseEntity = false;
893 hasExternalDtdSubset = false;
894 lockEncoding = false;
895 namespaceProcessing = true;
898 readBuffer.clear();
900
901 type = QXmlStreamReader::NoToken;
902 error = QXmlStreamReader::NoError;
904 foundDTD = false;
905}
906
907/*
908 Well-formed requires that we verify entity values. We do this with a
909 standard parser.
910 */
912{
913 Q_Q(QXmlStreamReader);
914
915 if (value.isEmpty())
916 return;
917
918
919 if (!entityParser)
920 entityParser = std::make_unique<QXmlStreamReaderPrivate>(q);
921 else
922 entityParser->init();
923 entityParser->inParseEntity = true;
924 entityParser->readBuffer = value;
925 entityParser->injectToken(PARSE_ENTITY);
926 while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid)
927 entityParser->parse();
928 if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size())
929 raiseWellFormedError(QXmlStream::tr("Invalid entity value."));
930
931}
932
934{
935 stack_size <<= 1;
936 sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
938 state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
940}
941
942
944{
945 free(sym_stack);
946 free(state_stack);
947}
948
949
951{
952 uint peekc = peekChar();
953 if (peekc == '\n') {
954 if (putStack.size())
955 putStack.pop();
956 else
958 return peekc;
959 }
960 if (peekc == StreamEOF) {
961 putChar('\r');
962 return 0;
963 }
964 return '\n';
965}
966
972{
973 uint c;
974 if (putStack.size()) {
975 c = atEnd ? StreamEOF : putStack.pop();
976 } else {
977 if (readBufferPos < readBuffer.size())
978 c = readBuffer.at(readBufferPos++).unicode();
979 else
980 c = getChar_helper();
981 }
982
983 return c;
984}
985
987{
988 uint c;
989 if (putStack.size()) {
990 c = putStack.top();
991 } else if (readBufferPos < readBuffer.size()) {
992 c = readBuffer.at(readBufferPos).unicode();
993 } else {
994 if ((c = getChar_helper()) != StreamEOF)
996 }
997
998 return c;
999}
1000
1013bool QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject)
1014{
1015 const qsizetype pos = textBuffer.size();
1016 const auto oldLineNumber = lineNumber;
1017
1018 uint c;
1019 while ((c = getChar()) != StreamEOF) {
1020 /* First, we do the validation & normalization. */
1021 switch (c) {
1022 case '\r':
1023 if ((c = filterCarriageReturn()) == 0)
1024 break;
1025 Q_FALLTHROUGH();
1026 case '\n':
1027 ++lineNumber;
1029 Q_FALLTHROUGH();
1030 case '\t':
1031 textBuffer += QChar(c);
1032 continue;
1033 default:
1034 if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) {
1035 raiseWellFormedError(QXmlStream::tr("Invalid XML character."));
1036 lineNumber = oldLineNumber;
1037 return false;
1038 }
1039 textBuffer += QChar(c);
1040 }
1041
1042
1043 /* Second, attempt to lookup str. */
1044 if (c == uint(*str)) {
1045 if (!*(str + 1)) {
1046 if (tokenToInject >= 0)
1047 injectToken(tokenToInject);
1048 return true;
1049 } else {
1050 if (scanString(str + 1, tokenToInject, false))
1051 return true;
1052 }
1053 }
1054 }
1057 lineNumber = oldLineNumber;
1058 return false;
1059}
1060
1061bool QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace)
1062{
1063 qsizetype n = 0;
1064 while (str[n]) {
1065 uint c = getChar();
1066 if (c != ushort(str[n])) {
1067 if (c != StreamEOF)
1068 putChar(c);
1069 while (n--) {
1070 putChar(ushort(str[n]));
1071 }
1072 return false;
1073 }
1074 ++n;
1075 }
1077 if (requireSpace) {
1078 const qsizetype s = fastScanSpace();
1079 if (!s || atEnd) {
1080 qsizetype pos = textBuffer.size() - n - s;
1083 return false;
1084 }
1085 }
1086 if (tokenToInject >= 0)
1087 injectToken(tokenToInject);
1088 return true;
1089}
1090
1092{
1093 switch (peekChar()) {
1094 case '[':
1095 return scanString(spell[CDATA_START], CDATA_START, false);
1096 case 'D':
1097 return scanString(spell[DOCTYPE], DOCTYPE);
1098 case 'A':
1099 return scanString(spell[ATTLIST], ATTLIST);
1100 case 'N':
1102 case 'E':
1104 return true;
1105 return scanString(spell[ENTITY], ENTITY);
1106
1107 default:
1108 ;
1109 };
1110 return false;
1111}
1112
1114{
1115 switch (peekChar()) {
1116 case 'S':
1117 return scanString(spell[SYSTEM], SYSTEM);
1118 case 'P':
1119 return scanString(spell[PUBLIC], PUBLIC);
1120 default:
1121 ;
1122 }
1123 return false;
1124}
1125
1127{
1128 if (fastScanSpace()) {
1129 if (scanString(spell[NDATA], NDATA))
1130 return true;
1131 putChar(' ');
1132 }
1133 return false;
1134}
1135
1137{
1138 switch (peekChar()) {
1139 case 'R':
1140 return scanString(spell[REQUIRED], REQUIRED, false);
1141 case 'I':
1142 return scanString(spell[IMPLIED], IMPLIED, false);
1143 case 'F':
1144 return scanString(spell[FIXED], FIXED, false);
1145 default:
1146 ;
1147 }
1148 return false;
1149}
1150
1152{
1153 switch (peekChar()) {
1154 case 'C':
1155 return scanString(spell[CDATA], CDATA);
1156 case 'I':
1157 if (scanString(spell[ID], ID))
1158 return true;
1159 if (scanString(spell[IDREF], IDREF))
1160 return true;
1161 return scanString(spell[IDREFS], IDREFS);
1162 case 'E':
1164 return true;
1166 case 'N':
1168 return true;
1170 return true;
1172 default:
1173 ;
1174 }
1175 return false;
1176}
1177
1191{
1192 qsizetype n = 0;
1193 uint c;
1194 while ((c = getChar()) != StreamEOF) {
1195 switch (ushort(c)) {
1196 case 0xfffe:
1197 case 0xffff:
1198 case 0:
1199 /* The putChar() call is necessary so the parser re-gets
1200 * the character from the input source, when raising an error. */
1201 putChar(c);
1202 return n;
1203 case '\r':
1204 if (filterCarriageReturn() == 0)
1205 return n;
1206 Q_FALLTHROUGH();
1207 case '\n':
1208 ++lineNumber;
1210 Q_FALLTHROUGH();
1211 case ' ':
1212 case '\t':
1214 textBuffer += u' ';
1215 else
1216 textBuffer += QChar(c);
1217 ++n;
1218 break;
1219 case '&':
1220 case '<':
1221 case '\"':
1222 case '\'':
1223 if (!(c & 0xff0000)) {
1224 putChar(c);
1225 return n;
1226 }
1227 Q_FALLTHROUGH();
1228 default:
1229 if (c < 0x20) {
1230 putChar(c);
1231 return n;
1232 }
1233 textBuffer += QChar(ushort(c));
1234 ++n;
1235 }
1236 }
1237 return n;
1238}
1239
1241{
1242 qsizetype n = 0;
1243 uint c;
1244 while ((c = getChar()) != StreamEOF) {
1245 switch (c) {
1246 case '\r':
1247 if ((c = filterCarriageReturn()) == 0)
1248 return n;
1249 Q_FALLTHROUGH();
1250 case '\n':
1251 ++lineNumber;
1253 Q_FALLTHROUGH();
1254 case ' ':
1255 case '\t':
1256 textBuffer += QChar(c);
1257 ++n;
1258 break;
1259 default:
1260 putChar(c);
1261 return n;
1262 }
1263 }
1264 return n;
1265}
1266
1274{
1275 qsizetype n = 0;
1276 uint c;
1277 while ((c = getChar()) != StreamEOF) {
1278 switch (ushort(c)) {
1279 case 0xfffe:
1280 case 0xffff:
1281 case 0:
1282 putChar(c);
1283 return n;
1284 case ']': {
1285 isWhitespace = false;
1286 const qsizetype pos = textBuffer.size();
1287 textBuffer += QChar(ushort(c));
1288 ++n;
1289 while ((c = getChar()) == ']') {
1290 textBuffer += QChar(ushort(c));
1291 ++n;
1292 }
1293 if (c == 0) {
1296 } else if (c == '>' && textBuffer.at(textBuffer.size() - 2) == u']') {
1297 raiseWellFormedError(QXmlStream::tr("Sequence ']]>' not allowed in content."));
1298 } else {
1299 putChar(c);
1300 break;
1301 }
1302 return n;
1303 } break;
1304 case '\r':
1305 if ((c = filterCarriageReturn()) == 0)
1306 return n;
1307 Q_FALLTHROUGH();
1308 case '\n':
1309 ++lineNumber;
1311 Q_FALLTHROUGH();
1312 case ' ':
1313 case '\t':
1314 textBuffer += QChar(ushort(c));
1315 ++n;
1316 break;
1317 case '&':
1318 case '<':
1319 if (!(c & 0xff0000)) {
1320 putChar(c);
1321 return n;
1322 }
1323 Q_FALLTHROUGH();
1324 default:
1325 if (c < 0x20) {
1326 putChar(c);
1327 return n;
1328 }
1329 isWhitespace = false;
1330 textBuffer += QChar(ushort(c));
1331 ++n;
1332 }
1333 }
1334 return n;
1335}
1336
1337// Fast scan an XML attribute name (e.g. "xml:lang").
1338inline std::optional<qsizetype> QXmlStreamReaderPrivate::fastScanName(Value *val)
1339{
1340 qsizetype n = 0;
1341 uint c;
1342 while ((c = getChar()) != StreamEOF) {
1343 if (n >= 4096) {
1344 // This is too long to be a sensible name, and
1345 // can exhaust memory, or the range of decltype(*prefix)
1347 return std::nullopt;
1348 }
1349 switch (c) {
1350 case '\n':
1351 case ' ':
1352 case '\t':
1353 case '\r':
1354 case '&':
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 putChar(c);
1376 if (val && val->prefix == n + 1) {
1377 val->prefix = 0;
1378 putChar(':');
1379 --n;
1380 }
1381 return n;
1382 case ':':
1383 if (val) {
1384 if (val->prefix == 0) {
1385 val->prefix = qint16(n + 2);
1386 } else { // only one colon allowed according to the namespace spec.
1387 putChar(c);
1388 return n;
1389 }
1390 } else {
1391 putChar(c);
1392 return n;
1393 }
1394 Q_FALLTHROUGH();
1395 default:
1396 textBuffer += QChar(ushort(c));
1397 ++n;
1398 }
1399 }
1400
1401 if (val)
1402 val->prefix = 0;
1406 return 0;
1407}
1408
1409enum NameChar { NameBeginning, NameNotBeginning, NotName };
1410
1411static const char Begi = static_cast<char>(NameBeginning);
1412static const char NtBg = static_cast<char>(NameNotBeginning);
1413static const char NotN = static_cast<char>(NotName);
1414
1415static const char nameCharTable[128] =
1416{
1417// 0x00
1418 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1419 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1420// 0x10
1421 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1422 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1423// 0x20 (0x2D is '-', 0x2E is '.')
1424 NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN,
1425 NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN,
1426// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':')
1427 NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg,
1428 NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN,
1429// 0x40 (0x41..0x5A are 'A'..'Z')
1430 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1431 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1432// 0x50 (0x5F is '_')
1433 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1434 Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi,
1435// 0x60 (0x61..0x7A are 'a'..'z')
1436 NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1437 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1438// 0x70
1439 Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi,
1440 Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN
1441};
1442
1443static inline NameChar fastDetermineNameChar(QChar ch)
1444{
1445 ushort uc = ch.unicode();
1446 if (!(uc & ~0x7f)) // uc < 128
1447 return static_cast<NameChar>(nameCharTable[uc]);
1448
1449 QChar::Category cat = ch.category();
1450 // ### some these categories might be slightly wrong
1451 if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other)
1452 || cat == QChar::Number_Letter)
1453 return NameBeginning;
1454 if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other)
1455 || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing))
1456 return NameNotBeginning;
1457 return NotName;
1458}
1459
1461{
1462 qsizetype n = 0;
1463 uint c;
1464 while ((c = getChar()) != StreamEOF) {
1465 if (fastDetermineNameChar(QChar(c)) == NotName) {
1466 putChar(c);
1467 return n;
1468 } else {
1469 ++n;
1470 textBuffer += QChar(c);
1471 }
1472 }
1473
1477
1478 return n;
1479}
1480
1482{
1483 if (from != 0) {
1484 putString(s.mid(from));
1485 return;
1486 }
1487 putStack.reserve(s.size());
1488 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1489 putStack.rawPush() = it->unicode();
1490}
1491
1493{
1494 putStack.reserve(s.size());
1495 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it)
1496 putStack.rawPush() = ((LETTER << 16) | it->unicode());
1497}
1498
1500{
1501 putStack.reserve(s.size());
1502 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1503 char16_t c = it->unicode();
1504 if (c == '\n' || c == '\r')
1505 putStack.rawPush() = ((LETTER << 16) | c);
1506 else
1507 putStack.rawPush() = c;
1508 }
1509}
1511{
1512 putStack.reserve(s.size());
1513 for (auto it = s.rbegin(), end = s.rend(); it != end; ++it) {
1514 char16_t c = it->unicode();
1515 if (c == '&' || c == ';')
1516 putStack.rawPush() = c;
1517 else if (c == '\n' || c == '\r')
1518 putStack.rawPush() = ' ';
1519 else
1520 putStack.rawPush() = ((LETTER << 16) | c);
1521 }
1522}
1523
1525{
1526 constexpr qsizetype BUFFER_SIZE = 8192;
1528 readBufferPos = 0;
1529 if (readBuffer.size())
1530 readBuffer.resize(0);
1531 if (decoder.isValid())
1532 nbytesread = 0;
1533 if (device) {
1535 qint64 nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread);
1536 nbytesread += qMax(nbytesreadOrMinus1, qint64{0});
1537 } else {
1538 if (nbytesread)
1540 else
1543 dataBuffer.clear();
1544 }
1545 if (!nbytesread) {
1546 atEnd = true;
1547 return StreamEOF;
1548 }
1549
1550 if (!decoder.isValid()) {
1551 if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus
1552 // one extra for the utf8 codec
1553 atEnd = true;
1554 return StreamEOF;
1555 }
1556 auto encoding = QStringDecoder::encodingForData(rawReadBuffer, char16_t('<'));
1557 if (!encoding)
1558 // assume utf-8
1559 encoding = QStringDecoder::Utf8;
1560 decoder = QStringDecoder(*encoding);
1561 }
1562
1564
1565 if (lockEncoding && decoder.hasError()) {
1566 raiseWellFormedError(QXmlStream::tr("Encountered incorrectly encoded content."));
1567 readBuffer.clear();
1568 return StreamEOF;
1569 }
1570
1571 readBuffer.reserve(1); // keep capacity when calling resize() next time
1572
1573 if (readBufferPos < readBuffer.size()) {
1574 ushort c = readBuffer.at(readBufferPos++).unicode();
1575 return c;
1576 }
1577
1578 atEnd = true;
1579 return StreamEOF;
1580}
1581
1583{
1584 for (const NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
1585 if (namespaceDeclaration.prefix == prefix) {
1586 return namespaceDeclaration.namespaceUri;
1587 }
1588 }
1589
1590#if 1
1591 if (namespaceProcessing && !prefix.isEmpty())
1592 raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix));
1593#endif
1594
1595 return XmlStringRef();
1596}
1597
1598/*
1599 uses namespaceForPrefix and builds the attribute vector
1600 */
1602{
1603 const auto attributeStackCleaner = qScopeGuard([this](){ attributeStack.clear(); });
1604 const qsizetype n = attributeStack.size();
1605
1606 if (namespaceProcessing) {
1607 for (DtdAttribute &dtdAttribute : dtdAttributes) {
1608 if (!dtdAttribute.isNamespaceAttribute
1609 || dtdAttribute.defaultValue.isNull()
1610 || dtdAttribute.tagName != qualifiedName
1611 || dtdAttribute.attributeQualifiedName.isNull())
1612 continue;
1613 qsizetype i = 0;
1614 while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1615 ++i;
1616 if (i != n)
1617 continue;
1618 if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == "xmlns"_L1) {
1619 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1620 namespaceDeclaration.prefix.clear();
1621
1622 const XmlStringRef ns(dtdAttribute.defaultValue);
1623 if (ns == "http://www.w3.org/2000/xmlns/"_L1 ||
1624 ns == "http://www.w3.org/XML/1998/namespace"_L1)
1625 raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
1626 else
1627 namespaceDeclaration.namespaceUri = ns;
1628 } else if (dtdAttribute.attributePrefix == "xmlns"_L1) {
1629 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
1630 XmlStringRef namespacePrefix = dtdAttribute.attributeName;
1631 XmlStringRef namespaceUri = dtdAttribute.defaultValue;
1632 if (((namespacePrefix == "xml"_L1)
1633 ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1))
1634 || namespaceUri == "http://www.w3.org/2000/xmlns/"_L1
1635 || namespaceUri.isEmpty()
1636 || namespacePrefix == "xmlns"_L1)
1637 raiseWellFormedError(QXmlStream::tr("Illegal namespace declaration."));
1638
1639 namespaceDeclaration.prefix = namespacePrefix;
1640 namespaceDeclaration.namespaceUri = namespaceUri;
1641 }
1642 }
1643 }
1644
1645 tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix);
1646
1648
1649 for (qsizetype i = 0; i < n; ++i) {
1651 Attribute &attrib = attributeStack[i];
1652 XmlStringRef prefix(symPrefix(attrib.key));
1653 XmlStringRef name(symString(attrib.key));
1654 XmlStringRef qualifiedName(symName(attrib.key));
1655 XmlStringRef value(symString(attrib.value));
1656
1657 attribute.m_name = name;
1658 attribute.m_qualifiedName = qualifiedName;
1659 attribute.m_value = value;
1660
1661 if (!prefix.isEmpty()) {
1662 XmlStringRef attributeNamespaceUri = namespaceForPrefix(prefix);
1663 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1664 }
1665
1666 for (qsizetype j = 0; j < i; ++j) {
1667 if (attributes[j].name() == attribute.name()
1668 && attributes[j].namespaceUri() == attribute.namespaceUri()
1669 && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName()))
1670 {
1671 raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName()));
1672 return;
1673 }
1674 }
1675 }
1676
1677 for (DtdAttribute &dtdAttribute : dtdAttributes) {
1678 if (dtdAttribute.isNamespaceAttribute
1679 || dtdAttribute.defaultValue.isNull()
1680 || dtdAttribute.tagName != qualifiedName
1681 || dtdAttribute.attributeQualifiedName.isNull())
1682 continue;
1683 qsizetype i = 0;
1684 while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName)
1685 ++i;
1686 if (i != n)
1687 continue;
1688
1689
1690
1692 attribute.m_name = dtdAttribute.attributeName;
1693 attribute.m_qualifiedName = dtdAttribute.attributeQualifiedName;
1694 attribute.m_value = dtdAttribute.defaultValue;
1695
1696 if (!dtdAttribute.attributePrefix.isEmpty()) {
1697 XmlStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix);
1698 attribute.m_namespaceUri = XmlStringRef(attributeNamespaceUri);
1699 }
1700 attribute.m_isDefault = true;
1701 attributes.append(std::move(attribute));
1702 }
1703}
1704
1706{
1707 const Tag &tag = tagStack.top();
1708 qsizetype n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize;
1710 for (qsizetype i = 0; i < n; ++i) {
1711 const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i);
1712 QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i];
1713 publicNamespaceDeclaration.m_prefix = namespaceDeclaration.prefix;
1714 publicNamespaceDeclaration.m_namespaceUri = namespaceDeclaration.namespaceUri;
1715 }
1716}
1717
1719{
1721 for (qsizetype i = 0; i < notationDeclarations.size(); ++i) {
1723 QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i];
1724 publicNotationDeclaration.m_name = notationDeclaration.name;
1725 publicNotationDeclaration.m_systemId = notationDeclaration.systemId;
1726 publicNotationDeclaration.m_publicId = notationDeclaration.publicId;
1727
1728 }
1729 notationDeclarations.clear();
1731 for (qsizetype i = 0; i < entityDeclarations.size(); ++i) {
1733 QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i];
1734 publicEntityDeclaration.m_name = entityDeclaration.name;
1735 publicEntityDeclaration.m_notationName = entityDeclaration.notationName;
1736 publicEntityDeclaration.m_systemId = entityDeclaration.systemId;
1737 publicEntityDeclaration.m_publicId = entityDeclaration.publicId;
1738 publicEntityDeclaration.m_value = entityDeclaration.value;
1739 }
1740 entityDeclarations.clear();
1741 parameterEntityHash.clear();
1742}
1743
1745{
1746 bool ok = true;
1747 uint s;
1748 // ### add toXShort to XmlString?
1749 if (sym(symbolIndex).c == 'x')
1750 s = symString(symbolIndex, 1).view().toUInt(&ok, 16);
1751 else
1752 s = symString(symbolIndex).view().toUInt(&ok, 10);
1753
1754 ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff)
1755 || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint));
1756
1757 return ok ? s : 0;
1758}
1759
1760
1762{
1763//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
1764
1765 const char16_t *data = publicId.utf16();
1766 uchar c = 0;
1767 qsizetype i;
1768 for (i = publicId.size() - 1; i >= 0; --i) {
1769 if (data[i] < 256)
1770 switch ((c = data[i])) {
1771 case ' ': case '\n': case '\r': case '-': case '(': case ')':
1772 case '+': case ',': case '.': case '/': case ':': case '=':
1773 case '?': case ';': case '!': case '*': case '#': case '@':
1774 case '$': case '_': case '%': case '\'': case '\"':
1775 continue;
1776 default:
1778 continue;
1779 }
1780 break;
1781 }
1782 if (i >= 0)
1783 raiseWellFormedError(QXmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c))));
1784}
1785
1786/*
1787 Checks whether the document starts with an xml declaration. If it
1788 does, this function returns \c true; otherwise it sets up everything
1789 for a synthetic start document event and returns \c false.
1790 */
1792{
1794
1795 if (scanString(spell[XML], XML))
1796 return true;
1797
1798 type = QXmlStreamReader::StartDocument;
1799 if (atEnd) {
1801 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1802 }
1803 return false;
1804}
1805
1807{
1808 QString err;
1809 if (documentVersion != "1.0"_L1) {
1810 if (documentVersion.view().contains(u' '))
1811 err = QXmlStream::tr("Invalid XML version string.");
1812 else
1813 err = QXmlStream::tr("Unsupported XML version.");
1814 }
1815 qsizetype n = attributeStack.size();
1816
1817 /* We use this bool to ensure that the pesudo attributes are in the
1818 * proper order:
1819 *
1820 * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
1821
1822 for (qsizetype i = 0; err.isNull() && i < n; ++i) {
1823 Attribute &attrib = attributeStack[i];
1824 XmlStringRef prefix(symPrefix(attrib.key));
1825 XmlStringRef key(symString(attrib.key));
1826 XmlStringRef value(symString(attrib.value));
1827
1828 if (prefix.isEmpty() && key == "encoding"_L1) {
1830
1831 if (hasStandalone)
1832 err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding.");
1834 err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value);
1835 else {
1836 QByteArray enc = value.toString().toUtf8();
1837 if (!lockEncoding) {
1839 if (!decoder.isValid()) {
1840 err = QXmlStream::tr("Encoding %1 is unsupported").arg(value);
1841 } else {
1843 }
1844 }
1845 }
1846 } else if (prefix.isEmpty() && key == "standalone"_L1) {
1847 hasStandalone = true;
1848 if (value == "yes"_L1)
1849 standalone = true;
1850 else if (value == "no"_L1)
1851 standalone = false;
1852 else
1853 err = QXmlStream::tr("Standalone accepts only yes or no.");
1854 } else {
1855 err = QXmlStream::tr("Invalid attribute in XML declaration: %1 = %2").arg(key).arg(value);
1856 }
1857 }
1858
1859 if (!err.isNull())
1861 attributeStack.clear();
1862}
1863
1864
1865void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message)
1866{
1867 this->error = error;
1869 if (errorString.isNull()) {
1870 if (error == QXmlStreamReader::PrematureEndOfDocumentError)
1871 errorString = QXmlStream::tr("Premature end of document.");
1872 else if (error == QXmlStreamReader::CustomError)
1873 errorString = QXmlStream::tr("Invalid document.");
1874 }
1875
1876 type = QXmlStreamReader::Invalid;
1877}
1878
1880{
1881 raiseError(QXmlStreamReader::NotWellFormedError, message);
1882}
1883
1885{
1886 // TODO: add a ImplementationLimitsExceededError and use it instead
1887 raiseError(QXmlStreamReader::NotWellFormedError,
1888 QXmlStream::tr("Length of XML attribute name exceeds implementation limits (4KiB "
1889 "characters)."));
1890}
1891
1893{
1894
1895 if (token == EOF_SYMBOL) {
1896 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1897 return;
1898 }
1899 const int nmax = 4;
1900 QString error_message;
1901 int ers = state_stack[tos];
1902 int nexpected = 0;
1903 int expected[nmax];
1904 if (token != XML_ERROR)
1905 for (int tk = 0; tk < TERMINAL_COUNT; ++tk) {
1906 int k = t_action(ers, tk);
1907 if (k <= 0)
1908 continue;
1909 if (spell[tk]) {
1910 if (nexpected < nmax)
1911 expected[nexpected++] = tk;
1912 }
1913 }
1914
1915 if (nexpected && nexpected < nmax) {
1916 //: '<first option>'
1917 QString exp_str = QXmlStream::tr("'%1'", "expected")
1919 if (nexpected == 2) {
1920 //: <first option>, '<second option>'
1921 exp_str = QXmlStream::tr("%1 or '%2'", "expected")
1922 .arg(exp_str, QLatin1StringView(spell[expected[1]]));
1923 } else if (nexpected > 2) {
1924 int s = 1;
1925 for (; s < nexpected - 1; ++s) {
1926 //: <options so far>, '<next option>'
1927 exp_str = QXmlStream::tr("%1, '%2'", "expected")
1928 .arg(exp_str, QLatin1StringView(spell[expected[s]]));
1929 }
1930 //: <options so far>, or '<final option>'
1931 exp_str = QXmlStream::tr("%1, or '%2'", "expected")
1932 .arg(exp_str, QLatin1StringView(spell[expected[s]]));
1933 }
1934 error_message = QXmlStream::tr("Expected %1, but got '%2'.")
1935 .arg(exp_str, QLatin1StringView(spell[token]));
1936 } else {
1937 error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1StringView(spell[token]));
1938 }
1939
1940 raiseWellFormedError(error_message);
1941}
1942
1945 if (error == QXmlStreamReader::NoError)
1946 raiseError(QXmlStreamReader::PrematureEndOfDocumentError);
1947}
1948
1953qint64 QXmlStreamReader::lineNumber() const
1954{
1955 Q_D(const QXmlStreamReader);
1956 return d->lineNumber + 1; // in public we start with 1
1957}
1958
1963qint64 QXmlStreamReader::columnNumber() const
1964{
1965 Q_D(const QXmlStreamReader);
1966 return d->characterOffset - d->lastLineStart + d->readBufferPos;
1967}
1968
1973qint64 QXmlStreamReader::characterOffset() const
1974{
1975 Q_D(const QXmlStreamReader);
1976 return d->characterOffset + d->readBufferPos;
1977}
1978
1979
1983QStringView QXmlStreamReader::text() const
1984{
1985 Q_D(const QXmlStreamReader);
1986 return d->text;
1987}
1988
1989
1996QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const
1997{
1998 Q_D(const QXmlStreamReader);
1999 if (d->notationDeclarations.size())
2000 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2001 return d->publicNotationDeclarations;
2002}
2003
2004
2011QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const
2012{
2013 Q_D(const QXmlStreamReader);
2014 if (d->entityDeclarations.size())
2015 const_cast<QXmlStreamReaderPrivate *>(d)->resolveDtd();
2016 return d->publicEntityDeclarations;
2017}
2018
2026QStringView QXmlStreamReader::dtdName() const
2027{
2028 Q_D(const QXmlStreamReader);
2029 if (d->type == QXmlStreamReader::DTD)
2030 return d->dtdName;
2031 return QStringView();
2032}
2033
2041QStringView QXmlStreamReader::dtdPublicId() const
2042{
2043 Q_D(const QXmlStreamReader);
2044 if (d->type == QXmlStreamReader::DTD)
2045 return d->dtdPublicId;
2046 return QStringView();
2047}
2048
2056QStringView QXmlStreamReader::dtdSystemId() const
2057{
2058 Q_D(const QXmlStreamReader);
2059 if (d->type == QXmlStreamReader::DTD)
2060 return d->dtdSystemId;
2061 return QStringView();
2062}
2063
2073int QXmlStreamReader::entityExpansionLimit() const
2074{
2075 Q_D(const QXmlStreamReader);
2076 return d->entityExpansionLimit;
2077}
2078
2094void QXmlStreamReader::setEntityExpansionLimit(int limit)
2095{
2096 Q_D(QXmlStreamReader);
2097 d->entityExpansionLimit = limit;
2098}
2099
2109QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const
2110{
2111 Q_D(const QXmlStreamReader);
2112 if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement)
2113 const_cast<QXmlStreamReaderPrivate *>(d)->resolvePublicNamespaces();
2114 return d->publicNamespaceDeclarations;
2115}
2116
2117
2128void QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration)
2129{
2130 Q_D(QXmlStreamReader);
2131 QXmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
2132 namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix());
2133 namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri());
2134}
2135
2143void QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &extraNamespaceDeclarations)
2144{
2145 for (const auto &extraNamespaceDeclaration : extraNamespaceDeclarations)
2146 addExtraNamespaceDeclaration(extraNamespaceDeclaration);
2147}
2148
2149
2167QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour)
2168{
2169 Q_D(QXmlStreamReader);
2170 if (isStartElement()) {
2172 forever {
2173 switch (readNext()) {
2174 case Characters:
2175 case EntityReference:
2176 result.insert(result.size(), d->text);
2177 break;
2178 case EndElement:
2179 return result;
2180 case ProcessingInstruction:
2181 case Comment:
2182 break;
2183 case StartElement:
2184 if (behaviour == SkipChildElements) {
2185 skipCurrentElement();
2186 break;
2187 } else if (behaviour == IncludeChildElements) {
2188 result += readElementText(behaviour);
2189 break;
2190 }
2191 Q_FALLTHROUGH();
2192 default:
2193 if (d->error || behaviour == ErrorOnUnexpectedElement) {
2194 if (!d->error)
2195 d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data."));
2196 return result;
2197 }
2198 }
2199 }
2200 }
2201 return QString();
2202}
2203
2208void QXmlStreamReader::raiseError(const QString& message)
2209{
2210 Q_D(QXmlStreamReader);
2211 d->raiseError(CustomError, message);
2212}
2213
2219QString QXmlStreamReader::errorString() const
2220{
2221 Q_D(const QXmlStreamReader);
2222 if (d->type == QXmlStreamReader::Invalid)
2223 return d->errorString;
2224 return QString();
2225}
2226
2231QXmlStreamReader::Error QXmlStreamReader::error() const
2232{
2233 Q_D(const QXmlStreamReader);
2234 if (d->type == QXmlStreamReader::Invalid)
2235 return d->error;
2236 return NoError;
2237}
2238
2242QStringView QXmlStreamReader::processingInstructionTarget() const
2243{
2244 Q_D(const QXmlStreamReader);
2245 return d->processingInstructionTarget;
2246}
2247
2251QStringView QXmlStreamReader::processingInstructionData() const
2252{
2253 Q_D(const QXmlStreamReader);
2254 return d->processingInstructionData;
2255}
2256
2257
2258
2264QStringView QXmlStreamReader::name() const
2265{
2266 Q_D(const QXmlStreamReader);
2267 return d->name;
2268}
2269
2275QStringView QXmlStreamReader::namespaceUri() const
2276{
2277 Q_D(const QXmlStreamReader);
2278 return d->namespaceUri;
2279}
2280
2293QStringView QXmlStreamReader::qualifiedName() const
2294{
2295 Q_D(const QXmlStreamReader);
2296 return d->qualifiedName;
2297}
2298
2299
2300
2308QStringView QXmlStreamReader::prefix() const
2309{
2310 Q_D(const QXmlStreamReader);
2311 return d->prefix;
2312}
2313
2317QXmlStreamAttributes QXmlStreamReader::attributes() const
2318{
2319 Q_D(const QXmlStreamReader);
2320 return d->attributes;
2321}
2322
2323#endif // feature xmlstreamreader
2324
2344{
2345 m_isDefault = false;
2346}
2347
2351QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)
2352{
2353 m_namespaceUri = namespaceUri;
2354 m_name = m_qualifiedName = name;
2355 m_value = value;
2356 m_namespaceUri = namespaceUri;
2357}
2358
2363{
2364 qsizetype colon = qualifiedName.indexOf(u':');
2365 m_name = qualifiedName.mid(colon + 1);
2366 m_qualifiedName = qualifiedName;
2367 m_value = value;
2368}
2369
2471{
2472}
2473
2531{
2532}
2533
2540{
2541 m_prefix = prefix;
2542 m_namespaceUri = namespaceUri;
2543}
2544
2588{
2589}
2590
2631{
2632 for (const QXmlStreamAttribute &attribute : *this) {
2633 if (attribute.name() == name && attribute.namespaceUri() == namespaceUri)
2634 return attribute.value();
2635 }
2636 return QStringView();
2637}
2638
2655QStringView QXmlStreamAttributes::value(QAnyStringView qualifiedName) const noexcept
2656{
2657 for (const QXmlStreamAttribute &attribute : *this) {
2658 if (attribute.qualifiedName() == qualifiedName)
2659 return attribute.value();
2660 }
2661 return QStringView();
2662}
2663
2668void QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value)
2669{
2670 append(QXmlStreamAttribute(namespaceUri, name, value));
2671}
2672
2677void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value)
2678{
2679 append(QXmlStreamAttribute(qualifiedName, value));
2680}
2681
2682#if QT_CONFIG(xmlstreamreader)
2683
2719bool QXmlStreamReader::isWhitespace() const
2720{
2721 Q_D(const QXmlStreamReader);
2722 return d->type == QXmlStreamReader::Characters && d->isWhitespace;
2723}
2724
2730bool QXmlStreamReader::isCDATA() const
2731{
2732 Q_D(const QXmlStreamReader);
2733 return d->type == QXmlStreamReader::Characters && d->isCDATA;
2734}
2735
2736
2737
2746bool QXmlStreamReader::isStandaloneDocument() const
2747{
2748 Q_D(const QXmlStreamReader);
2749 return d->standalone;
2750}
2751
2762bool QXmlStreamReader::hasStandaloneDeclaration() const
2763{
2764 Q_D(const QXmlStreamReader);
2765 return d->hasStandalone;
2766}
2767
2775QStringView QXmlStreamReader::documentVersion() const
2776{
2777 Q_D(const QXmlStreamReader);
2778 if (d->type == QXmlStreamReader::StartDocument)
2779 return d->documentVersion;
2780 return QStringView();
2781}
2782
2790QStringView QXmlStreamReader::documentEncoding() const
2791{
2792 Q_D(const QXmlStreamReader);
2793 if (d->type == QXmlStreamReader::StartDocument)
2794 return d->documentEncoding;
2795 return QStringView();
2796}
2797
2798#endif // feature xmlstreamreader
2799
2875#if QT_CONFIG(xmlstreamwriter)
2876
2877class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
2878{
2879 QXmlStreamWriter *q_ptr;
2880 Q_DECLARE_PUBLIC(QXmlStreamWriter)
2881public:
2882 QXmlStreamWriterPrivate(QXmlStreamWriter *q);
2883 ~QXmlStreamWriterPrivate() {
2884 if (deleteDevice)
2885 delete device;
2886 }
2887
2888 void write(QAnyStringView s);
2889 void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
2890 bool finishStartElement(bool contents = true);
2893 QString *stringDevice;
2894 uint deleteDevice :1;
2895 uint inStartElement :1;
2896 uint inEmptyElement :1;
2897 uint lastWasStartElement :1;
2898 uint wroteSomething :1;
2899 uint hasIoError :1;
2900 uint hasEncodingError :1;
2901 uint autoFormatting :1;
2902 std::string autoFormattingIndent;
2903 NamespaceDeclaration emptyNamespace;
2904 qsizetype lastNamespaceDeclaration;
2905
2906 NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
2907 void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration);
2908
2909 int namespacePrefixCount;
2910
2911 void indent(int level);
2912private:
2913 void doWriteToDevice(QStringView s);
2914 void doWriteToDevice(QUtf8StringView s);
2915 void doWriteToDevice(QLatin1StringView s);
2916};
2917
2918
2919QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
2920 : autoFormattingIndent(4, ' ')
2921{
2922 q_ptr = q;
2923 device = nullptr;
2924 stringDevice = nullptr;
2925 deleteDevice = false;
2926 inStartElement = inEmptyElement = false;
2927 wroteSomething = false;
2928 hasIoError = false;
2929 hasEncodingError = false;
2930 lastWasStartElement = false;
2931 lastNamespaceDeclaration = 1;
2932 autoFormatting = false;
2933 namespacePrefixCount = 0;
2934}
2935
2936void QXmlStreamWriterPrivate::write(QAnyStringView s)
2937{
2938 if (device) {
2939 if (hasIoError)
2940 return;
2941
2942 s.visit([&] (auto s) { doWriteToDevice(s); });
2943 } else if (stringDevice) {
2944 s.visit([&] (auto s) { stringDevice->append(s); });
2945 } else {
2946 qWarning("QXmlStreamWriter: No device");
2947 }
2948}
2949
2950void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
2951{
2952 QString escaped;
2953 escaped.reserve(s.size());
2954 s.visit([&] (auto s) {
2955 using View = decltype(s);
2956
2957 auto it = s.begin();
2958 const auto end = s.end();
2959
2960 while (it != end) {
2961 QLatin1StringView replacement;
2962 auto mark = it;
2963
2964 while (it != end) {
2965 if (*it == u'<') {
2966 replacement = "&lt;"_L1;
2967 break;
2968 } else if (*it == u'>') {
2969 replacement = "&gt;"_L1;
2970 break;
2971 } else if (*it == u'&') {
2972 replacement = "&amp;"_L1;
2973 break;
2974 } else if (*it == u'\"') {
2975 replacement = "&quot;"_L1;
2976 break;
2977 } else if (*it == u'\t') {
2978 if (escapeWhitespace) {
2979 replacement = "&#9;"_L1;
2980 break;
2981 }
2982 } else if (*it == u'\n') {
2983 if (escapeWhitespace) {
2984 replacement = "&#10;"_L1;
2985 break;
2986 }
2987 } else if (*it == u'\v' || *it == u'\f') {
2988 hasEncodingError = true;
2989 break;
2990 } else if (*it == u'\r') {
2991 if (escapeWhitespace) {
2992 replacement = "&#13;"_L1;
2993 break;
2994 }
2995 } else if (*it <= u'\x1F' || *it >= u'\uFFFE') {
2996 hasEncodingError = true;
2997 break;
2998 }
2999 ++it;
3000 }
3001
3002 escaped.append(View{mark, it});
3003 escaped.append(replacement);
3004 if (it != end)
3005 ++it;
3006 }
3007 } );
3008
3009 write(escaped);
3010}
3011
3012void QXmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) {
3013 if (namespaceDeclaration.prefix.isEmpty()) {
3014 write(" xmlns=\"");
3015 write(namespaceDeclaration.namespaceUri);
3016 write("\"");
3017 } else {
3018 write(" xmlns:");
3019 write(namespaceDeclaration.prefix);
3020 write("=\"");
3021 write(namespaceDeclaration.namespaceUri);
3022 write("\"");
3023 }
3024}
3025
3026bool QXmlStreamWriterPrivate::finishStartElement(bool contents)
3027{
3028 bool hadSomethingWritten = wroteSomething;
3029 wroteSomething = contents;
3030 if (!inStartElement)
3031 return hadSomethingWritten;
3032
3033 if (inEmptyElement) {
3034 write("/>");
3035 QXmlStreamWriterPrivate::Tag tag = tagStack_pop();
3036 lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3037 lastWasStartElement = false;
3038 } else {
3039 write(">");
3040 }
3041 inStartElement = inEmptyElement = false;
3042 lastNamespaceDeclaration = namespaceDeclarations.size();
3043 return hadSomethingWritten;
3044}
3045
3046QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::findNamespace(QAnyStringView namespaceUri, bool writeDeclaration, bool noDefault)
3047{
3048 for (NamespaceDeclaration &namespaceDeclaration : reversed(namespaceDeclarations)) {
3049 if (namespaceDeclaration.namespaceUri == namespaceUri) {
3050 if (!noDefault || !namespaceDeclaration.prefix.isEmpty())
3051 return namespaceDeclaration;
3052 }
3053 }
3054 if (namespaceUri.isEmpty())
3055 return emptyNamespace;
3056 NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push();
3057 if (namespaceUri.isEmpty()) {
3058 namespaceDeclaration.prefix.clear();
3059 } else {
3060 QString s;
3061 int n = ++namespacePrefixCount;
3062 forever {
3063 s = u'n' + QString::number(n++);
3064 qsizetype j = namespaceDeclarations.size() - 2;
3065 while (j >= 0 && namespaceDeclarations.at(j).prefix != s)
3066 --j;
3067 if (j < 0)
3068 break;
3069 }
3070 namespaceDeclaration.prefix = addToStringStorage(s);
3071 }
3072 namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri);
3073 if (writeDeclaration)
3074 writeNamespaceDeclaration(namespaceDeclaration);
3075 return namespaceDeclaration;
3076}
3077
3078
3079
3080void QXmlStreamWriterPrivate::indent(int level)
3081{
3082 write("\n");
3083 for (int i = 0; i < level; ++i)
3084 write(autoFormattingIndent);
3085}
3086
3087void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
3088{
3089 constexpr qsizetype MaxChunkSize = 512;
3090 char buffer [3 * MaxChunkSize];
3092 while (!s.isEmpty()) {
3093 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3094 char *end = QUtf8::convertFromUnicode(buffer, s.first(chunkSize), &state);
3095 doWriteToDevice(QUtf8StringView{buffer, end});
3096 s = s.sliced(chunkSize);
3097 }
3098 if (state.remainingChars > 0)
3099 hasEncodingError = true;
3100}
3101
3102void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
3103{
3104 QByteArrayView bytes = s;
3105 if (device->write(bytes.data(), bytes.size()) != bytes.size())
3106 hasIoError = true;
3107}
3108
3109void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
3110{
3111 constexpr qsizetype MaxChunkSize = 512;
3112 char buffer [2 * MaxChunkSize];
3113 while (!s.isEmpty()) {
3114 const qsizetype chunkSize = std::min(s.size(), MaxChunkSize);
3115 char *end = QUtf8::convertFromLatin1(buffer, s.first(chunkSize));
3116 doWriteToDevice(QUtf8StringView{buffer, end});
3117 s = s.sliced(chunkSize);
3118 }
3119}
3120
3126QXmlStreamWriter::QXmlStreamWriter()
3127 : d_ptr(new QXmlStreamWriterPrivate(this))
3128{
3129}
3130
3134QXmlStreamWriter::QXmlStreamWriter(QIODevice *device)
3135 : d_ptr(new QXmlStreamWriterPrivate(this))
3136{
3137 Q_D(QXmlStreamWriter);
3138 d->device = device;
3139}
3140
3145QXmlStreamWriter::QXmlStreamWriter(QByteArray *array)
3146 : d_ptr(new QXmlStreamWriterPrivate(this))
3147{
3148 Q_D(QXmlStreamWriter);
3149 d->device = new QBuffer(array);
3150 d->device->open(QIODevice::WriteOnly);
3151 d->deleteDevice = true;
3152}
3153
3154
3158QXmlStreamWriter::QXmlStreamWriter(QString *string)
3159 : d_ptr(new QXmlStreamWriterPrivate(this))
3160{
3161 Q_D(QXmlStreamWriter);
3162 d->stringDevice = string;
3163}
3164
3168QXmlStreamWriter::~QXmlStreamWriter()
3169{
3170}
3171
3172
3179void QXmlStreamWriter::setDevice(QIODevice *device)
3180{
3181 Q_D(QXmlStreamWriter);
3182 if (device == d->device)
3183 return;
3184 d->stringDevice = nullptr;
3185 if (d->deleteDevice) {
3186 delete d->device;
3187 d->deleteDevice = false;
3188 }
3189 d->device = device;
3190}
3191
3198QIODevice *QXmlStreamWriter::device() const
3199{
3200 Q_D(const QXmlStreamWriter);
3201 return d->device;
3202}
3203
3228void QXmlStreamWriter::setAutoFormatting(bool enable)
3229{
3230 Q_D(QXmlStreamWriter);
3231 d->autoFormatting = enable;
3232}
3233
3239bool QXmlStreamWriter::autoFormatting() const
3240{
3241 Q_D(const QXmlStreamWriter);
3242 return d->autoFormatting;
3243}
3244
3259void QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs)
3260{
3261 Q_D(QXmlStreamWriter);
3262 d->autoFormattingIndent.assign(size_t(qAbs(spacesOrTabs)), spacesOrTabs >= 0 ? ' ' : '\t');
3263}
3264
3265int QXmlStreamWriter::autoFormattingIndent() const
3266{
3267 Q_D(const QXmlStreamWriter);
3268 const QLatin1StringView indent(d->autoFormattingIndent);
3269 return indent.count(u' ') - indent.count(u'\t');
3270}
3271
3281bool QXmlStreamWriter::hasError() const
3282{
3283 Q_D(const QXmlStreamWriter);
3284 return d->hasIoError || d->hasEncodingError;
3285}
3286
3298void QXmlStreamWriter::writeAttribute(QAnyStringView qualifiedName, QAnyStringView value)
3299{
3300 Q_D(QXmlStreamWriter);
3301 Q_ASSERT(d->inStartElement);
3302 Q_ASSERT(count(qualifiedName, ':') <= 1);
3303 d->write(" ");
3304 d->write(qualifiedName);
3305 d->write("=\"");
3306 d->writeEscaped(value, true);
3307 d->write("\"");
3308}
3309
3321void QXmlStreamWriter::writeAttribute(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView value)
3322{
3323 Q_D(QXmlStreamWriter);
3324 Q_ASSERT(d->inStartElement);
3325 Q_ASSERT(!contains(name, ':'));
3326 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true);
3327 d->write(" ");
3328 if (!namespaceDeclaration.prefix.isEmpty()) {
3329 d->write(namespaceDeclaration.prefix);
3330 d->write(":");
3331 }
3332 d->write(name);
3333 d->write("=\"");
3334 d->writeEscaped(value, true);
3335 d->write("\"");
3336}
3337
3346void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& attribute)
3347{
3348 if (attribute.namespaceUri().isEmpty())
3349 writeAttribute(attribute.qualifiedName(), attribute.value());
3350 else
3351 writeAttribute(attribute.namespaceUri(), attribute.name(), attribute.value());
3352}
3353
3354
3364void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes)
3365{
3366 Q_D(QXmlStreamWriter);
3367 Q_ASSERT(d->inStartElement);
3368 Q_UNUSED(d);
3369 for (const auto &attr : attributes)
3370 writeAttribute(attr);
3371}
3372
3373
3385void QXmlStreamWriter::writeCDATA(QAnyStringView text)
3386{
3387 Q_D(QXmlStreamWriter);
3388 d->finishStartElement();
3389 d->write("<![CDATA[");
3390 while (!text.isEmpty()) {
3391 const auto idx = indexOf(text, "]]>"_L1);
3392 if (idx < 0)
3393 break; // no forbidden sequence found
3394 d->write(text.first(idx));
3395 d->write("]]" // text[idx, idx + 2)
3396 "]]><![CDATA[" // escape sequence to separate ]] and >
3397 ">"); // text[idx + 2, idx + 3)
3398 text = text.sliced(idx + 3); // skip over "]]>"
3399 }
3400 d->write(text); // write remainder
3401 d->write("]]>");
3402}
3403
3404
3414void QXmlStreamWriter::writeCharacters(QAnyStringView text)
3415{
3416 Q_D(QXmlStreamWriter);
3417 d->finishStartElement();
3418 d->writeEscaped(text);
3419}
3420
3421
3429void QXmlStreamWriter::writeComment(QAnyStringView text)
3430{
3431 Q_D(QXmlStreamWriter);
3432 Q_ASSERT(!contains(text, "--"_L1) && !endsWith(text, '-'));
3433 if (!d->finishStartElement(false) && d->autoFormatting)
3434 d->indent(d->tagStack.size());
3435 d->write("<!--");
3436 d->write(text);
3437 d->write("-->");
3438 d->inStartElement = d->lastWasStartElement = false;
3439}
3440
3441
3448void QXmlStreamWriter::writeDTD(QAnyStringView dtd)
3449{
3450 Q_D(QXmlStreamWriter);
3451 d->finishStartElement();
3452 if (d->autoFormatting)
3453 d->write("\n");
3454 d->write(dtd);
3455 if (d->autoFormatting)
3456 d->write("\n");
3457}
3458
3459
3460
3468void QXmlStreamWriter::writeEmptyElement(QAnyStringView qualifiedName)
3469{
3470 Q_D(QXmlStreamWriter);
3471 Q_ASSERT(count(qualifiedName, ':') <= 1);
3472 d->writeStartElement({}, qualifiedName);
3473 d->inEmptyElement = true;
3474}
3475
3476
3487void QXmlStreamWriter::writeEmptyElement(QAnyStringView namespaceUri, QAnyStringView name)
3488{
3489 Q_D(QXmlStreamWriter);
3490 Q_ASSERT(!contains(name, ':'));
3491 d->writeStartElement(namespaceUri, name);
3492 d->inEmptyElement = true;
3493}
3494
3495
3506void QXmlStreamWriter::writeTextElement(QAnyStringView qualifiedName, QAnyStringView text)
3507{
3508 writeStartElement(qualifiedName);
3511}
3512
3525void QXmlStreamWriter::writeTextElement(QAnyStringView namespaceUri, QAnyStringView name, QAnyStringView text)
3526{
3527 writeStartElement(namespaceUri, name);
3530}
3531
3532
3538void QXmlStreamWriter::writeEndDocument()
3539{
3540 Q_D(QXmlStreamWriter);
3541 while (d->tagStack.size())
3543 d->write("\n");
3544}
3545
3551void QXmlStreamWriter::writeEndElement()
3552{
3553 Q_D(QXmlStreamWriter);
3554 if (d->tagStack.isEmpty())
3555 return;
3556
3557 // shortcut: if nothing was written, close as empty tag
3558 if (d->inStartElement && !d->inEmptyElement) {
3559 d->write("/>");
3560 d->lastWasStartElement = d->inStartElement = false;
3561 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
3562 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3563 return;
3564 }
3565
3566 if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting)
3567 d->indent(d->tagStack.size()-1);
3568 if (d->tagStack.isEmpty())
3569 return;
3570 d->lastWasStartElement = false;
3571 QXmlStreamWriterPrivate::Tag tag = d->tagStack_pop();
3572 d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize;
3573 d->write("</");
3574 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
3575 d->write(tag.namespaceDeclaration.prefix);
3576 d->write(":");
3577 }
3578 d->write(tag.name);
3579 d->write(">");
3580}
3581
3582
3583
3590void QXmlStreamWriter::writeEntityReference(QAnyStringView name)
3591{
3592 Q_D(QXmlStreamWriter);
3593 d->finishStartElement();
3594 d->write("&");
3595 d->write(name);
3596 d->write(";");
3597}
3598
3599
3617void QXmlStreamWriter::writeNamespace(QAnyStringView namespaceUri, QAnyStringView prefix)
3618{
3619 Q_D(QXmlStreamWriter);
3620 Q_ASSERT(prefix != "xmlns"_L1);
3621 if (prefix.isEmpty()) {
3622 d->findNamespace(namespaceUri, d->inStartElement);
3623 } else {
3624 Q_ASSERT(!((prefix == "xml"_L1) ^ (namespaceUri == "http://www.w3.org/XML/1998/namespace"_L1)));
3625 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
3626 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
3627 namespaceDeclaration.prefix = d->addToStringStorage(prefix);
3628 namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri);
3629 if (d->inStartElement)
3630 d->writeNamespaceDeclaration(namespaceDeclaration);
3631 }
3632}
3633
3634
3648void QXmlStreamWriter::writeDefaultNamespace(QAnyStringView namespaceUri)
3649{
3650 Q_D(QXmlStreamWriter);
3651 Q_ASSERT(namespaceUri != "http://www.w3.org/XML/1998/namespace"_L1);
3652 Q_ASSERT(namespaceUri != "http://www.w3.org/2000/xmlns/"_L1);
3653 QXmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push();
3654 namespaceDeclaration.prefix.clear();
3655 namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri);
3656 if (d->inStartElement)
3657 d->writeNamespaceDeclaration(namespaceDeclaration);
3658}
3659
3660
3668void QXmlStreamWriter::writeProcessingInstruction(QAnyStringView target, QAnyStringView data)
3669{
3670 Q_D(QXmlStreamWriter);
3671 Q_ASSERT(!contains(data, "?>"_L1));
3672 if (!d->finishStartElement(false) && d->autoFormatting)
3673 d->indent(d->tagStack.size());
3674 d->write("<?");
3675 d->write(target);
3676 if (!data.isNull()) {
3677 d->write(" ");
3678 d->write(data);
3679 }
3680 d->write("?>");
3681}
3682
3683
3684
3692void QXmlStreamWriter::writeStartDocument()
3693{
3694 writeStartDocument("1.0"_L1);
3695}
3696
3697
3706void QXmlStreamWriter::writeStartDocument(QAnyStringView version)
3707{
3708 Q_D(QXmlStreamWriter);
3709 d->finishStartElement(false);
3710 d->write("<?xml version=\"");
3711 d->write(version);
3712 if (d->device) // stringDevice does not get any encoding
3713 d->write("\" encoding=\"UTF-8");
3714 d->write("\"?>");
3715}
3716
3726void QXmlStreamWriter::writeStartDocument(QAnyStringView version, bool standalone)
3727{
3728 Q_D(QXmlStreamWriter);
3729 d->finishStartElement(false);
3730 d->write("<?xml version=\"");
3731 d->write(version);
3732 if (d->device) // stringDevice does not get any encoding
3733 d->write("\" encoding=\"UTF-8");
3734 if (standalone)
3735 d->write("\" standalone=\"yes\"?>");
3736 else
3737 d->write("\" standalone=\"no\"?>");
3738}
3739
3740
3751void QXmlStreamWriter::writeStartElement(QAnyStringView qualifiedName)
3752{
3753 Q_D(QXmlStreamWriter);
3754 Q_ASSERT(count(qualifiedName, ':') <= 1);
3755 d->writeStartElement({}, qualifiedName);
3756}
3757
3758
3770void QXmlStreamWriter::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
3771{
3772 Q_D(QXmlStreamWriter);
3773 Q_ASSERT(!contains(name, ':'));
3774 d->writeStartElement(namespaceUri, name);
3775}
3776
3777void QXmlStreamWriterPrivate::writeStartElement(QAnyStringView namespaceUri, QAnyStringView name)
3778{
3779 if (!finishStartElement(false) && autoFormatting)
3780 indent(tagStack.size());
3781
3782 Tag &tag = tagStack_push();
3783 tag.name = addToStringStorage(name);
3784 tag.namespaceDeclaration = findNamespace(namespaceUri);
3785 write("<");
3786 if (!tag.namespaceDeclaration.prefix.isEmpty()) {
3787 write(tag.namespaceDeclaration.prefix);
3788 write(":");
3789 }
3790 write(tag.name);
3791 inStartElement = lastWasStartElement = true;
3792
3793 for (qsizetype i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i)
3794 writeNamespaceDeclaration(namespaceDeclarations[i]);
3795 tag.namespaceDeclarationsSize = lastNamespaceDeclaration;
3796}
3797
3798#if QT_CONFIG(xmlstreamreader)
3806void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader)
3807{
3808 switch (reader.tokenType()) {
3809 case QXmlStreamReader::NoToken:
3810 break;
3811 case QXmlStreamReader::StartDocument:
3812 writeStartDocument();
3813 break;
3814 case QXmlStreamReader::EndDocument:
3815 writeEndDocument();
3816 break;
3817 case QXmlStreamReader::StartElement: {
3818 writeStartElement(reader.namespaceUri(), reader.name());
3819 const QXmlStreamNamespaceDeclarations decls = reader.namespaceDeclarations();
3820 for (const auto &namespaceDeclaration : decls) {
3821 writeNamespace(namespaceDeclaration.namespaceUri(),
3822 namespaceDeclaration.prefix());
3823 }
3824 writeAttributes(reader.attributes());
3825 } break;
3826 case QXmlStreamReader::EndElement:
3828 break;
3829 case QXmlStreamReader::Characters:
3830 if (reader.isCDATA())
3831 writeCDATA(reader.text());
3832 else
3834 break;
3835 case QXmlStreamReader::Comment:
3836 writeComment(reader.text());
3837 break;
3838 case QXmlStreamReader::DTD:
3839 writeDTD(reader.text());
3840 break;
3841 case QXmlStreamReader::EntityReference:
3842 writeEntityReference(reader.name());
3843 break;
3844 case QXmlStreamReader::ProcessingInstruction:
3845 writeProcessingInstruction(reader.processingInstructionTarget(),
3846 reader.processingInstructionData());
3847 break;
3848 default:
3849 Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid);
3850 qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state.");
3851 break;
3852 }
3853}
3854
3855static constexpr bool isTokenAllowedInContext(QXmlStreamReader::TokenType type,
3857{
3858 switch (type) {
3859 case QXmlStreamReader::StartDocument:
3860 case QXmlStreamReader::DTD:
3862
3863 case QXmlStreamReader::StartElement:
3864 case QXmlStreamReader::EndElement:
3865 case QXmlStreamReader::Characters:
3866 case QXmlStreamReader::EntityReference:
3867 case QXmlStreamReader::EndDocument:
3869
3870 case QXmlStreamReader::Comment:
3871 case QXmlStreamReader::ProcessingInstruction:
3872 return true;
3873
3874 case QXmlStreamReader::NoToken:
3875 case QXmlStreamReader::Invalid:
3876 return false;
3877 }
3878
3879 // GCC 8.x does not treat __builtin_unreachable() as constexpr
3880#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
3881 Q_UNREACHABLE_RETURN(false);
3882#else
3883 return false;
3884#endif
3885}
3886
3894bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type)
3895{
3896 // Don't change currentContext, if Invalid or NoToken occur in the prolog
3897 if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken)
3898 return false;
3899
3900 // If a token type gets rejected in the body, there is no recovery
3901 const bool result = isTokenAllowedInContext(type, currentContext);
3903 return result;
3904
3905 // First non-Prolog token observed => switch context to body and check again.
3907 return isTokenAllowedInContext(type, currentContext);
3908}
3909
3916{
3917 Q_Q(QXmlStreamReader);
3918
3919 // The token type must be consumed, to keep track if the body has been reached.
3921 const bool ok = isValidToken(type);
3922
3923 // Do nothing if an error has been raised already (going along with an unexpected token)
3924 if (error != QXmlStreamReader::Error::NoError)
3925 return;
3926
3927 if (!ok) {
3928 raiseError(QXmlStreamReader::UnexpectedElementError,
3929 QObject::tr("Unexpected token type %1 in %2.")
3930 .arg(q->tokenString(), contextString(context)));
3931 return;
3932 }
3933
3934 if (type != QXmlStreamReader::DTD)
3935 return;
3936
3937 // Raise error on multiple DTD tokens
3938 if (foundDTD) {
3939 raiseError(QXmlStreamReader::UnexpectedElementError,
3940 QObject::tr("Found second DTD token in %1.").arg(contextString(context)));
3941 } else {
3942 foundDTD = true;
3943 }
3944}
3945
3970#endif // feature xmlstreamreader
3971#endif // feature xmlstreamwriter
3972
3974
3975#endif // feature xmlstream
IOBluetoothDevice * device
\inmodule QtCore
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:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
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
Definition qchar.h:48
@ LastValidCodePoint
Definition qchar.h:66
Category
This enum maps the Unicode character categories.
Definition qchar.h:104
@ Number_Letter
Definition qchar.h:110
@ Number_DecimalDigit
Definition qchar.h:109
@ Number_Other
Definition qchar.h:111
@ Letter_Other
Definition qchar.h:127
@ Letter_Uppercase
Definition qchar.h:123
@ Mark_NonSpacing
Definition qchar.h:105
@ Mark_Enclosing
Definition qchar.h:107
\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
void reserve(qsizetype size)
Definition qlist.h:746
void resize(qsizetype size)
Definition qlist.h:392
void clear()
Definition qlist.h:417
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:76
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...
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1173
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:898
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QString first(qsizetype n) const
Definition qstring.h:337
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
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:7822
QString sliced(qsizetype pos) const
Definition qstring.h:341
QString & append(QChar c)
Definition qstring.cpp:3227
QByteArray toUtf8() const &
Definition qstring.h:563
void resize(qsizetype size)
Sets the size of the string to size characters.
Definition qstring.cpp:2654
QStringView value() const
Definition qxmlstream.h:59
QStringView qualifiedName() const
Definition qxmlstream.h:55
QStringView namespaceUri() const
Definition qxmlstream.h:53
QStringView name() const
Definition qxmlstream.h:54
Q_CORE_EXPORT void append(const QString &namespaceUri, const QString &name, const QString &value)
Q_CORE_EXPORT QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
virtual QString resolveEntity(const QString &publicId, const QString &systemId)
virtual QString resolveUndeclaredEntity(const QString &name)
virtual ~QXmlStreamEntityResolver()
static int t_action(int state, int token)
static const char *const spell[]
QStringView namespaceUri() const
Definition qxmlstream.h:113
QStringView prefix() const
Definition qxmlstream.h:112
qsizetype initialTagStackStringStorageSize
XmlStringRef addToStringStorage(QAnyStringView s)
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
QXmlStreamEntityResolver * entityResolver
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)
#define this
Definition dialogs.cpp:9
QJSValue expected
Definition qjsengine.cpp:12
QString str
[2]
QString text
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
\macro QT_NAMESPACE
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
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT EGLint attribute
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:162
@ 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
GLenum GLuint buffer
GLenum type
GLenum target
GLboolean enable
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLint first
GLfloat n
GLuint GLenum GLenum transform
const GLubyte * c
GLuint GLfloat * val
GLenum array
GLint limit
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
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)
@ 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)
Tag
unsigned char uchar
Definition qtypes.h:27
short qint16
Definition qtypes.h:42
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
unsigned short ushort
Definition qtypes.h:28
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:17
static Q_CORE_EXPORT QByteArray convertFromUnicode(QStringView in)
static Q_CORE_EXPORT char * convertFromLatin1(char *out, QLatin1StringView in)