summaryrefslogtreecommitdiffstats
path: root/src/xml/dom/qdomhelpers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xml/dom/qdomhelpers.cpp')
-rw-r--r--src/xml/dom/qdomhelpers.cpp520
1 files changed, 468 insertions, 52 deletions
diff --git a/src/xml/dom/qdomhelpers.cpp b/src/xml/dom/qdomhelpers.cpp
index 74d2c8c124e..10e37f7c0fe 100644
--- a/src/xml/dom/qdomhelpers.cpp
+++ b/src/xml/dom/qdomhelpers.cpp
@@ -39,26 +39,23 @@
#include "qdomhelpers_p.h"
#include "qdom_p.h"
+#include "qxmlstream.h"
#include "private/qxml_p.h"
QT_BEGIN_NAMESPACE
+#if QT_DEPRECATED_SINCE(5, 15)
+
/**************************************************************
*
* QDomHandler
*
**************************************************************/
-
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
QDomHandler::QDomHandler(QDomDocumentPrivate *adoc, QXmlSimpleReader *areader,
bool namespaceProcessing)
- : errorLine(0),
- errorColumn(0),
- doc(adoc),
- node(adoc),
- cdata(false),
- nsProcessing(namespaceProcessing),
- locator(nullptr),
- reader(areader)
+ : cdata(false), reader(areader), domBuilder(adoc, &locator, namespaceProcessing)
{
}
@@ -66,13 +63,183 @@ QDomHandler::~QDomHandler() {}
bool QDomHandler::endDocument()
{
+ return domBuilder.endDocument();
+}
+
+bool QDomHandler::startDTD(const QString &name, const QString &publicId, const QString &systemId)
+{
+ return domBuilder.startDTD(name, publicId, systemId);
+}
+
+bool QDomHandler::startElement(const QString &nsURI, const QString &, const QString &qName,
+ const QXmlAttributes &atts)
+{
+ return domBuilder.startElement(nsURI, qName, atts);
+}
+
+bool QDomHandler::endElement(const QString &, const QString &, const QString &)
+{
+ return domBuilder.endElement();
+}
+
+bool QDomHandler::characters(const QString &ch)
+{
+ return domBuilder.characters(ch, cdata);
+}
+
+bool QDomHandler::processingInstruction(const QString &target, const QString &data)
+{
+ return domBuilder.processingInstruction(target, data);
+}
+
+bool QDomHandler::skippedEntity(const QString &name)
+{
+ // we can only handle inserting entity references into content
+ if (reader && !reader->d_ptr->skipped_entity_in_content)
+ return true;
+
+ return domBuilder.skippedEntity(name);
+}
+
+bool QDomHandler::fatalError(const QXmlParseException &exception)
+{
+ domBuilder.errorMsg = exception.message();
+ domBuilder.errorLine = exception.lineNumber();
+ domBuilder.errorColumn = exception.columnNumber();
+ return QXmlDefaultHandler::fatalError(exception);
+}
+
+bool QDomHandler::startCDATA()
+{
+ cdata = true;
+ return true;
+}
+
+bool QDomHandler::endCDATA()
+{
+ cdata = false;
+ return true;
+}
+
+bool QDomHandler::startEntity(const QString &name)
+{
+ return domBuilder.startEntity(name);
+}
+
+bool QDomHandler::endEntity(const QString &)
+{
+ return domBuilder.endEntity();
+}
+
+bool QDomHandler::comment(const QString &ch)
+{
+ return domBuilder.comment(ch);
+}
+
+bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicId,
+ const QString &systemId, const QString &notationName)
+{
+ return domBuilder.unparsedEntityDecl(name, publicId, systemId, notationName);
+}
+
+bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicId,
+ const QString &systemId)
+{
+ return unparsedEntityDecl(name, publicId, systemId, QString());
+}
+
+bool QDomHandler::notationDecl(const QString &name, const QString &publicId,
+ const QString &systemId)
+{
+ return domBuilder.notationDecl(name, publicId, systemId);
+}
+
+void QDomHandler::setDocumentLocator(QXmlLocator *locator)
+{
+ this->locator.setLocator(locator);
+}
+
+QDomBuilder::ErrorInfo QDomHandler::errorInfo() const
+{
+ return domBuilder.error();
+}
+QT_WARNING_POP
+
+#endif // QT_DEPRECATED_SINCE(5, 15)
+
+/**************************************************************
+ *
+ * QXmlDocumentLocators
+ *
+ **************************************************************/
+
+int QDomDocumentLocator::column() const
+{
+ Q_ASSERT(reader);
+ return static_cast<int>(reader->columnNumber());
+}
+
+int QDomDocumentLocator::line() const
+{
+ Q_ASSERT(reader);
+ return static_cast<int>(reader->lineNumber());
+}
+
+#if QT_DEPRECATED_SINCE(5, 15)
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+void QSAXDocumentLocator::setLocator(QXmlLocator *l)
+{
+ locator = l;
+}
+QT_WARNING_POP
+
+int QSAXDocumentLocator::column() const
+{
+ if (!locator)
+ return 0;
+
+ return static_cast<int>(locator->columnNumber());
+}
+
+int QSAXDocumentLocator::line() const
+{
+ if (!locator)
+ return 0;
+
+ return static_cast<int>(locator->lineNumber());
+}
+
+#endif // QT_DEPRECATED_SINCE(5, 15)
+
+/**************************************************************
+ *
+ * QDomBuilder
+ *
+ **************************************************************/
+
+QDomBuilder::QDomBuilder(QDomDocumentPrivate *d, QXmlDocumentLocator *l, bool namespaceProcessing)
+ : errorLine(0),
+ errorColumn(0),
+ doc(d),
+ node(d),
+ locator(l),
+ nsProcessing(namespaceProcessing)
+{
+}
+
+QDomBuilder::~QDomBuilder() {}
+
+bool QDomBuilder::endDocument()
+{
// ### is this really necessary? (rms)
if (node != doc)
return false;
return true;
}
-bool QDomHandler::startDTD(const QString &name, const QString &publicId, const QString &systemId)
+bool QDomBuilder::startDTD(const QString &name, const QString &publicId, const QString &systemId)
{
doc->doctype()->name = name;
doc->doctype()->publicId = publicId;
@@ -80,7 +247,11 @@ bool QDomHandler::startDTD(const QString &name, const QString &publicId, const Q
return true;
}
-bool QDomHandler::startElement(const QString &nsURI, const QString &, const QString &qName,
+#if QT_DEPRECATED_SINCE(5, 15)
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+bool QDomBuilder::startElement(const QString &nsURI, const QString &qName,
const QXmlAttributes &atts)
{
// tag name
@@ -94,24 +265,65 @@ bool QDomHandler::startElement(const QString &nsURI, const QString &, const QStr
if (!n)
return false;
- n->setLocation(locator->lineNumber(), locator->columnNumber());
+ n->setLocation(locator->line(), locator->column());
node->appendChild(n);
node = n;
// attributes
for (int i = 0; i < atts.length(); i++) {
+ auto domElement = static_cast<QDomElementPrivate *>(node);
+ if (nsProcessing)
+ domElement->setAttributeNS(atts.uri(i), atts.qName(i), atts.value(i));
+ else
+ domElement->setAttribute(atts.qName(i), atts.value(i));
+ }
+
+ return true;
+}
+QT_WARNING_POP
+
+#endif // QT_DEPRECATED_SINCE(5, 15)
+
+inline QString stringRefToString(const QStringRef &stringRef)
+{
+ // Calling QStringRef::toString() on a NULL QStringRef in some cases returns
+ // an empty string (i.e. QString("")) instead of a NULL string (i.e. QString()).
+ // QDom implementation differentiates between NULL and empty strings, so
+ // we need this as workaround to keep the current behavior unchanged.
+ return stringRef.isNull() ? QString() : stringRef.toString();
+}
+
+bool QDomBuilder::startElement(const QString &nsURI, const QString &qName,
+ const QXmlStreamAttributes &atts)
+{
+ QDomNodePrivate *n =
+ nsProcessing ? doc->createElementNS(nsURI, qName) : doc->createElement(qName);
+ if (!n)
+ return false;
+
+ n->setLocation(locator->line(), locator->column());
+
+ node->appendChild(n);
+ node = n;
+
+ // attributes
+ for (const auto &attr : atts) {
+ auto domElement = static_cast<QDomElementPrivate *>(node);
if (nsProcessing) {
- ((QDomElementPrivate *)node)->setAttributeNS(atts.uri(i), atts.qName(i), atts.value(i));
+ domElement->setAttributeNS(stringRefToString(attr.namespaceUri()),
+ stringRefToString(attr.qualifiedName()),
+ stringRefToString(attr.value()));
} else {
- ((QDomElementPrivate *)node)->setAttribute(atts.qName(i), atts.value(i));
+ domElement->setAttribute(stringRefToString(attr.qualifiedName()),
+ stringRefToString(attr.value()));
}
}
return true;
}
-bool QDomHandler::endElement(const QString &, const QString &, const QString &)
+bool QDomBuilder::endElement()
{
if (!node || node == doc)
return false;
@@ -120,7 +332,7 @@ bool QDomHandler::endElement(const QString &, const QString &, const QString &)
return true;
}
-bool QDomHandler::characters(const QString &ch)
+bool QDomBuilder::characters(const QString &characters, bool cdata)
{
// No text as child of some document
if (node == doc)
@@ -128,91 +340,79 @@ bool QDomHandler::characters(const QString &ch)
QScopedPointer<QDomNodePrivate> n;
if (cdata) {
- n.reset(doc->createCDATASection(ch));
+ n.reset(doc->createCDATASection(characters));
} else if (!entityName.isEmpty()) {
QScopedPointer<QDomEntityPrivate> e(
new QDomEntityPrivate(doc, nullptr, entityName, QString(), QString(), QString()));
- e->value = ch;
+ e->value = characters;
e->ref.deref();
doc->doctype()->appendChild(e.data());
e.take();
n.reset(doc->createEntityReference(entityName));
} else {
- n.reset(doc->createTextNode(ch));
+ n.reset(doc->createTextNode(characters));
}
- n->setLocation(locator->lineNumber(), locator->columnNumber());
+ n->setLocation(locator->line(), locator->column());
node->appendChild(n.data());
n.take();
return true;
}
-bool QDomHandler::processingInstruction(const QString &target, const QString &data)
+bool QDomBuilder::processingInstruction(const QString &target, const QString &data)
{
QDomNodePrivate *n;
n = doc->createProcessingInstruction(target, data);
if (n) {
- n->setLocation(locator->lineNumber(), locator->columnNumber());
+ n->setLocation(locator->line(), locator->column());
node->appendChild(n);
return true;
} else
return false;
}
-bool QDomHandler::skippedEntity(const QString &name)
+bool QDomBuilder::skippedEntity(const QString &name)
{
- // we can only handle inserting entity references into content
- if (reader && !reader->d_ptr->skipped_entity_in_content)
- return true;
-
QDomNodePrivate *n = doc->createEntityReference(name);
- n->setLocation(locator->lineNumber(), locator->columnNumber());
+ n->setLocation(locator->line(), locator->column());
node->appendChild(n);
return true;
}
-bool QDomHandler::fatalError(const QXmlParseException &exception)
+void QDomBuilder::fatalError(const QString &message)
{
- errorMsg = exception.message();
- errorLine = exception.lineNumber();
- errorColumn = exception.columnNumber();
- return QXmlDefaultHandler::fatalError(exception);
+ errorMsg = message;
+ errorLine = static_cast<int>(locator->line());
+ errorColumn = static_cast<int>(locator->column());
}
-bool QDomHandler::startCDATA()
+QDomBuilder::ErrorInfo QDomBuilder::error() const
{
- cdata = true;
- return true;
+ return ErrorInfo(errorMsg, errorLine, errorColumn);
}
-bool QDomHandler::endCDATA()
-{
- cdata = false;
- return true;
-}
-
-bool QDomHandler::startEntity(const QString &name)
+bool QDomBuilder::startEntity(const QString &name)
{
entityName = name;
return true;
}
-bool QDomHandler::endEntity(const QString &)
+bool QDomBuilder::endEntity()
{
entityName.clear();
return true;
}
-bool QDomHandler::comment(const QString &ch)
+bool QDomBuilder::comment(const QString &characters)
{
QDomNodePrivate *n;
- n = doc->createComment(ch);
- n->setLocation(locator->lineNumber(), locator->columnNumber());
+ n = doc->createComment(characters);
+ n->setLocation(locator->line(), locator->column());
node->appendChild(n);
return true;
}
-bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicId,
+bool QDomBuilder::unparsedEntityDecl(const QString &name, const QString &publicId,
const QString &systemId, const QString &notationName)
{
QDomEntityPrivate *e =
@@ -223,13 +423,13 @@ bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicI
return true;
}
-bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicId,
+bool QDomBuilder::externalEntityDecl(const QString &name, const QString &publicId,
const QString &systemId)
{
return unparsedEntityDecl(name, publicId, systemId, QString());
}
-bool QDomHandler::notationDecl(const QString &name, const QString &publicId,
+bool QDomBuilder::notationDecl(const QString &name, const QString &publicId,
const QString &systemId)
{
QDomNotationPrivate *n = new QDomNotationPrivate(doc, nullptr, name, publicId, systemId);
@@ -239,9 +439,225 @@ bool QDomHandler::notationDecl(const QString &name, const QString &publicId,
return true;
}
-void QDomHandler::setDocumentLocator(QXmlLocator *locator)
+/**************************************************************
+ *
+ * QDomParser
+ *
+ **************************************************************/
+
+QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r, bool namespaceProcessing)
+ : reader(r), locator(r), domBuilder(d, &locator, namespaceProcessing)
+{
+}
+
+bool QDomParser::parse()
+{
+ return parseProlog() && parseBody();
+}
+
+QDomBuilder::ErrorInfo QDomParser::errorInfo() const
+{
+ return domBuilder.error();
+}
+
+bool QDomParser::parseProlog()
+{
+ Q_ASSERT(reader);
+
+ bool foundDtd = false;
+
+ while (!reader->atEnd()) {
+ reader->readNext();
+
+ if (reader->hasError()) {
+ domBuilder.fatalError(reader->errorString());
+ return false;
+ }
+
+ switch (reader->tokenType()) {
+ case QXmlStreamReader::StartDocument:
+ if (!reader->documentVersion().isEmpty()) {
+ QString value(QLatin1String("version='"));
+ value += reader->documentVersion();
+ value += QLatin1Char('\'');
+ if (!reader->documentEncoding().isEmpty()) {
+ value += QLatin1String(" encoding='");
+ value += reader->documentEncoding();
+ value += QLatin1Char('\'');
+ }
+ if (reader->isStandaloneDocument()) {
+ value += QLatin1String(" standalone='yes'");
+ } else {
+ // TODO: Add standalone='no', if 'standalone' is specified. With the current
+ // QXmlStreamReader there is no way to figure out if it was specified or not.
+ // QXmlStreamReader needs to be modified for handling that case correctly.
+ }
+
+ if (!domBuilder.processingInstruction(QLatin1String("xml"), value)) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing XML declaration"));
+ return false;
+ }
+ }
+ break;
+ case QXmlStreamReader::DTD:
+ if (foundDtd) {
+ domBuilder.fatalError(QDomParser::tr("Multiple DTD sections are not allowed"));
+ return false;
+ }
+ foundDtd = true;
+
+ if (!domBuilder.startDTD(stringRefToString(reader->dtdName()),
+ stringRefToString(reader->dtdPublicId()),
+ stringRefToString(reader->dtdSystemId()))) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing document type declaration"));
+ return false;
+ }
+ if (!parseMarkupDecl())
+ return false;
+ break;
+ case QXmlStreamReader::Comment:
+ if (!domBuilder.comment(reader->text().toString())) {
+ domBuilder.fatalError(QDomParser::tr("Error occurred while processing comment"));
+ return false;
+ }
+ break;
+ case QXmlStreamReader::ProcessingInstruction:
+ if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
+ reader->processingInstructionData().toString())) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing a processing instruction"));
+ return false;
+ }
+ break;
+ default:
+ // If the token is none of the above, prolog processing is done.
+ return true;
+ }
+ }
+
+ return true;
+}
+
+bool QDomParser::parseBody()
{
- this->locator = locator;
+ Q_ASSERT(reader);
+
+ std::stack<QStringRef> tagStack;
+ while (!reader->atEnd() && !reader->hasError()) {
+ switch (reader->tokenType()) {
+ case QXmlStreamReader::StartElement:
+ tagStack.push(reader->qualifiedName());
+ if (!domBuilder.startElement(stringRefToString(reader->namespaceUri()),
+ stringRefToString(reader->qualifiedName()),
+ reader->attributes())) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing a start element"));
+ return false;
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (tagStack.empty() || reader->qualifiedName() != tagStack.top()) {
+ domBuilder.fatalError(
+ QDomParser::tr("Unexpected end element '%1'").arg(reader->name()));
+ return false;
+ }
+ tagStack.pop();
+ if (!domBuilder.endElement()) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing an end element"));
+ return false;
+ }
+ break;
+ case QXmlStreamReader::Characters:
+ if (!reader->isWhitespace()) { // Skip the content consisting of only whitespaces
+ if (!reader->text().toString().trimmed().isEmpty()) {
+ if (!domBuilder.characters(reader->text().toString(), reader->isCDATA())) {
+ domBuilder.fatalError(QDomParser::tr(
+ "Error occurred while processing the element content"));
+ return false;
+ }
+ }
+ }
+ break;
+ case QXmlStreamReader::Comment:
+ if (!domBuilder.comment(reader->text().toString())) {
+ domBuilder.fatalError(QDomParser::tr("Error occurred while processing comments"));
+ return false;
+ }
+ break;
+ case QXmlStreamReader::ProcessingInstruction:
+ if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
+ reader->processingInstructionData().toString())) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing a processing instruction"));
+ return false;
+ }
+ break;
+ case QXmlStreamReader::EntityReference:
+ if (!domBuilder.skippedEntity(reader->name().toString())) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing an entity reference"));
+ return false;
+ }
+ break;
+ default:
+ domBuilder.fatalError(QDomParser::tr("Unexpected token"));
+ return false;
+ }
+
+ reader->readNext();
+ }
+
+ if (reader->hasError()) {
+ domBuilder.fatalError(reader->errorString());
+ reader->readNext();
+ return false;
+ }
+
+ if (!tagStack.empty()) {
+ domBuilder.fatalError(QDomParser::tr("Tag mismatch"));
+ return false;
+ }
+
+ return true;
+}
+
+bool QDomParser::parseMarkupDecl()
+{
+ Q_ASSERT(reader);
+
+ const auto entities = reader->entityDeclarations();
+ for (const auto &entityDecl : entities) {
+ // Entity declarations are created only for Extrenal Entities. Internal Entities
+ // are parsed, and QXmlStreamReader handles the parsing itself and returns the
+ // parsed result. So we don't need to do anything for the Internal Entities.
+ if (!entityDecl.publicId().isEmpty() || !entityDecl.systemId().isEmpty()) {
+ // External Entity
+ if (!domBuilder.unparsedEntityDecl(stringRefToString(entityDecl.name()),
+ stringRefToString(entityDecl.publicId()),
+ stringRefToString(entityDecl.systemId()),
+ stringRefToString(entityDecl.notationName()))) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing entity declaration"));
+ return false;
+ }
+ }
+ }
+
+ const auto notations = reader->notationDeclarations();
+ for (const auto &notationDecl : notations) {
+ if (!domBuilder.notationDecl(stringRefToString(notationDecl.name()),
+ stringRefToString(notationDecl.publicId()),
+ stringRefToString(notationDecl.systemId()))) {
+ domBuilder.fatalError(
+ QDomParser::tr("Error occurred while processing notation declaration"));
+ return false;
+ }
+ }
+
+ return true;
}
QT_END_NAMESPACE