Port QDomDocument to QXmlStreamReader
Reimplement QDomDocument using QXmlStreamReader and switch to the new implementation starting from Qt 6. The changes in the behavior are reflected in tests: some test cases which were marked as "expected to fail" are now passing. Task-number: QTBUG-76178 Change-Id: I5ace2f13c036a9a778de922b47a1ce35957ce5f6 Reviewed-by: Kai Koehne <kai.koehne@qt.io>
This commit is contained in:
parent
f0443984b8
commit
ccc2133c64
@ -59,6 +59,7 @@
|
|||||||
#include <qvariant.h>
|
#include <qvariant.h>
|
||||||
#include <qshareddata.h>
|
#include <qshareddata.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
#include <qxmlstream.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
@ -5734,6 +5735,34 @@ bool QDomDocumentPrivate::setContent(QXmlInputSource *source, QXmlReader *reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QDomDocumentPrivate::setContent(QXmlStreamReader *reader, bool namespaceProcessing,
|
||||||
|
QString *errorMsg, int *errorLine, int *errorColumn)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
impl = new QDomImplementationPrivate;
|
||||||
|
type = new QDomDocumentTypePrivate(this, this);
|
||||||
|
type->ref.deref();
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
qWarning("Failed to set content, XML reader is not initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomParser domParser(this, reader, namespaceProcessing);
|
||||||
|
|
||||||
|
if (!domParser.parse()) {
|
||||||
|
if (errorMsg)
|
||||||
|
*errorMsg = std::get<0>(domParser.errorInfo());
|
||||||
|
if (errorLine)
|
||||||
|
*errorLine = std::get<1>(domParser.errorInfo());
|
||||||
|
if (errorColumn)
|
||||||
|
*errorColumn = std::get<2>(domParser.errorInfo());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QDomNodePrivate* QDomDocumentPrivate::cloneNode(bool deep)
|
QDomNodePrivate* QDomDocumentPrivate::cloneNode(bool deep)
|
||||||
{
|
{
|
||||||
QDomNodePrivate *p = new QDomDocumentPrivate(this, deep);
|
QDomNodePrivate *p = new QDomDocumentPrivate(this, deep);
|
||||||
@ -6153,9 +6182,16 @@ bool QDomDocument::setContent(const QString& text, bool namespaceProcessing, QSt
|
|||||||
{
|
{
|
||||||
if (!impl)
|
if (!impl)
|
||||||
impl = new QDomDocumentPrivate();
|
impl = new QDomDocumentPrivate();
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QXmlInputSource source;
|
QXmlInputSource source;
|
||||||
source.setData(text);
|
source.setData(text);
|
||||||
return IMPL->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
return IMPL->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(text);
|
||||||
|
streamReader.setNamespaceProcessing(namespaceProcessing);
|
||||||
|
return IMPL->setContent(&streamReader, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -6215,10 +6251,17 @@ bool QDomDocument::setContent(const QByteArray &data, bool namespaceProcessing,
|
|||||||
{
|
{
|
||||||
if (!impl)
|
if (!impl)
|
||||||
impl = new QDomDocumentPrivate();
|
impl = new QDomDocumentPrivate();
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QBuffer buf;
|
QBuffer buf;
|
||||||
buf.setData(data);
|
buf.setData(data);
|
||||||
QXmlInputSource source(&buf);
|
QXmlInputSource source(&buf);
|
||||||
return IMPL->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
return IMPL->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(data);
|
||||||
|
streamReader.setNamespaceProcessing(namespaceProcessing);
|
||||||
|
return IMPL->setContent(&streamReader, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -6231,8 +6274,15 @@ bool QDomDocument::setContent(QIODevice* dev, bool namespaceProcessing, QString
|
|||||||
{
|
{
|
||||||
if (!impl)
|
if (!impl)
|
||||||
impl = new QDomDocumentPrivate();
|
impl = new QDomDocumentPrivate();
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QXmlInputSource source(dev);
|
QXmlInputSource source(dev);
|
||||||
return IMPL->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
return IMPL->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(dev);
|
||||||
|
streamReader.setNamespaceProcessing(namespaceProcessing);
|
||||||
|
return IMPL->setContent(&streamReader, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -6314,6 +6364,33 @@ bool QDomDocument::setContent(QXmlInputSource *source, QXmlReader *reader, QStri
|
|||||||
return IMPL->setContent(source, reader, nullptr, errorMsg, errorLine, errorColumn);
|
return IMPL->setContent(source, reader, nullptr, errorMsg, errorLine, errorColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\overload
|
||||||
|
\since 5.15
|
||||||
|
|
||||||
|
This function reads the XML document from the QXmlStreamReader \a reader
|
||||||
|
and parses it. Returns \c true if the content was successfully parsed;
|
||||||
|
otherwise returns \c false.
|
||||||
|
|
||||||
|
If \a namespaceProcessing is \c true, the parser recognizes namespaces in the XML
|
||||||
|
file and sets the prefix name, local name and namespace URI to appropriate values.
|
||||||
|
If \a namespaceProcessing is \c false, the parser does no namespace processing when
|
||||||
|
it reads the XML file.
|
||||||
|
|
||||||
|
If a parse error occurs, the error message is placed in \c{*}\a{errorMsg}, the line
|
||||||
|
number in \c{*}\a{errorLine} and the column number in \c{*}\a{errorColumn} (unless
|
||||||
|
the associated pointer is set to 0).
|
||||||
|
|
||||||
|
\sa QXmlStreamReader
|
||||||
|
*/
|
||||||
|
bool QDomDocument::setContent(QXmlStreamReader *reader, bool namespaceProcessing, QString *errorMsg,
|
||||||
|
int *errorLine, int *errorColumn)
|
||||||
|
{
|
||||||
|
if (!impl)
|
||||||
|
impl = new QDomDocumentPrivate();
|
||||||
|
return IMPL->setContent(reader, namespaceProcessing, errorMsg, errorLine, errorColumn);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Converts the parsed document back to its textual representation.
|
Converts the parsed document back to its textual representation.
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ class QDomNode;
|
|||||||
class QDomEntity;
|
class QDomEntity;
|
||||||
class QDomNotation;
|
class QDomNotation;
|
||||||
class QDomCharacterData;
|
class QDomCharacterData;
|
||||||
|
class QXmlStreamReader;
|
||||||
|
|
||||||
class Q_XML_EXPORT QDomImplementation
|
class Q_XML_EXPORT QDomImplementation
|
||||||
{
|
{
|
||||||
@ -343,6 +344,8 @@ public:
|
|||||||
bool setContent(const QString& text, QString *errorMsg=nullptr, int *errorLine=nullptr, int *errorColumn=nullptr );
|
bool setContent(const QString& text, QString *errorMsg=nullptr, int *errorLine=nullptr, int *errorColumn=nullptr );
|
||||||
bool setContent(QIODevice* dev, QString *errorMsg=nullptr, int *errorLine=nullptr, int *errorColumn=nullptr );
|
bool setContent(QIODevice* dev, QString *errorMsg=nullptr, int *errorLine=nullptr, int *errorColumn=nullptr );
|
||||||
bool setContent(QXmlInputSource *source, QXmlReader *reader, QString *errorMsg=nullptr, int *errorLine=nullptr, int *errorColumn=nullptr );
|
bool setContent(QXmlInputSource *source, QXmlReader *reader, QString *errorMsg=nullptr, int *errorLine=nullptr, int *errorColumn=nullptr );
|
||||||
|
bool setContent(QXmlStreamReader *reader, bool namespaceProcessing, QString *errorMsg = nullptr,
|
||||||
|
int *errorLine = nullptr, int *errorColumn = nullptr);
|
||||||
|
|
||||||
// Qt extensions
|
// Qt extensions
|
||||||
QString toString(int = 1) const;
|
QString toString(int = 1) const;
|
||||||
|
@ -465,6 +465,8 @@ public:
|
|||||||
int *errorLine, int *errorColumn);
|
int *errorLine, int *errorColumn);
|
||||||
bool setContent(QXmlInputSource *source, QXmlReader *reader, QXmlSimpleReader *simpleReader,
|
bool setContent(QXmlInputSource *source, QXmlReader *reader, QXmlSimpleReader *simpleReader,
|
||||||
QString *errorMsg, int *errorLine, int *errorColumn);
|
QString *errorMsg, int *errorLine, int *errorColumn);
|
||||||
|
bool setContent(QXmlStreamReader *reader, bool namespaceProcessing, QString *errorMsg,
|
||||||
|
int *errorLine, int *errorColumn);
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
QDomDocumentTypePrivate *doctype() { return type.data(); }
|
QDomDocumentTypePrivate *doctype() { return type.data(); }
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#include "qdomhelpers_p.h"
|
#include "qdomhelpers_p.h"
|
||||||
#include "qdom_p.h"
|
#include "qdom_p.h"
|
||||||
|
#include "qxmlstream.h"
|
||||||
#include "private/qxml_p.h"
|
#include "private/qxml_p.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
@ -166,6 +167,18 @@ QDomBuilder::ErrorInfo QDomHandler::errorInfo() const
|
|||||||
*
|
*
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
void QSAXDocumentLocator::setLocator(QXmlLocator *l)
|
void QSAXDocumentLocator::setLocator(QXmlLocator *l)
|
||||||
{
|
{
|
||||||
locator = l;
|
locator = l;
|
||||||
@ -252,6 +265,44 @@ bool QDomBuilder::startElement(const QString &nsURI, const QString &qName,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
domElement->setAttributeNS(stringRefToString(attr.namespaceUri()),
|
||||||
|
stringRefToString(attr.qualifiedName()),
|
||||||
|
stringRefToString(attr.value()));
|
||||||
|
} else {
|
||||||
|
domElement->setAttribute(stringRefToString(attr.qualifiedName()),
|
||||||
|
stringRefToString(attr.value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool QDomBuilder::endElement()
|
bool QDomBuilder::endElement()
|
||||||
{
|
{
|
||||||
if (!node || node == doc)
|
if (!node || node == doc)
|
||||||
@ -368,4 +419,225 @@ bool QDomBuilder::notationDecl(const QString &name, const QString &publicId,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
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
|
QT_END_NAMESPACE
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#ifndef QDOMHELPERS_P_H
|
#ifndef QDOMHELPERS_P_H
|
||||||
#define QDOMHELPERS_P_H
|
#define QDOMHELPERS_P_H
|
||||||
|
|
||||||
|
#include <qcoreapplication.h>
|
||||||
#include <qglobal.h>
|
#include <qglobal.h>
|
||||||
#include <qxml.h>
|
#include <qxml.h>
|
||||||
|
|
||||||
@ -57,6 +58,8 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
class QDomDocumentPrivate;
|
class QDomDocumentPrivate;
|
||||||
class QDomNodePrivate;
|
class QDomNodePrivate;
|
||||||
|
class QXmlStreamReader;
|
||||||
|
class QXmlStreamAttributes;
|
||||||
|
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
*
|
*
|
||||||
@ -77,6 +80,19 @@ public:
|
|||||||
virtual int line() const = 0;
|
virtual int line() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QDomDocumentLocator : public QXmlDocumentLocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QDomDocumentLocator(QXmlStreamReader *r) : reader(r) {}
|
||||||
|
~QDomDocumentLocator() override = default;
|
||||||
|
|
||||||
|
int column() const override;
|
||||||
|
int line() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QXmlStreamReader *reader;
|
||||||
|
};
|
||||||
|
|
||||||
class QSAXDocumentLocator : public QXmlDocumentLocator
|
class QSAXDocumentLocator : public QXmlDocumentLocator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -105,6 +121,7 @@ public:
|
|||||||
|
|
||||||
bool endDocument();
|
bool endDocument();
|
||||||
bool startElement(const QString &nsURI, const QString &qName, const QXmlAttributes &atts);
|
bool startElement(const QString &nsURI, const QString &qName, const QXmlAttributes &atts);
|
||||||
|
bool startElement(const QString &nsURI, const QString &qName, const QXmlStreamAttributes &atts);
|
||||||
bool endElement();
|
bool endElement();
|
||||||
bool characters(const QString &characters, bool cdata = false);
|
bool characters(const QString &characters, bool cdata = false);
|
||||||
bool processingInstruction(const QString &target, const QString &data);
|
bool processingInstruction(const QString &target, const QString &data);
|
||||||
@ -188,6 +205,31 @@ private:
|
|||||||
QDomBuilder domBuilder;
|
QDomBuilder domBuilder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* QDomParser
|
||||||
|
*
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
class QDomParser
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(QDomParser)
|
||||||
|
public:
|
||||||
|
QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r, bool namespaceProcessing);
|
||||||
|
|
||||||
|
bool parse();
|
||||||
|
QDomBuilder::ErrorInfo errorInfo() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parseProlog();
|
||||||
|
bool parseBody();
|
||||||
|
bool parseMarkupDecl();
|
||||||
|
|
||||||
|
QXmlStreamReader *reader;
|
||||||
|
QDomDocumentLocator locator;
|
||||||
|
QDomBuilder domBuilder;
|
||||||
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // QDOMHELPERS_P_H
|
#endif // QDOMHELPERS_P_H
|
||||||
|
@ -1793,6 +1793,12 @@ void tst_QTextDocument::toHtml()
|
|||||||
QCOMPARE(output, expectedOutput);
|
QCOMPARE(output, expectedOutput);
|
||||||
|
|
||||||
QDomDocument document;
|
QDomDocument document;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
QEXPECT_FAIL("charfmt-for-list-item",
|
||||||
|
"The attribute \"style\" is redefined in the generated HTML, which is not valid "
|
||||||
|
"according to XML standard. The new QDomDocument implementation follows the XML "
|
||||||
|
"standard.", Continue);
|
||||||
|
#endif
|
||||||
QVERIFY2(document.setContent(output), "Output was not valid XML");
|
QVERIFY2(document.setContent(output), "Output was not valid XML");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +173,8 @@ void tst_QDom::setContent_data()
|
|||||||
" </b3>\n"
|
" </b3>\n"
|
||||||
"</a1>\n");
|
"</a1>\n");
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
// These configurations cannot be supported by the QXmlStreamReader-based implementation
|
||||||
QTest::newRow( "02" ) << doc01
|
QTest::newRow( "02" ) << doc01
|
||||||
<< QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ')
|
<< QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ')
|
||||||
<< QStringList()
|
<< QStringList()
|
||||||
@ -227,6 +229,7 @@ void tst_QDom::setContent_data()
|
|||||||
" <c1/>\n"
|
" <c1/>\n"
|
||||||
" </b3>\n"
|
" </b3>\n"
|
||||||
"</a1>\n");
|
"</a1>\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
QTest::newRow("05") << QString("<message>\n"
|
QTest::newRow("05") << QString("<message>\n"
|
||||||
" <body><b>foo</b>>]]></body>\n"
|
" <body><b>foo</b>>]]></body>\n"
|
||||||
@ -242,6 +245,8 @@ void tst_QDom::setContent()
|
|||||||
{
|
{
|
||||||
QFETCH( QString, doc );
|
QFETCH( QString, doc );
|
||||||
|
|
||||||
|
QDomDocument domDoc;
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QXmlInputSource source;
|
QXmlInputSource source;
|
||||||
source.setData( doc );
|
source.setData( doc );
|
||||||
|
|
||||||
@ -258,8 +263,11 @@ void tst_QDom::setContent()
|
|||||||
reader.setFeature( *it, false );
|
reader.setFeature( *it, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
QDomDocument domDoc;
|
|
||||||
QVERIFY( domDoc.setContent( &source, &reader ) );
|
QVERIFY( domDoc.setContent( &source, &reader ) );
|
||||||
|
#else
|
||||||
|
QXmlStreamReader reader(doc);
|
||||||
|
QVERIFY(domDoc.setContent(&reader, true));
|
||||||
|
#endif
|
||||||
|
|
||||||
QString eRes;
|
QString eRes;
|
||||||
QTextStream ts( &eRes, QIODevice::WriteOnly );
|
QTextStream ts( &eRes, QIODevice::WriteOnly );
|
||||||
@ -1475,8 +1483,9 @@ void tst_QDom::normalizeAttributes() const
|
|||||||
QDomDocument doc;
|
QDomDocument doc;
|
||||||
QVERIFY(doc.setContent(&buffer, true));
|
QVERIFY(doc.setContent(&buffer, true));
|
||||||
|
|
||||||
// ### Qt 5: fix this, if we keep QDom at all
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QEXPECT_FAIL("", "The parser doesn't perform Attribute Value Normalization. Fixing that would change behavior.", Continue);
|
QEXPECT_FAIL("", "The parser doesn't perform Attribute Value Normalization. Fixing that would change behavior.", Continue);
|
||||||
|
#endif
|
||||||
QCOMPARE(doc.documentElement().attribute(QLatin1String("attribute")), QString::fromLatin1("a a"));
|
QCOMPARE(doc.documentElement().attribute(QLatin1String("attribute")), QString::fromLatin1("a a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1517,9 +1526,10 @@ void tst_QDom::serializeNamespaces() const
|
|||||||
"<b:element b:name=''/>"
|
"<b:element b:name=''/>"
|
||||||
"</doc>";
|
"</doc>";
|
||||||
|
|
||||||
|
QDomDocument doc;
|
||||||
QByteArray ba(input);
|
QByteArray ba(input);
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QBuffer buffer(&ba);
|
QBuffer buffer(&ba);
|
||||||
|
|
||||||
QVERIFY(buffer.open(QIODevice::ReadOnly));
|
QVERIFY(buffer.open(QIODevice::ReadOnly));
|
||||||
|
|
||||||
QXmlInputSource source(&buffer);
|
QXmlInputSource source(&buffer);
|
||||||
@ -1527,8 +1537,11 @@ void tst_QDom::serializeNamespaces() const
|
|||||||
reader.setFeature("http://xml.org/sax/features/namespaces", true);
|
reader.setFeature("http://xml.org/sax/features/namespaces", true);
|
||||||
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
|
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
|
||||||
|
|
||||||
QDomDocument doc;
|
|
||||||
QVERIFY(doc.setContent(&source, &reader));
|
QVERIFY(doc.setContent(&source, &reader));
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(input);
|
||||||
|
QVERIFY(doc.setContent(&streamReader, true));
|
||||||
|
#endif
|
||||||
|
|
||||||
const QByteArray serialized(doc.toByteArray());
|
const QByteArray serialized(doc.toByteArray());
|
||||||
|
|
||||||
@ -1552,7 +1565,9 @@ void tst_QDom::flagInvalidNamespaces() const
|
|||||||
|
|
||||||
QDomDocument doc;
|
QDomDocument doc;
|
||||||
QVERIFY(!doc.setContent(QString::fromLatin1(input, true)));
|
QVERIFY(!doc.setContent(QString::fromLatin1(input, true)));
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QEXPECT_FAIL("", "The parser doesn't flag identical qualified attribute names. Fixing this would change behavior.", Continue);
|
QEXPECT_FAIL("", "The parser doesn't flag identical qualified attribute names. Fixing this would change behavior.", Continue);
|
||||||
|
#endif
|
||||||
QVERIFY(!doc.setContent(QString::fromLatin1(input)));
|
QVERIFY(!doc.setContent(QString::fromLatin1(input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1563,7 +1578,9 @@ void tst_QDom::flagUndeclaredNamespace() const
|
|||||||
"<b:element b:name=''/>"
|
"<b:element b:name=''/>"
|
||||||
"</a:doc>";
|
"</a:doc>";
|
||||||
|
|
||||||
|
QDomDocument doc;
|
||||||
QByteArray ba(input);
|
QByteArray ba(input);
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QBuffer buffer(&ba);
|
QBuffer buffer(&ba);
|
||||||
|
|
||||||
QVERIFY(buffer.open(QIODevice::ReadOnly));
|
QVERIFY(buffer.open(QIODevice::ReadOnly));
|
||||||
@ -1573,9 +1590,12 @@ void tst_QDom::flagUndeclaredNamespace() const
|
|||||||
reader.setFeature("http://xml.org/sax/features/namespaces", true);
|
reader.setFeature("http://xml.org/sax/features/namespaces", true);
|
||||||
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
|
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
|
||||||
|
|
||||||
QDomDocument doc;
|
|
||||||
QEXPECT_FAIL("", "The parser doesn't flag not declared prefixes. Fixing this would change behavior.", Continue);
|
QEXPECT_FAIL("", "The parser doesn't flag not declared prefixes. Fixing this would change behavior.", Continue);
|
||||||
QVERIFY(!doc.setContent(&source, &reader));
|
QVERIFY(!doc.setContent(&source, &reader));
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(ba);
|
||||||
|
QVERIFY(!doc.setContent(&streamReader, true));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QDom::indentComments() const
|
void tst_QDom::indentComments() const
|
||||||
@ -1642,7 +1662,9 @@ void tst_QDom::reportDuplicateAttributes() const
|
|||||||
QDomDocument dd;
|
QDomDocument dd;
|
||||||
bool isSuccess = dd.setContent(QLatin1String("<test x=\"1\" x=\"2\"/>"));
|
bool isSuccess = dd.setContent(QLatin1String("<test x=\"1\" x=\"2\"/>"));
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QEXPECT_FAIL("", "The parser doesn't flag duplicate attributes. Fixing this would change behavior.", Continue);
|
QEXPECT_FAIL("", "The parser doesn't flag duplicate attributes. Fixing this would change behavior.", Continue);
|
||||||
|
#endif
|
||||||
QVERIFY2(!isSuccess, "Duplicate attributes are well-formedness errors, and should be reported as such.");
|
QVERIFY2(!isSuccess, "Duplicate attributes are well-formedness errors, and should be reported as such.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1842,10 +1864,15 @@ void tst_QDom::doubleNamespaceDeclarations() const
|
|||||||
QFile file(testFile);
|
QFile file(testFile);
|
||||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QXmlSimpleReader reader;
|
QXmlSimpleReader reader;
|
||||||
|
|
||||||
QXmlInputSource source(&file);
|
QXmlInputSource source(&file);
|
||||||
QVERIFY(doc.setContent(&source, &reader));
|
QVERIFY(doc.setContent(&source, &reader));
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(&file);
|
||||||
|
QVERIFY(doc.setContent(&streamReader, true));
|
||||||
|
#endif
|
||||||
|
|
||||||
// tst_QDom relies on a specific QHash ordering, see QTBUG-25071
|
// tst_QDom relies on a specific QHash ordering, see QTBUG-25071
|
||||||
QString docAsString = doc.toString(0);
|
QString docAsString = doc.toString(0);
|
||||||
@ -1862,11 +1889,15 @@ void tst_QDom::setContentQXmlReaderOverload() const
|
|||||||
{
|
{
|
||||||
QDomDocument doc;
|
QDomDocument doc;
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QXmlSimpleReader reader;
|
QXmlSimpleReader reader;
|
||||||
QXmlInputSource data;
|
QXmlInputSource data;
|
||||||
data.setData(QByteArray("<e/>"));
|
data.setData(QByteArray("<e/>"));
|
||||||
|
|
||||||
doc.setContent(&data, true);
|
doc.setContent(&data, true);
|
||||||
|
#else
|
||||||
|
QXmlStreamReader streamReader(QByteArray("<e/>"));
|
||||||
|
doc.setContent(&streamReader, true);
|
||||||
|
#endif
|
||||||
QCOMPARE(doc.documentElement().nodeName(), QString::fromLatin1("e"));
|
QCOMPARE(doc.documentElement().nodeName(), QString::fromLatin1("e"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1961,6 +1992,10 @@ void tst_QDom::setContentWhitespace_data() const
|
|||||||
|
|
||||||
void tst_QDom::taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const
|
void tst_QDom::taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const
|
||||||
{
|
{
|
||||||
|
// QXmlStreamReader fails to read XML documents with unknown encoding. It
|
||||||
|
// needs to be modified if we want to support this case with the QXmlStreamReader-based
|
||||||
|
// implementation.
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QString xmlWithUnknownEncoding("<?xml version='1.0' encoding='unknown-encoding'?>"
|
QString xmlWithUnknownEncoding("<?xml version='1.0' encoding='unknown-encoding'?>"
|
||||||
"<foo>"
|
"<foo>"
|
||||||
" <bar>How will this sentence be handled?</bar>"
|
" <bar>How will this sentence be handled?</bar>"
|
||||||
@ -1970,6 +2005,7 @@ void tst_QDom::taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() co
|
|||||||
|
|
||||||
QString dontAssert = d.toString(); // this should not assert
|
QString dontAssert = d.toString(); // this should not assert
|
||||||
QVERIFY(true);
|
QVERIFY(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QDom::cloneDTD_QTBUG8398() const
|
void tst_QDom::cloneDTD_QTBUG8398() const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user