Move out the reusable part of QDomHandler to a new class

QDomHandler implements methods for building the DOM tree. These
methods can be reused also in the new QXmlStreamReader-based
implementation. They are moved to a new QDomBuilder class and
QDomHandler become a wrapper around it.

Task-number: QTBUG-76178
Change-Id: I01956c209ae253b69c23f20d90a5befe7b5329a0
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
This commit is contained in:
Sona Kurazyan 2019-10-24 11:10:56 +02:00 committed by Kai Koehne
parent 3c7df4a0ff
commit fa2c9a27e2
3 changed files with 313 additions and 117 deletions

View File

@ -5728,11 +5728,11 @@ bool QDomDocumentPrivate::setContent(QXmlInputSource *source, QXmlReader *reader
if (!reader->parse(source)) { if (!reader->parse(source)) {
if (errorMsg) if (errorMsg)
*errorMsg = hnd.errorMsg; *errorMsg = std::get<0>(hnd.errorInfo());
if (errorLine) if (errorLine)
*errorLine = hnd.errorLine; *errorLine = std::get<1>(hnd.errorInfo());
if (errorColumn) if (errorColumn)
*errorColumn = hnd.errorColumn; *errorColumn = std::get<2>(hnd.errorInfo());
return false; return false;
} }

View File

@ -51,14 +51,7 @@ QT_BEGIN_NAMESPACE
QDomHandler::QDomHandler(QDomDocumentPrivate *adoc, QXmlSimpleReader *areader, QDomHandler::QDomHandler(QDomDocumentPrivate *adoc, QXmlSimpleReader *areader,
bool namespaceProcessing) bool namespaceProcessing)
: errorLine(0), : cdata(false), reader(areader), domBuilder(adoc, &locator, namespaceProcessing)
errorColumn(0),
doc(adoc),
node(adoc),
cdata(false),
nsProcessing(namespaceProcessing),
locator(nullptr),
reader(areader)
{ {
} }
@ -66,97 +59,33 @@ QDomHandler::~QDomHandler() {}
bool QDomHandler::endDocument() bool QDomHandler::endDocument()
{ {
// ### is this really necessary? (rms) return domBuilder.endDocument();
if (node != doc)
return false;
return true;
} }
bool QDomHandler::startDTD(const QString &name, const QString &publicId, const QString &systemId) bool QDomHandler::startDTD(const QString &name, const QString &publicId, const QString &systemId)
{ {
doc->doctype()->name = name; return domBuilder.startDTD(name, publicId, systemId);
doc->doctype()->publicId = publicId;
doc->doctype()->systemId = systemId;
return true;
} }
bool QDomHandler::startElement(const QString &nsURI, const QString &, const QString &qName, bool QDomHandler::startElement(const QString &nsURI, const QString &, const QString &qName,
const QXmlAttributes &atts) const QXmlAttributes &atts)
{ {
// tag name return domBuilder.startElement(nsURI, qName, atts);
QDomNodePrivate *n;
if (nsProcessing) {
n = doc->createElementNS(nsURI, qName);
} else {
n = doc->createElement(qName);
}
if (!n)
return false;
n->setLocation(locator->lineNumber(), locator->columnNumber());
node->appendChild(n);
node = n;
// attributes
for (int i = 0; i < atts.length(); i++) {
if (nsProcessing) {
((QDomElementPrivate *)node)->setAttributeNS(atts.uri(i), atts.qName(i), atts.value(i));
} else {
((QDomElementPrivate *)node)->setAttribute(atts.qName(i), atts.value(i));
}
}
return true;
} }
bool QDomHandler::endElement(const QString &, const QString &, const QString &) bool QDomHandler::endElement(const QString &, const QString &, const QString &)
{ {
if (!node || node == doc) return domBuilder.endElement();
return false;
node = node->parent();
return true;
} }
bool QDomHandler::characters(const QString &ch) bool QDomHandler::characters(const QString &ch)
{ {
// No text as child of some document return domBuilder.characters(ch, cdata);
if (node == doc)
return false;
QScopedPointer<QDomNodePrivate> n;
if (cdata) {
n.reset(doc->createCDATASection(ch));
} else if (!entityName.isEmpty()) {
QScopedPointer<QDomEntityPrivate> e(
new QDomEntityPrivate(doc, nullptr, entityName, QString(), QString(), QString()));
e->value = ch;
e->ref.deref();
doc->doctype()->appendChild(e.data());
e.take();
n.reset(doc->createEntityReference(entityName));
} else {
n.reset(doc->createTextNode(ch));
}
n->setLocation(locator->lineNumber(), locator->columnNumber());
node->appendChild(n.data());
n.take();
return true;
} }
bool QDomHandler::processingInstruction(const QString &target, const QString &data) bool QDomHandler::processingInstruction(const QString &target, const QString &data)
{ {
QDomNodePrivate *n; return domBuilder.processingInstruction(target, data);
n = doc->createProcessingInstruction(target, data);
if (n) {
n->setLocation(locator->lineNumber(), locator->columnNumber());
node->appendChild(n);
return true;
} else
return false;
} }
bool QDomHandler::skippedEntity(const QString &name) bool QDomHandler::skippedEntity(const QString &name)
@ -165,17 +94,14 @@ bool QDomHandler::skippedEntity(const QString &name)
if (reader && !reader->d_ptr->skipped_entity_in_content) if (reader && !reader->d_ptr->skipped_entity_in_content)
return true; return true;
QDomNodePrivate *n = doc->createEntityReference(name); return domBuilder.skippedEntity(name);
n->setLocation(locator->lineNumber(), locator->columnNumber());
node->appendChild(n);
return true;
} }
bool QDomHandler::fatalError(const QXmlParseException &exception) bool QDomHandler::fatalError(const QXmlParseException &exception)
{ {
errorMsg = exception.message(); domBuilder.errorMsg = exception.message();
errorLine = exception.lineNumber(); domBuilder.errorLine = exception.lineNumber();
errorColumn = exception.columnNumber(); domBuilder.errorColumn = exception.columnNumber();
return QXmlDefaultHandler::fatalError(exception); return QXmlDefaultHandler::fatalError(exception);
} }
@ -193,34 +119,23 @@ bool QDomHandler::endCDATA()
bool QDomHandler::startEntity(const QString &name) bool QDomHandler::startEntity(const QString &name)
{ {
entityName = name; return domBuilder.startEntity(name);
return true;
} }
bool QDomHandler::endEntity(const QString &) bool QDomHandler::endEntity(const QString &)
{ {
entityName.clear(); return domBuilder.endEntity();
return true;
} }
bool QDomHandler::comment(const QString &ch) bool QDomHandler::comment(const QString &ch)
{ {
QDomNodePrivate *n; return domBuilder.comment(ch);
n = doc->createComment(ch);
n->setLocation(locator->lineNumber(), locator->columnNumber());
node->appendChild(n);
return true;
} }
bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicId, bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicId,
const QString &systemId, const QString &notationName) const QString &systemId, const QString &notationName)
{ {
QDomEntityPrivate *e = return domBuilder.unparsedEntityDecl(name, publicId, systemId, notationName);
new QDomEntityPrivate(doc, nullptr, name, publicId, systemId, notationName);
// keep the refcount balanced: appendChild() does a ref anyway.
e->ref.deref();
doc->doctype()->appendChild(e);
return true;
} }
bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicId, bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicId,
@ -231,6 +146,220 @@ bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicI
bool QDomHandler::notationDecl(const QString &name, const QString &publicId, bool QDomHandler::notationDecl(const QString &name, const QString &publicId,
const QString &systemId) 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();
}
/**************************************************************
*
* QXmlDocumentLocators
*
**************************************************************/
void QSAXDocumentLocator::setLocator(QXmlLocator *l)
{
locator = l;
}
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());
}
/**************************************************************
*
* 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 QDomBuilder::startDTD(const QString &name, const QString &publicId, const QString &systemId)
{
doc->doctype()->name = name;
doc->doctype()->publicId = publicId;
doc->doctype()->systemId = systemId;
return true;
}
bool QDomBuilder::startElement(const QString &nsURI, const QString &qName,
const QXmlAttributes &atts)
{
// tag name
QDomNodePrivate *n;
if (nsProcessing) {
n = doc->createElementNS(nsURI, qName);
} else {
n = doc->createElement(qName);
}
if (!n)
return false;
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;
}
bool QDomBuilder::endElement()
{
if (!node || node == doc)
return false;
node = node->parent();
return true;
}
bool QDomBuilder::characters(const QString &characters, bool cdata)
{
// No text as child of some document
if (node == doc)
return false;
QScopedPointer<QDomNodePrivate> n;
if (cdata) {
n.reset(doc->createCDATASection(characters));
} else if (!entityName.isEmpty()) {
QScopedPointer<QDomEntityPrivate> e(
new QDomEntityPrivate(doc, nullptr, entityName, QString(), QString(), QString()));
e->value = characters;
e->ref.deref();
doc->doctype()->appendChild(e.data());
e.take();
n.reset(doc->createEntityReference(entityName));
} else {
n.reset(doc->createTextNode(characters));
}
n->setLocation(locator->line(), locator->column());
node->appendChild(n.data());
n.take();
return true;
}
bool QDomBuilder::processingInstruction(const QString &target, const QString &data)
{
QDomNodePrivate *n;
n = doc->createProcessingInstruction(target, data);
if (n) {
n->setLocation(locator->line(), locator->column());
node->appendChild(n);
return true;
} else
return false;
}
bool QDomBuilder::skippedEntity(const QString &name)
{
QDomNodePrivate *n = doc->createEntityReference(name);
n->setLocation(locator->line(), locator->column());
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);
}
bool QDomBuilder::startEntity(const QString &name)
{
entityName = name;
return true;
}
bool QDomBuilder::endEntity()
{
entityName.clear();
return true;
}
bool QDomBuilder::comment(const QString &characters)
{
QDomNodePrivate *n;
n = doc->createComment(characters);
n->setLocation(locator->line(), locator->column());
node->appendChild(n);
return true;
}
bool QDomBuilder::unparsedEntityDecl(const QString &name, const QString &publicId,
const QString &systemId, const QString &notationName)
{
QDomEntityPrivate *e =
new QDomEntityPrivate(doc, nullptr, name, publicId, systemId, notationName);
// keep the refcount balanced: appendChild() does a ref anyway.
e->ref.deref();
doc->doctype()->appendChild(e);
return true;
}
bool QDomBuilder::externalEntityDecl(const QString &name, const QString &publicId,
const QString &systemId)
{
return unparsedEntityDecl(name, publicId, systemId, QString());
}
bool QDomBuilder::notationDecl(const QString &name, const QString &publicId,
const QString &systemId)
{ {
QDomNotationPrivate *n = new QDomNotationPrivate(doc, nullptr, name, publicId, systemId); QDomNotationPrivate *n = new QDomNotationPrivate(doc, nullptr, name, publicId, systemId);
// keep the refcount balanced: appendChild() does a ref anyway. // keep the refcount balanced: appendChild() does a ref anyway.
@ -239,9 +368,4 @@ bool QDomHandler::notationDecl(const QString &name, const QString &publicId,
return true; return true;
} }
void QDomHandler::setDocumentLocator(QXmlLocator *locator)
{
this->locator = locator;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -58,6 +58,83 @@ QT_BEGIN_NAMESPACE
class QDomDocumentPrivate; class QDomDocumentPrivate;
class QDomNodePrivate; class QDomNodePrivate;
/**************************************************************
*
* QXmlDocumentLocators
*
**************************************************************/
/* TODO: QXmlDocumentLocator can be removed when the SAX-based
* implementation is removed. Right now it is needed for QDomBuilder
* to work with both QXmlStreamReader and QXmlInputSource (SAX)
* based implementations.
*/
class QXmlDocumentLocator
{
public:
virtual ~QXmlDocumentLocator() = default;
virtual int column() const = 0;
virtual int line() const = 0;
};
class QSAXDocumentLocator : public QXmlDocumentLocator
{
public:
~QSAXDocumentLocator() override = default;
int column() const override;
int line() const override;
void setLocator(QXmlLocator *l);
private:
QXmlLocator *locator = nullptr;
};
/**************************************************************
*
* QDomBuilder
*
**************************************************************/
class QDomBuilder
{
public:
QDomBuilder(QDomDocumentPrivate *d, QXmlDocumentLocator *l, bool namespaceProcessing);
~QDomBuilder();
bool endDocument();
bool startElement(const QString &nsURI, const QString &qName, const QXmlAttributes &atts);
bool endElement();
bool characters(const QString &characters, bool cdata = false);
bool processingInstruction(const QString &target, const QString &data);
bool skippedEntity(const QString &name);
bool startEntity(const QString &name);
bool endEntity();
bool startDTD(const QString &name, const QString &publicId, const QString &systemId);
bool comment(const QString &characters);
bool externalEntityDecl(const QString &name, const QString &publicId, const QString &systemId);
bool notationDecl(const QString &name, const QString &publicId, const QString &systemId);
bool unparsedEntityDecl(const QString &name, const QString &publicId, const QString &systemId,
const QString &notationName);
void fatalError(const QString &message);
using ErrorInfo = std::tuple<QString, int, int>;
ErrorInfo error() const;
QString errorMsg;
int errorLine;
int errorColumn;
private:
QDomDocumentPrivate *doc;
QDomNodePrivate *node;
QXmlDocumentLocator *locator;
QString entityName;
bool nsProcessing;
};
/************************************************************** /**************************************************************
* *
* QDomHandler * QDomHandler
@ -68,7 +145,7 @@ class QDomHandler : public QXmlDefaultHandler
{ {
public: public:
QDomHandler(QDomDocumentPrivate *d, QXmlSimpleReader *reader, bool namespaceProcessing); QDomHandler(QDomDocumentPrivate *d, QXmlSimpleReader *reader, bool namespaceProcessing);
~QDomHandler(); ~QDomHandler() override;
// content handler // content handler
bool endDocument() override; bool endDocument() override;
@ -102,18 +179,13 @@ public:
void setDocumentLocator(QXmlLocator *locator) override; void setDocumentLocator(QXmlLocator *locator) override;
QString errorMsg; QDomBuilder::ErrorInfo errorInfo() const;
int errorLine;
int errorColumn;
private: private:
QDomDocumentPrivate *doc;
QDomNodePrivate *node;
QString entityName;
bool cdata; bool cdata;
bool nsProcessing;
QXmlLocator *locator;
QXmlSimpleReader *reader; QXmlSimpleReader *reader;
QSAXDocumentLocator locator;
QDomBuilder domBuilder;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE