5#include <QtXml/qtxmlglobal.h>
9#include "qdomhelpers_p.h"
12#include <QtCore/qshareddata.h>
13#include "qxmlstream.h"
14#include "private/qxmlstream_p.h"
21using namespace Qt::StringLiterals;
23template <
typename T,
typename...Args>
24static QExplicitlySharedDataPointer<T> qdom_make_esdp(Args&&...args)
26 QExplicitlySharedDataPointer<T> dp{
27 new T(std::forward<Args>(args)...),
30 Q_ASSERT(dp->ref.loadRelaxed() == 1);
35
36
37
38
40QDomBuilder::QDomBuilder(QDomDocumentPrivate *d, QXmlStreamReader *r,
41 QDomDocument::ParseOptions options)
42 : doc(d), node(d), reader(r), parseOptions(options)
48QDomBuilder::~QDomBuilder() {}
50bool QDomBuilder::endDocument()
58bool QDomBuilder::startDTD(
const QString &name,
const QString &publicId,
const QString &systemId)
60 doc->doctype()->name = name;
61 doc->doctype()->publicId = publicId;
62 doc->doctype()->systemId = systemId;
66QString QDomBuilder::dtdInternalSubset(
const QString &dtd)
70 const QString &name = doc->doctype()->name;
71 QStringView tmp = QStringView(dtd).sliced(dtd.indexOf(name) + name.size());
73 const QString &publicId = doc->doctype()->publicId;
74 if (!publicId.isEmpty())
75 tmp = tmp.sliced(tmp.indexOf(publicId) + publicId.size());
77 const QString &systemId = doc->doctype()->systemId;
78 if (!systemId.isEmpty())
79 tmp = tmp.sliced(tmp.indexOf(systemId) + systemId.size());
81 const qsizetype obra = tmp.indexOf(u'[');
82 const qsizetype cbra = tmp.lastIndexOf(u']');
83 if (obra >= 0 && cbra >= 0)
84 return tmp.left(cbra).sliced(obra + 1).toString();
89bool QDomBuilder::parseDTD(
const QString &dtd)
91 doc->doctype()->internalSubset = dtdInternalSubset(dtd);
95bool QDomBuilder::startElement(
const QString &nsURI,
const QString &qName,
96 const QXmlStreamAttributes &atts)
98 const bool nsProcessing =
99 parseOptions.testFlag(QDomDocument::ParseOption::UseNamespaceProcessing);
101 nsProcessing ? doc->createElementNS(nsURI, qName) : doc->createElement(qName);
105 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
107 node->appendChild(n);
111 for (
const auto &attr : atts) {
112 auto domElement =
static_cast<QDomElementPrivate *>(node);
114 domElement->setAttributeNS(attr.namespaceUri().toString(),
115 attr.qualifiedName().toString(),
116 attr.value().toString());
118 domElement->setAttribute(attr.qualifiedName().toString(),
119 attr.value().toString());
126bool QDomBuilder::endElement()
128 if (!node || node == doc)
130 node = node->parent();
135bool QDomBuilder::characters(
const QString &characters,
bool cdata)
141 QExplicitlySharedDataPointer<QDomNodePrivate> n;
143 n.reset(doc->createCDATASection(characters));
144 }
else if (!entityName.isEmpty()) {
145 auto e = qdom_make_esdp<QDomEntityPrivate>(
146 doc,
nullptr, entityName, QString(), QString(), QString());
147 e->value = characters;
148 doc->doctype()->appendChild(e.get());
150 n.reset(doc->createEntityReference(entityName));
152 n.reset(doc->createTextNode(characters));
154 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
155 node->appendChild(n.get());
160bool QDomBuilder::processingInstruction(
const QString &target,
const QString &data)
163 n = doc->createProcessingInstruction(target, data);
165 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
166 node->appendChild(n);
172bool QDomBuilder::skippedEntity(
const QString &name)
174 QDomNodePrivate *n = doc->createEntityReference(name);
175 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
176 node->appendChild(n);
180void QDomBuilder::fatalError(
const QString &message)
182 parseResult.errorMessage = message;
183 parseResult.errorLine = reader->lineNumber();
184 parseResult.errorColumn = reader->columnNumber();
187bool QDomBuilder::startEntity(
const QString &name)
193bool QDomBuilder::endEntity()
199bool QDomBuilder::comment(
const QString &characters)
202 n = doc->createComment(characters);
203 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
204 node->appendChild(n);
208bool QDomBuilder::unparsedEntityDecl(
const QString &name,
const QString &publicId,
209 const QString &systemId,
const QString ¬ationName)
211 QDomEntityPrivate *e =
212 new QDomEntityPrivate(doc,
nullptr, name, publicId, systemId, notationName);
215 doc->doctype()->appendChild(e);
219bool QDomBuilder::externalEntityDecl(
const QString &name,
const QString &publicId,
220 const QString &systemId)
222 return unparsedEntityDecl(name, publicId, systemId, QString());
225bool QDomBuilder::notationDecl(
const QString &name,
const QString &publicId,
226 const QString &systemId)
228 QDomNotationPrivate *n =
new QDomNotationPrivate(doc,
nullptr, name, publicId, systemId);
231 doc->doctype()->appendChild(n);
236
237
238
239
241QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r,
242 QDomDocument::ParseOptions options)
243 : reader(r), domBuilder(d, r, options)
247bool QDomParser::parse()
249 return parseProlog() && parseBody();
252bool QDomParser::parseProlog()
256 bool foundDtd =
false;
258 while (!reader->atEnd()) {
261 if (reader->hasError()) {
262 domBuilder.fatalError(reader->errorString());
266 switch (reader->tokenType()) {
267 case QXmlStreamReader::StartDocument:
268 if (!reader->documentVersion().isEmpty()) {
269 QString value(u"version='"_s);
270 value += reader->documentVersion();
272 if (!reader->documentEncoding().isEmpty()) {
273 value += u" encoding='"_s;
274 value += reader->documentEncoding();
277 if (reader->isStandaloneDocument()) {
278 value += u" standalone='yes'"_s;
281 if (reader->hasStandaloneDeclaration())
282 value += u" standalone='no'"_s;
285 if (!domBuilder.processingInstruction(u"xml"_s, value)) {
286 domBuilder.fatalError(
287 QDomParser::tr(
"Error occurred while processing XML declaration"));
292 case QXmlStreamReader::DTD:
294 domBuilder.fatalError(QDomParser::tr(
"Multiple DTD sections are not allowed"));
299 if (!domBuilder.startDTD(reader->dtdName().toString(),
300 reader->dtdPublicId().toString(),
301 reader->dtdSystemId().toString())) {
302 domBuilder.fatalError(
303 QDomParser::tr(
"Error occurred while processing document type declaration"));
306 if (!domBuilder.parseDTD(reader->text().toString()))
308 if (!parseMarkupDecl())
311 case QXmlStreamReader::Comment:
312 if (!domBuilder.comment(reader->text().toString())) {
313 domBuilder.fatalError(QDomParser::tr(
"Error occurred while processing comment"));
317 case QXmlStreamReader::ProcessingInstruction:
318 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
319 reader->processingInstructionData().toString())) {
320 domBuilder.fatalError(
321 QDomParser::tr(
"Error occurred while processing a processing instruction"));
334bool QDomParser::parseBody()
338 std::stack<QString> tagStack;
339 while (!reader->atEnd() && !reader->hasError()) {
340 switch (reader->tokenType()) {
341 case QXmlStreamReader::StartElement:
342 tagStack.push(reader->qualifiedName().toString());
343 if (!domBuilder.startElement(reader->namespaceUri().toString(),
344 reader->qualifiedName().toString(),
345 reader->attributes())) {
346 domBuilder.fatalError(
347 QDomParser::tr(
"Error occurred while processing a start element"));
351 case QXmlStreamReader::EndElement:
352 if (tagStack.empty() || reader->qualifiedName() != tagStack.top()) {
353 domBuilder.fatalError(
354 QDomParser::tr(
"Unexpected end element '%1'").arg(reader->name()));
358 if (!domBuilder.endElement()) {
359 domBuilder.fatalError(
360 QDomParser::tr(
"Error occurred while processing an end element"));
364 case QXmlStreamReader::Characters:
367 if (reader->isCDATA() || domBuilder.preserveSpacingOnlyNodes()
368 || !(reader->isWhitespace() || reader->text().trimmed().isEmpty())) {
369 if (!domBuilder.characters(reader->text().toString(), reader->isCDATA())) {
370 domBuilder.fatalError(
371 QDomParser::tr(
"Error occurred while processing the element content"));
376 case QXmlStreamReader::Comment:
377 if (!domBuilder.comment(reader->text().toString())) {
378 domBuilder.fatalError(QDomParser::tr(
"Error occurred while processing comments"));
382 case QXmlStreamReader::ProcessingInstruction:
383 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
384 reader->processingInstructionData().toString())) {
385 domBuilder.fatalError(
386 QDomParser::tr(
"Error occurred while processing a processing instruction"));
390 case QXmlStreamReader::EntityReference:
391 if (!domBuilder.skippedEntity(reader->name().toString())) {
392 domBuilder.fatalError(
393 QDomParser::tr(
"Error occurred while processing an entity reference"));
398 domBuilder.fatalError(QDomParser::tr(
"Unexpected token"));
405 if (reader->hasError()) {
406 domBuilder.fatalError(reader->errorString());
411 if (!tagStack.empty()) {
412 domBuilder.fatalError(QDomParser::tr(
"Tag mismatch"));
419bool QDomParser::parseMarkupDecl()
423 const auto entities = reader->entityDeclarations();
424 for (
const auto &entityDecl : entities) {
428 if (!entityDecl.publicId().isEmpty() || !entityDecl.systemId().isEmpty()) {
430 if (!domBuilder.unparsedEntityDecl(entityDecl.name().toString(),
431 entityDecl.publicId().toString(),
432 entityDecl.systemId().toString(),
433 entityDecl.notationName().toString())) {
434 domBuilder.fatalError(
435 QDomParser::tr(
"Error occurred while processing entity declaration"));
441 const auto notations = reader->notationDeclarations();
442 for (
const auto ¬ationDecl : notations) {
443 if (!domBuilder.notationDecl(notationDecl.name().toString(),
444 notationDecl.publicId().toString(),
445 notationDecl.systemId().toString())) {
446 domBuilder.fatalError(
447 QDomParser::tr(
"Error occurred while processing notation declaration"));