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));
156 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
157 node->appendChild(n.get());
162bool QDomBuilder::processingInstruction(
const QString &target,
const QString &data)
165 n = doc->createProcessingInstruction(target, data);
167 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
168 node->appendChild(n);
174bool QDomBuilder::skippedEntity(
const QString &name)
176 QDomNodePrivate *n = doc->createEntityReference(name);
179 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
180 node->appendChild(n);
184void QDomBuilder::fatalError(
const QString &message)
186 parseResult.errorMessage = message;
187 parseResult.errorLine = reader->lineNumber();
188 parseResult.errorColumn = reader->columnNumber();
191bool QDomBuilder::startEntity(
const QString &name)
197bool QDomBuilder::endEntity()
203bool QDomBuilder::comment(
const QString &characters)
206 n = doc->createComment(characters);
209 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
210 node->appendChild(n);
214bool QDomBuilder::unparsedEntityDecl(
const QString &name,
const QString &publicId,
215 const QString &systemId,
const QString ¬ationName)
217 QDomEntityPrivate *e =
218 new QDomEntityPrivate(doc,
nullptr, name, publicId, systemId, notationName);
221 doc->doctype()->appendChild(e);
225bool QDomBuilder::externalEntityDecl(
const QString &name,
const QString &publicId,
226 const QString &systemId)
228 return unparsedEntityDecl(name, publicId, systemId, QString());
231bool QDomBuilder::notationDecl(
const QString &name,
const QString &publicId,
232 const QString &systemId)
234 QDomNotationPrivate *n =
new QDomNotationPrivate(doc,
nullptr, name, publicId, systemId);
237 doc->doctype()->appendChild(n);
242
243
244
245
247QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r,
248 QDomDocument::ParseOptions options)
249 : reader(r), domBuilder(d, r, options)
253bool QDomParser::parse()
255 return parseProlog() && parseBody();
258bool QDomParser::parseProlog()
262 bool foundDtd =
false;
264 while (!reader->atEnd()) {
267 if (reader->hasError()) {
268 domBuilder.fatalError(reader->errorString());
272 switch (reader->tokenType()) {
273 case QXmlStreamReader::StartDocument:
274 if (!reader->documentVersion().isEmpty()) {
275 QString value(u"version='"_s);
276 value += reader->documentVersion();
278 if (!reader->documentEncoding().isEmpty()) {
279 value += u" encoding='"_s;
280 value += reader->documentEncoding();
283 if (reader->isStandaloneDocument()) {
284 value += u" standalone='yes'"_s;
287 if (reader->hasStandaloneDeclaration())
288 value += u" standalone='no'"_s;
291 if (!domBuilder.processingInstruction(u"xml"_s, value)) {
292 domBuilder.fatalError(
293 QDomParser::tr(
"Error occurred while processing XML declaration"));
298 case QXmlStreamReader::DTD:
300 domBuilder.fatalError(QDomParser::tr(
"Multiple DTD sections are not allowed"));
305 if (!domBuilder.startDTD(reader->dtdName().toString(),
306 reader->dtdPublicId().toString(),
307 reader->dtdSystemId().toString())) {
308 domBuilder.fatalError(
309 QDomParser::tr(
"Error occurred while processing document type declaration"));
312 if (!domBuilder.parseDTD(reader->text().toString()))
314 if (!parseMarkupDecl())
317 case QXmlStreamReader::Comment:
318 if (!domBuilder.comment(reader->text().toString())) {
319 domBuilder.fatalError(QDomParser::tr(
"Error occurred while processing comment"));
323 case QXmlStreamReader::ProcessingInstruction:
324 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
325 reader->processingInstructionData().toString())) {
326 domBuilder.fatalError(
327 QDomParser::tr(
"Error occurred while processing a processing instruction"));
340bool QDomParser::parseBody()
344 std::stack<QString> tagStack;
345 while (!reader->atEnd() && !reader->hasError()) {
346 switch (reader->tokenType()) {
347 case QXmlStreamReader::StartElement:
348 tagStack.push(reader->qualifiedName().toString());
349 if (!domBuilder.startElement(reader->namespaceUri().toString(),
350 reader->qualifiedName().toString(),
351 reader->attributes())) {
352 domBuilder.fatalError(
353 QDomParser::tr(
"Error occurred while processing a start element"));
357 case QXmlStreamReader::EndElement:
358 if (tagStack.empty() || reader->qualifiedName() != tagStack.top()) {
359 domBuilder.fatalError(
360 QDomParser::tr(
"Unexpected end element '%1'").arg(reader->name()));
364 if (!domBuilder.endElement()) {
365 domBuilder.fatalError(
366 QDomParser::tr(
"Error occurred while processing an end element"));
370 case QXmlStreamReader::Characters:
373 if (reader->isCDATA() || domBuilder.preserveSpacingOnlyNodes()
374 || !(reader->isWhitespace() || reader->text().trimmed().isEmpty())) {
375 if (!domBuilder.characters(reader->text().toString(), reader->isCDATA())) {
376 domBuilder.fatalError(
377 QDomParser::tr(
"Error occurred while processing the element content"));
382 case QXmlStreamReader::Comment:
383 if (!domBuilder.comment(reader->text().toString())) {
384 domBuilder.fatalError(QDomParser::tr(
"Error occurred while processing comments"));
388 case QXmlStreamReader::ProcessingInstruction:
389 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
390 reader->processingInstructionData().toString())) {
391 domBuilder.fatalError(
392 QDomParser::tr(
"Error occurred while processing a processing instruction"));
396 case QXmlStreamReader::EntityReference:
397 if (!domBuilder.skippedEntity(reader->name().toString())) {
398 domBuilder.fatalError(
399 QDomParser::tr(
"Error occurred while processing an entity reference"));
404 domBuilder.fatalError(QDomParser::tr(
"Unexpected token"));
411 if (reader->hasError()) {
412 domBuilder.fatalError(reader->errorString());
417 if (!tagStack.empty()) {
418 domBuilder.fatalError(QDomParser::tr(
"Tag mismatch"));
425bool QDomParser::parseMarkupDecl()
429 const auto entities = reader->entityDeclarations();
430 for (
const auto &entityDecl : entities) {
434 if (!entityDecl.publicId().isEmpty() || !entityDecl.systemId().isEmpty()) {
436 if (!domBuilder.unparsedEntityDecl(entityDecl.name().toString(),
437 entityDecl.publicId().toString(),
438 entityDecl.systemId().toString(),
439 entityDecl.notationName().toString())) {
440 domBuilder.fatalError(
441 QDomParser::tr(
"Error occurred while processing entity declaration"));
447 const auto notations = reader->notationDeclarations();
448 for (
const auto ¬ationDecl : notations) {
449 if (!domBuilder.notationDecl(notationDecl.name().toString(),
450 notationDecl.publicId().toString(),
451 notationDecl.systemId().toString())) {
452 domBuilder.fatalError(
453 QDomParser::tr(
"Error occurred while processing notation declaration"));