diff options
Diffstat (limited to 'src/xml/dom/qdomhelpers.cpp')
-rw-r--r-- | src/xml/dom/qdomhelpers.cpp | 185 |
1 files changed, 79 insertions, 106 deletions
diff --git a/src/xml/dom/qdomhelpers.cpp b/src/xml/dom/qdomhelpers.cpp index 75d4c2c2b7..0649e0c75d 100644 --- a/src/xml/dom/qdomhelpers.cpp +++ b/src/xml/dom/qdomhelpers.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtXml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtXml/qtxmlglobal.h> @@ -44,28 +8,14 @@ #include "qdomhelpers_p.h" #include "qdom_p.h" #include "qxmlstream.h" +#include "private/qxmlstream_p.h" +#include <memory> #include <stack> QT_BEGIN_NAMESPACE -/************************************************************** - * - * 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()); -} +using namespace Qt::StringLiterals; /************************************************************** * @@ -73,14 +23,12 @@ int QDomDocumentLocator::line() const * **************************************************************/ -QDomBuilder::QDomBuilder(QDomDocumentPrivate *d, QXmlDocumentLocator *l, bool namespaceProcessing) - : errorLine(0), - errorColumn(0), - doc(d), - node(d), - locator(l), - nsProcessing(namespaceProcessing) +QDomBuilder::QDomBuilder(QDomDocumentPrivate *d, QXmlStreamReader *r, + QDomDocument::ParseOptions options) + : doc(d), node(d), reader(r), parseOptions(options) { + Q_ASSERT(doc); + Q_ASSERT(reader); } QDomBuilder::~QDomBuilder() {} @@ -101,15 +49,46 @@ bool QDomBuilder::startDTD(const QString &name, const QString &publicId, const Q return true; } +QString QDomBuilder::dtdInternalSubset(const QString &dtd) +{ + // https://www.w3.org/TR/xml/#NT-intSubset + // doctypedecl: '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' + const QString &name = doc->doctype()->name; + QStringView tmp = QStringView(dtd).sliced(dtd.indexOf(name) + name.size()); + + const QString &publicId = doc->doctype()->publicId; + if (!publicId.isEmpty()) + tmp = tmp.sliced(tmp.indexOf(publicId) + publicId.size()); + + const QString &systemId = doc->doctype()->systemId; + if (!systemId.isEmpty()) + tmp = tmp.sliced(tmp.indexOf(systemId) + systemId.size()); + + const qsizetype obra = tmp.indexOf(u'['); + const qsizetype cbra = tmp.lastIndexOf(u']'); + if (obra >= 0 && cbra >= 0) + return tmp.left(cbra).sliced(obra + 1).toString(); + + return QString(); +} + +bool QDomBuilder::parseDTD(const QString &dtd) +{ + doc->doctype()->internalSubset = dtdInternalSubset(dtd); + return true; +} + bool QDomBuilder::startElement(const QString &nsURI, const QString &qName, const QXmlStreamAttributes &atts) { + const bool nsProcessing = + parseOptions.testFlag(QDomDocument::ParseOption::UseNamespaceProcessing); QDomNodePrivate *n = nsProcessing ? doc->createElementNS(nsURI, qName) : doc->createElement(qName); if (!n) return false; - n->setLocation(locator->line(), locator->column()); + n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n); node = n; @@ -145,23 +124,23 @@ bool QDomBuilder::characters(const QString &characters, bool cdata) if (node == doc) return false; - QScopedPointer<QDomNodePrivate> n; + std::unique_ptr<QDomNodePrivate> n; if (cdata) { n.reset(doc->createCDATASection(characters)); } else if (!entityName.isEmpty()) { - QScopedPointer<QDomEntityPrivate> e( - new QDomEntityPrivate(doc, nullptr, entityName, QString(), QString(), QString())); + auto e = std::make_unique<QDomEntityPrivate>( + doc, nullptr, entityName, QString(), QString(), QString()); e->value = characters; e->ref.deref(); - doc->doctype()->appendChild(e.data()); - e.take(); + doc->doctype()->appendChild(e.get()); + Q_UNUSED(e.release()); n.reset(doc->createEntityReference(entityName)); } else { n.reset(doc->createTextNode(characters)); } - n->setLocation(locator->line(), locator->column()); - node->appendChild(n.data()); - n.take(); + n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); + node->appendChild(n.get()); + Q_UNUSED(n.release()); return true; } @@ -171,7 +150,7 @@ bool QDomBuilder::processingInstruction(const QString &target, const QString &da QDomNodePrivate *n; n = doc->createProcessingInstruction(target, data); if (n) { - n->setLocation(locator->line(), locator->column()); + n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n); return true; } else @@ -181,21 +160,16 @@ bool QDomBuilder::processingInstruction(const QString &target, const QString &da bool QDomBuilder::skippedEntity(const QString &name) { QDomNodePrivate *n = doc->createEntityReference(name); - n->setLocation(locator->line(), locator->column()); + n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n); return true; } void QDomBuilder::fatalError(const QString &message) { - errorMsg = message; - errorLine = static_cast<int>(locator->line()); - errorColumn = static_cast<int>(locator->column()); -} - -QDomBuilder::ErrorInfo QDomBuilder::error() const -{ - return ErrorInfo(errorMsg, errorLine, errorColumn); + parseResult.errorMessage = message; + parseResult.errorLine = reader->lineNumber(); + parseResult.errorColumn = reader->columnNumber(); } bool QDomBuilder::startEntity(const QString &name) @@ -214,7 +188,7 @@ bool QDomBuilder::comment(const QString &characters) { QDomNodePrivate *n; n = doc->createComment(characters); - n->setLocation(locator->line(), locator->column()); + n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n); return true; } @@ -252,8 +226,9 @@ bool QDomBuilder::notationDecl(const QString &name, const QString &publicId, * **************************************************************/ -QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r, bool namespaceProcessing) - : reader(r), locator(r), domBuilder(d, &locator, namespaceProcessing) +QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r, + QDomDocument::ParseOptions options) + : reader(r), domBuilder(d, r, options) { } @@ -262,11 +237,6 @@ bool QDomParser::parse() return parseProlog() && parseBody(); } -QDomBuilder::ErrorInfo QDomParser::errorInfo() const -{ - return domBuilder.error(); -} - bool QDomParser::parseProlog() { Q_ASSERT(reader); @@ -284,23 +254,23 @@ bool QDomParser::parseProlog() switch (reader->tokenType()) { case QXmlStreamReader::StartDocument: if (!reader->documentVersion().isEmpty()) { - QString value(QLatin1String("version='")); + QString value(u"version='"_s); value += reader->documentVersion(); - value += QLatin1Char('\''); + value += u'\''; if (!reader->documentEncoding().isEmpty()) { - value += QLatin1String(" encoding='"); + value += u" encoding='"_s; value += reader->documentEncoding(); - value += QLatin1Char('\''); + value += u'\''; } if (reader->isStandaloneDocument()) { - value += QLatin1String(" standalone='yes'"); + value += u" standalone='yes'"_s; } 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. + // Add the standalone attribute only if it was specified + if (reader->hasStandaloneDeclaration()) + value += u" standalone='no'"_s; } - if (!domBuilder.processingInstruction(QLatin1String("xml"), value)) { + if (!domBuilder.processingInstruction(u"xml"_s, value)) { domBuilder.fatalError( QDomParser::tr("Error occurred while processing XML declaration")); return false; @@ -321,6 +291,8 @@ bool QDomParser::parseProlog() QDomParser::tr("Error occurred while processing document type declaration")); return false; } + if (!domBuilder.parseDTD(reader->text().toString())) + return false; if (!parseMarkupDecl()) return false; break; @@ -378,13 +350,14 @@ bool QDomParser::parseBody() } 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; - } + // Skip the content if it contains only spacing characters, + // unless it's CDATA or PreserveSpacingOnlyNodes was specified. + if (reader->isCDATA() || domBuilder.preserveSpacingOnlyNodes() + || !(reader->isWhitespace() || reader->text().trimmed().isEmpty())) { + if (!domBuilder.characters(reader->text().toString(), reader->isCDATA())) { + domBuilder.fatalError( + QDomParser::tr("Error occurred while processing the element content")); + return false; } } break; @@ -437,7 +410,7 @@ bool QDomParser::parseMarkupDecl() const auto entities = reader->entityDeclarations(); for (const auto &entityDecl : entities) { - // Entity declarations are created only for Extrenal Entities. Internal Entities + // Entity declarations are created only for External 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()) { |