diff options
Diffstat (limited to 'src/xml/dom/qdomhelpers.cpp')
-rw-r--r-- | src/xml/dom/qdomhelpers.cpp | 520 |
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 ¬ationName) +{ + 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 ¬ationName) { 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 ¬ationDecl : 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 |