5#include <QtXml/qtxmlglobal.h>
9#include "qdomhelpers_p.h"
11#include "qxmlstream.h"
12#include "private/qxmlstream_p.h"
19using namespace Qt::StringLiterals;
22
23
24
25
27QDomBuilder::QDomBuilder(QDomDocumentPrivate *d, QXmlStreamReader *r,
28 QDomDocument::ParseOptions options)
29 : doc(d), node(d), reader(r), parseOptions(options)
35QDomBuilder::~QDomBuilder() {}
37bool QDomBuilder::endDocument()
45bool QDomBuilder::startDTD(
const QString &name,
const QString &publicId,
const QString &systemId)
47 doc->doctype()->name = name;
48 doc->doctype()->publicId = publicId;
49 doc->doctype()->systemId = systemId;
53QString QDomBuilder::dtdInternalSubset(
const QString &dtd)
57 const QString &name = doc->doctype()->name;
58 QStringView tmp = QStringView(dtd).sliced(dtd.indexOf(name) + name.size());
60 const QString &publicId = doc->doctype()->publicId;
61 if (!publicId.isEmpty())
62 tmp = tmp.sliced(tmp.indexOf(publicId) + publicId.size());
64 const QString &systemId = doc->doctype()->systemId;
65 if (!systemId.isEmpty())
66 tmp = tmp.sliced(tmp.indexOf(systemId) + systemId.size());
68 const qsizetype obra = tmp.indexOf(u'[');
69 const qsizetype cbra = tmp.lastIndexOf(u']');
70 if (obra >= 0 && cbra >= 0)
71 return tmp.left(cbra).sliced(obra + 1).toString();
76bool QDomBuilder::parseDTD(
const QString &dtd)
78 doc->doctype()->internalSubset = dtdInternalSubset(dtd);
82bool QDomBuilder::startElement(
const QString &nsURI,
const QString &qName,
83 const QXmlStreamAttributes &atts)
85 const bool nsProcessing =
86 parseOptions.testFlag(QDomDocument::ParseOption::UseNamespaceProcessing);
88 nsProcessing ? doc->createElementNS(nsURI, qName) : doc->createElement(qName);
92 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
98 for (
const auto &attr : atts) {
99 auto domElement =
static_cast<QDomElementPrivate *>(node);
101 domElement->setAttributeNS(attr.namespaceUri().toString(),
102 attr.qualifiedName().toString(),
103 attr.value().toString());
105 domElement->setAttribute(attr.qualifiedName().toString(),
106 attr.value().toString());
113bool QDomBuilder::endElement()
115 if (!node || node == doc)
117 node = node->parent();
122bool QDomBuilder::characters(
const QString &characters,
bool cdata)
128 std::unique_ptr<QDomNodePrivate> n;
130 n.reset(doc->createCDATASection(characters));
131 }
else if (!entityName.isEmpty()) {
132 auto e = std::make_unique<QDomEntityPrivate>(
133 doc,
nullptr, entityName, QString(), QString(), QString());
134 e->value = characters;
136 doc->doctype()->appendChild(e.get());
137 Q_UNUSED(e.release());
138 n.reset(doc->createEntityReference(entityName));
140 n.reset(doc->createTextNode(characters));
142 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
143 node->appendChild(n.get());
144 Q_UNUSED(n.release());
149bool QDomBuilder::processingInstruction(
const QString &target,
const QString &data)
152 n = doc->createProcessingInstruction(target, data);
154 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
155 node->appendChild(n);
161bool QDomBuilder::skippedEntity(
const QString &name)
163 QDomNodePrivate *n = doc->createEntityReference(name);
164 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
165 node->appendChild(n);
169void QDomBuilder::fatalError(
const QString &message)
171 parseResult.errorMessage = message;
172 parseResult.errorLine = reader->lineNumber();
173 parseResult.errorColumn = reader->columnNumber();
176bool QDomBuilder::startEntity(
const QString &name)
182bool QDomBuilder::endEntity()
188bool QDomBuilder::comment(
const QString &characters)
191 n = doc->createComment(characters);
192 n->setLocation(
int(reader->lineNumber()),
int(reader->columnNumber()));
193 node->appendChild(n);
197bool QDomBuilder::unparsedEntityDecl(
const QString &name,
const QString &publicId,
198 const QString &systemId,
const QString ¬ationName)
200 QDomEntityPrivate *e =
201 new QDomEntityPrivate(doc,
nullptr, name, publicId, systemId, notationName);
204 doc->doctype()->appendChild(e);
208bool QDomBuilder::externalEntityDecl(
const QString &name,
const QString &publicId,
209 const QString &systemId)
211 return unparsedEntityDecl(name, publicId, systemId, QString());
214bool QDomBuilder::notationDecl(
const QString &name,
const QString &publicId,
215 const QString &systemId)
217 QDomNotationPrivate *n =
new QDomNotationPrivate(doc,
nullptr, name, publicId, systemId);
220 doc->doctype()->appendChild(n);
225
226
227
228
230QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r,
231 QDomDocument::ParseOptions options)
232 : reader(r), domBuilder(d, r, options)
236bool QDomParser::parse()
238 return parseProlog() && parseBody();
241bool QDomParser::parseProlog()
245 bool foundDtd =
false;
247 while (!reader->atEnd()) {
250 if (reader->hasError()) {
251 domBuilder.fatalError(reader->errorString());
255 switch (reader->tokenType()) {
256 case QXmlStreamReader::StartDocument:
257 if (!reader->documentVersion().isEmpty()) {
258 QString value(u"version='"_s);
259 value += reader->documentVersion();
261 if (!reader->documentEncoding().isEmpty()) {
262 value += u" encoding='"_s;
263 value += reader->documentEncoding();
266 if (reader->isStandaloneDocument()) {
267 value += u" standalone='yes'"_s;
270 if (reader->hasStandaloneDeclaration())
271 value += u" standalone='no'"_s;
274 if (!domBuilder.processingInstruction(u"xml"_s, value)) {
275 domBuilder.fatalError(
276 QDomParser::tr(
"Error occurred while processing XML declaration"));
281 case QXmlStreamReader::DTD:
283 domBuilder.fatalError(QDomParser::tr(
"Multiple DTD sections are not allowed"));
288 if (!domBuilder.startDTD(reader->dtdName().toString(),
289 reader->dtdPublicId().toString(),
290 reader->dtdSystemId().toString())) {
291 domBuilder.fatalError(
292 QDomParser::tr(
"Error occurred while processing document type declaration"));
295 if (!domBuilder.parseDTD(reader->text().toString()))
297 if (!parseMarkupDecl())
300 case QXmlStreamReader::Comment:
301 if (!domBuilder.comment(reader->text().toString())) {
302 domBuilder.fatalError(QDomParser::tr(
"Error occurred while processing comment"));
306 case QXmlStreamReader::ProcessingInstruction:
307 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
308 reader->processingInstructionData().toString())) {
309 domBuilder.fatalError(
310 QDomParser::tr(
"Error occurred while processing a processing instruction"));
323bool QDomParser::parseBody()
327 std::stack<QString> tagStack;
328 while (!reader->atEnd() && !reader->hasError()) {
329 switch (reader->tokenType()) {
330 case QXmlStreamReader::StartElement:
331 tagStack.push(reader->qualifiedName().toString());
332 if (!domBuilder.startElement(reader->namespaceUri().toString(),
333 reader->qualifiedName().toString(),
334 reader->attributes())) {
335 domBuilder.fatalError(
336 QDomParser::tr(
"Error occurred while processing a start element"));
340 case QXmlStreamReader::EndElement:
341 if (tagStack.empty() || reader->qualifiedName() != tagStack.top()) {
342 domBuilder.fatalError(
343 QDomParser::tr(
"Unexpected end element '%1'").arg(reader->name()));
347 if (!domBuilder.endElement()) {
348 domBuilder.fatalError(
349 QDomParser::tr(
"Error occurred while processing an end element"));
353 case QXmlStreamReader::Characters:
356 if (reader->isCDATA() || domBuilder.preserveSpacingOnlyNodes()
357 || !(reader->isWhitespace() || reader->text().trimmed().isEmpty())) {
358 if (!domBuilder.characters(reader->text().toString(), reader->isCDATA())) {
359 domBuilder.fatalError(
360 QDomParser::tr(
"Error occurred while processing the element content"));
365 case QXmlStreamReader::Comment:
366 if (!domBuilder.comment(reader->text().toString())) {
367 domBuilder.fatalError(QDomParser::tr(
"Error occurred while processing comments"));
371 case QXmlStreamReader::ProcessingInstruction:
372 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
373 reader->processingInstructionData().toString())) {
374 domBuilder.fatalError(
375 QDomParser::tr(
"Error occurred while processing a processing instruction"));
379 case QXmlStreamReader::EntityReference:
380 if (!domBuilder.skippedEntity(reader->name().toString())) {
381 domBuilder.fatalError(
382 QDomParser::tr(
"Error occurred while processing an entity reference"));
387 domBuilder.fatalError(QDomParser::tr(
"Unexpected token"));
394 if (reader->hasError()) {
395 domBuilder.fatalError(reader->errorString());
400 if (!tagStack.empty()) {
401 domBuilder.fatalError(QDomParser::tr(
"Tag mismatch"));
408bool QDomParser::parseMarkupDecl()
412 const auto entities = reader->entityDeclarations();
413 for (
const auto &entityDecl : entities) {
417 if (!entityDecl.publicId().isEmpty() || !entityDecl.systemId().isEmpty()) {
419 if (!domBuilder.unparsedEntityDecl(entityDecl.name().toString(),
420 entityDecl.publicId().toString(),
421 entityDecl.systemId().toString(),
422 entityDecl.notationName().toString())) {
423 domBuilder.fatalError(
424 QDomParser::tr(
"Error occurred while processing entity declaration"));
430 const auto notations = reader->notationDeclarations();
431 for (
const auto ¬ationDecl : notations) {
432 if (!domBuilder.notationDecl(notationDecl.name().toString(),
433 notationDecl.publicId().toString(),
434 notationDecl.systemId().toString())) {
435 domBuilder.fatalError(
436 QDomParser::tr(
"Error occurred while processing notation declaration"));