diff --git a/CMakeLists.txt b/CMakeLists.txt index b44e2b44e9..cea458138c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -356,7 +356,7 @@ ADD_CUSTOM_TARGET(mops2 DEPENDS ${PCH}) ## subdirs( mscore awl bww2mxml share midi audiofile fluid mstyle libmscore synthesizer - effects thirdparty/rtf2html thirdparty/diff + effects thirdparty/rtf2html thirdparty/diff xml ) add_subdirectory(mtest EXCLUDE_FROM_ALL) diff --git a/all.h b/all.h index 3e46fbb058..59415e04f4 100644 --- a/all.h +++ b/all.h @@ -39,7 +39,7 @@ #include #include #include -#include +// #include #ifdef Q_WS_X11 #include diff --git a/libmscore/xml.cpp b/libmscore/xml.cpp index 88ed26a783..6c98dc44b9 100644 --- a/libmscore/xml.cpp +++ b/libmscore/xml.cpp @@ -25,7 +25,7 @@ QString docName; //--------------------------------------------------------- XmlReader::XmlReader(QFile* d) - : QXmlStreamReader(d) + : XmlStreamReader(d) { docName = d->fileName(); _tick = 0; @@ -33,21 +33,21 @@ XmlReader::XmlReader(QFile* d) } XmlReader::XmlReader(const QByteArray& d) - : QXmlStreamReader(d) + : XmlStreamReader(d) { _tick = 0; _track = 0; } XmlReader::XmlReader(QIODevice* d) - : QXmlStreamReader(d) + : XmlStreamReader(d) { _tick = 0; _track = 0; } XmlReader::XmlReader(const QString& d) - : QXmlStreamReader(d) + : XmlStreamReader(d) { _tick = 0; _track = 0; @@ -114,14 +114,14 @@ bool XmlReader::hasAttribute(const char* s) const QPointF XmlReader::readPoint() { - Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); + Q_ASSERT(tokenType() == XmlStreamReader::StartElement); #ifndef NDEBUG if (!attributes().hasAttribute("x")) { - QXmlStreamAttributes map = attributes(); + XmlStreamAttributes map = attributes(); qDebug("XmlReader::readPoint: x attribute missing: %s (%d)", name().toUtf8().data(), map.size()); for (int i = 0; i < map.size(); ++i) { - const QXmlStreamAttribute& a = map.at(i); + const XmlStreamAttribute& a = map.at(i); qDebug(" attr <%s> <%s>", a.name().toUtf8().data(), a.value().toUtf8().data()); } unknown(); @@ -143,7 +143,7 @@ QPointF XmlReader::readPoint() QColor XmlReader::readColor() { - Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); + Q_ASSERT(tokenType() == XmlStreamReader::StartElement); QColor c; c.setRed(intAttribute("r")); c.setGreen(intAttribute("g")); @@ -159,7 +159,7 @@ QColor XmlReader::readColor() QSizeF XmlReader::readSize() { - Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); + Q_ASSERT(tokenType() == XmlStreamReader::StartElement); QSizeF p; p.setWidth(doubleAttribute("w", 0.0)); p.setHeight(doubleAttribute("h", 0.0)); @@ -173,7 +173,7 @@ QSizeF XmlReader::readSize() QRectF XmlReader::readRect() { - Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); + Q_ASSERT(tokenType() == XmlStreamReader::StartElement); QRectF p; p.setX(doubleAttribute("x", 0.0)); p.setY(doubleAttribute("y", 0.0)); @@ -189,7 +189,7 @@ QRectF XmlReader::readRect() Fraction XmlReader::readFraction() { - Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); + Q_ASSERT(tokenType() == XmlStreamReader::StartElement); int z = attribute("z", "0").toInt(); int n = attribute("n", "0").toInt(); skipCurrentElement(); @@ -203,7 +203,7 @@ Fraction XmlReader::readFraction() void XmlReader::unknown() const { - if (QXmlStreamReader::error()) + if (XmlStreamReader::error()) qDebug("StreamReaderError: %s", qPrintable(errorString())); qDebug("%s: xml read error at line %lld col %lld: %s", qPrintable(docName), lineNumber(), columnNumber(), @@ -696,31 +696,37 @@ void Xml::dump(int len, const unsigned char* p) void Xml::htmlToString(XmlReader& e, int level, QString* s) { *s += QString("<%1").arg(e.name().toString()); - QXmlStreamAttributes map = e.attributes(); + XmlStreamAttributes map = e.attributes(); int n = map.size(); for (int i = 0; i < n; ++i) { - const QXmlStreamAttribute& a = map.at(i); + const XmlStreamAttribute& a = map.at(i); *s += QString(" %1=\"%2\"").arg(a.name().toString()).arg(a.value().toString()); } *s += ">"; ++level; for (;;) { - QXmlStreamReader::TokenType t = e.readNext(); + XmlStreamReader::TokenType t = e.readNext(); switch(t) { - case QXmlStreamReader::StartElement: + case XmlStreamReader::StartElement: htmlToString(e, level, s); break; - case QXmlStreamReader::EndElement: + case XmlStreamReader::EndElement: *s += QString("").arg(e.name().toString()); --level; return; - case QXmlStreamReader::Characters: + case XmlStreamReader::Characters: if (!e.isWhitespace()) *s += e.text().toString(); break; - case QXmlStreamReader::Comment: + case XmlStreamReader::Comment: break; + + case XmlStreamReader::Invalid: + qDebug("htmlToString: invalid token: %s", qPrintable(e.errorString())); + return; + default: + qDebug("htmlToString: read token: %s", qPrintable(e.tokenString())); return; } } diff --git a/libmscore/xml.h b/libmscore/xml.h index 5d978035c1..f49208e766 100644 --- a/libmscore/xml.h +++ b/libmscore/xml.h @@ -17,6 +17,7 @@ #include "spatium.h" #include "fraction.h" #include "property.h" +#include "../xml/qxmlstream.h" namespace Ms { @@ -29,7 +30,7 @@ class ClefList; // XmlReader //--------------------------------------------------------- -class XmlReader : public QXmlStreamReader { +class XmlReader : public XmlStreamReader { QString docName; // used for error reporting // Score read context (for read optimizations): diff --git a/mscore/CMakeLists.txt b/mscore/CMakeLists.txt index b4725a883c..9b3155da9b 100644 --- a/mscore/CMakeLists.txt +++ b/mscore/CMakeLists.txt @@ -279,6 +279,7 @@ target_link_libraries(mscore diff_match_patch bww rtf2html + xmlreader ${QTSINGLEAPPLICATION_LIBRARIES} synthesizer midi diff --git a/xml/.gitignore b/xml/.gitignore new file mode 100644 index 0000000000..89f9ac04aa --- /dev/null +++ b/xml/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/xml/CMakeLists.txt b/xml/CMakeLists.txt new file mode 100644 index 0000000000..309cec86ce --- /dev/null +++ b/xml/CMakeLists.txt @@ -0,0 +1,38 @@ +#============================================================================= +# MuseScore +# Music Composition & Notation +# +# Copyright (C) 2013 Werner Schweer +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation and appearing in +# the file LICENSE.GPL +#============================================================================= + +include (${PROJECT_SOURCE_DIR}/build/gch.cmake) + +if (APPLE) + file(GLOB_RECURSE INCS "*.h") +else (APPLE) + set(INCS "") +endif (APPLE) + +add_library (xmlreader STATIC + ${PROJECT_BINARY_DIR}/all.h + ${PCH} + qxmlstream.cpp + qxmlutils.cpp + ) +set_target_properties ( + xmlreader + PROPERTIES + COMPILE_FLAGS "${PCH_INCLUDE} -g -Wall -Wextra -Winvalid-pch" + ) + +xcode_pch(xmlreader all) + +ADD_DEPENDENCIES(xmlreader mops1) +ADD_DEPENDENCIES(xmlreader mops2) + + diff --git a/xml/make-parser.sh b/xml/make-parser.sh new file mode 100755 index 0000000000..3c6a706396 --- /dev/null +++ b/xml/make-parser.sh @@ -0,0 +1,53 @@ +#!/bin/sh +############################################################################# +## +## Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +## Contact: http://www.qt-project.org/legal +## +## This file is the build configuration utility 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 Digia. For licensing terms and +## conditions see http://qt.digia.com/licensing. For further information +## use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 2.1 requirements +## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Digia gives you certain additional +## rights. These rights are described in the Digia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3.0 as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU General Public License version 3.0 requirements will be +## met: http://www.gnu.org/copyleft/gpl.html. +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +me=$(dirname $0) +mkdir -p $me/out +(cd $me/out && qlalr --qt --no-debug --no-lines ../qxmlstream.g) + +for f in $me/out/*.h; do + n=$(basename $f) + cp $f $n +done + +git diff . + diff --git a/xml/qxmlstream.cpp b/xml/qxmlstream.cpp new file mode 100644 index 0000000000..509b48a75e --- /dev/null +++ b/xml/qxmlstream.cpp @@ -0,0 +1,3980 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxmlstream.h" + +#if defined(QT_BUILD_XML_LIB) && defined(Q_OS_MAC64) +// No need to define this in the 64-bit Mac libraries. +// Since Qt 4.4 and previous weren't supported in 64-bit, there are +// no XmlStream* symbols to keep compatibility with +# define QT_NO_XMLSTREAM +#endif + +#ifndef QT_NO_XMLSTREAM + +#include "qxmlutils.h" +#include +#include +#include +#include +#include +#include +#ifndef QT_BOOTSTRAPPED +#include +#else +// This specialization of Q_DECLARE_TR_FUNCTIONS is not in qcoreapplication.h, +// because that header depends on QObject being available, which is not the +// case for most bootstrapped applications. +#define Q_DECLARE_TR_FUNCTIONS(context) \ +public: \ + static inline QString tr(const char *sourceText, const char *comment = 0) \ + { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ + static inline QString trUtf8(const char *sourceText, const char *comment = 0) \ + { Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \ + static inline QString tr(const char *sourceText, const char*, int) \ + { return QString::fromLatin1(sourceText); } \ + static inline QString trUtf8(const char *sourceText, const char*, int) \ + { return QString::fromLatin1(sourceText); } \ +private: +#endif +QT_BEGIN_NAMESPACE + +#include "qxmlstream_p.h" + +/*! + \enum XmlStreamReader::TokenType + + This enum specifies the type of token the reader just read. + + \value NoToken The reader has not yet read anything. + + \value Invalid An error has occurred, reported in error() and + errorString(). + + \value StartDocument The reader reports the XML version number in + documentVersion(), and the encoding as specified in the XML + document in documentEncoding(). If the document is declared + standalone, isStandaloneDocument() returns true; otherwise it + returns false. + + \value EndDocument The reader reports the end of the document. + + \value StartElement The reader reports the start of an element + with namespaceUri() and name(). Empty elements are also reported + as StartElement, followed directly by EndElement. The convenience + function readElementText() can be called to concatenate all + content until the corresponding EndElement. Attributes are + reported in attributes(), namespace declarations in + namespaceDeclarations(). + + \value EndElement The reader reports the end of an element with + namespaceUri() and name(). + + \value Characters The reader reports characters in text(). If the + characters are all white-space, isWhitespace() returns true. If + the characters stem from a CDATA section, isCDATA() returns true. + + \value Comment The reader reports a comment in text(). + + \value DTD The reader reports a DTD in text(), notation + declarations in notationDeclarations(), and entity declarations in + entityDeclarations(). Details of the DTD declaration are reported + in in dtdName(), dtdPublicId(), and dtdSystemId(). + + \value EntityReference The reader reports an entity reference that + could not be resolved. The name of the reference is reported in + name(), the replacement text in text(). + + \value ProcessingInstruction The reader reports a processing + instruction in processingInstructionTarget() and + processingInstructionData(). +*/ + +/*! + \enum XmlStreamReader::ReadElementTextBehaviour + + This enum specifies the different behaviours of readElementText(). + + \value ErrorOnUnexpectedElement Raise an UnexpectedElementError and return + what was read so far when a child element is encountered. + + \value IncludeChildElements Recursively include the text from child elements. + + \value SkipChildElements Skip child elements. + + \since 4.6 +*/ + +/*! + \enum XmlStreamReader::Error + + This enum specifies different error cases + + \value NoError No error has occurred. + + \value CustomError A custom error has been raised with + raiseError() + + \value NotWellFormedError The parser internally raised an error + due to the read XML not being well-formed. + + \value PrematureEndOfDocumentError The input stream ended before a + well-formed XML document was parsed. Recovery from this error is + possible if more XML arrives in the stream, either by calling + addData() or by waiting for it to arrive on the device(). + + \value UnexpectedElementError The parser encountered an element + that was different to those it expected. + +*/ + +/*! + \class XmlStreamEntityResolver + \reentrant + \since 4.4 + + \brief The XmlStreamEntityResolver class provides an entity + resolver for a XmlStreamReader. + + \ingroup xml-tools + */ + +/*! + Destroys the entity resolver. + */ +XmlStreamEntityResolver::~XmlStreamEntityResolver() +{ +} + +/*! \internal + +This function is a stub for later functionality. +*/ +QString XmlStreamEntityResolver::resolveEntity(const QString& /*publicId*/, const QString& /*systemId*/) +{ + return QString(); +} + + +/*! + Resolves the undeclared entity \a name and returns its replacement + text. If the entity is also unknown to the entity resolver, it + returns an empty string. + + The default implementation always returns an empty string. +*/ + +QString XmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/) +{ + return QString(); +} + +#ifndef QT_NO_XMLSTREAMREADER + +QString XmlStreamReaderPrivate::resolveUndeclaredEntity(const QString &name) +{ + if (entityResolver) + return entityResolver->resolveUndeclaredEntity(name); + return QString(); +} + + + +/*! + \since 4.4 + + Makes \a resolver the new entityResolver(). + + The stream reader does \e not take ownership of the resolver. It's + the callers responsibility to ensure that the resolver is valid + during the entire life-time of the stream reader object, or until + another resolver or 0 is set. + + \sa entityResolver() + */ +void XmlStreamReader::setEntityResolver(XmlStreamEntityResolver *resolver) +{ + Q_D(XmlStreamReader); + d->entityResolver = resolver; +} + +/*! + \since 4.4 + + Returns the entity resolver, or 0 if there is no entity resolver. + + \sa setEntityResolver() + */ +XmlStreamEntityResolver *XmlStreamReader::entityResolver() const +{ + Q_D(const XmlStreamReader); + return d->entityResolver; +} + + + +/*! + \class XmlStreamReader + \reentrant + \since 4.3 + + \brief The XmlStreamReader class provides a fast parser for reading + well-formed XML via a simple streaming API. + + + \ingroup xml-tools + + XmlStreamReader is a faster and more convenient replacement for + Qt's own SAX parser (see QXmlSimpleReader). In some cases it might + also be a faster and more convenient alternative for use in + applications that would otherwise use a DOM tree (see QDomDocument). + XmlStreamReader reads data either from a QIODevice (see + setDevice()), or from a raw QByteArray (see addData()). + + Qt provides XmlStreamWriter for writing XML. + + The basic concept of a stream reader is to report an XML document as + a stream of tokens, similar to SAX. The main difference between + XmlStreamReader and SAX is \e how these XML tokens are reported. + With SAX, the application must provide handlers (callback functions) + that receive so-called XML \e events from the parser at the parser's + convenience. With XmlStreamReader, the application code itself + drives the loop and pulls \e tokens from the reader, one after + another, as it needs them. This is done by calling readNext(), where + the reader reads from the input stream until it completes the next + token, at which point it returns the tokenType(). A set of + convenient functions including isStartElement() and text() can then + be used to examine the token to obtain information about what has + been read. The big advantage of this \e pulling approach is the + possibility to build recursive descent parsers with it, meaning you + can split your XML parsing code easily into different methods or + classes. This makes it easy to keep track of the application's own + state when parsing XML. + + A typical loop with XmlStreamReader looks like this: + + \snippet doc/src/snippets/code/src_corelib_xml_qxmlstream.cpp 0 + + + XmlStreamReader is a well-formed XML 1.0 parser that does \e not + include external parsed entities. As long as no error occurs, the + application code can thus be assured that the data provided by the + stream reader satisfies the W3C's criteria for well-formed XML. For + example, you can be certain that all tags are indeed nested and + closed properly, that references to internal entities have been + replaced with the correct replacement text, and that attributes have + been normalized or added according to the internal subset of the + DTD. + + If an error occurs while parsing, atEnd() and hasError() return + true, and error() returns the error that occurred. The functions + errorString(), lineNumber(), columnNumber(), and characterOffset() + are for constructing an appropriate error or warning message. To + simplify application code, XmlStreamReader contains a raiseError() + mechanism that lets you raise custom errors that trigger the same + error handling described. + + The \l{XmlStream Bookmarks Example} illustrates how to use the + recursive descent technique to read an XML bookmark file (XBEL) with + a stream reader. + + \section1 Namespaces + + XmlStream understands and resolves XML namespaces. E.g. in case of + a StartElement, namespaceUri() returns the namespace the element is + in, and name() returns the element's \e local name. The combination + of namespaceUri and name uniquely identifies an element. If a + namespace prefix was not declared in the XML entities parsed by the + reader, the namespaceUri is empty. + + If you parse XML data that does not utilize namespaces according to + the XML specification or doesn't use namespaces at all, you can use + the element's qualifiedName() instead. A qualified name is the + element's prefix() followed by colon followed by the element's local + name() - exactly like the element appears in the raw XML data. Since + the mapping namespaceUri to prefix is neither unique nor universal, + qualifiedName() should be avoided for namespace-compliant XML data. + + In order to parse standalone documents that do use undeclared + namespace prefixes, you can turn off namespace processing completely + with the \l namespaceProcessing property. + + \section1 Incremental parsing + + XmlStreamReader is an incremental parser. It can handle the case + where the document can't be parsed all at once because it arrives in + chunks (e.g. from multiple files, or over a network connection). + When the reader runs out of data before the complete document has + been parsed, it reports a PrematureEndOfDocumentError. When more + data arrives, either because of a call to addData() or because more + data is available through the network device(), the reader recovers + from the PrematureEndOfDocumentError error and continues parsing the + new data with the next call to readNext(). + + For example, if your application reads data from the network using a + \l{QNetworkAccessManager} {network access manager}, you would issue + a \l{QNetworkRequest} {network request} to the manager and receive a + \l{QNetworkReply} {network reply} in return. Since a QNetworkReply + is a QIODevice, you connect its \l{QNetworkReply::readyRead()} + {readyRead()} signal to a custom slot, e.g. \c{slotReadyRead()} in + the code snippet shown in the discussion for QNetworkAccessManager. + In this slot, you read all available data with + \l{QNetworkReply::readAll()} {readAll()} and pass it to the XML + stream reader using addData(). Then you call your custom parsing + function that reads the XML events from the reader. + + \section1 Performance and memory consumption + + XmlStreamReader is memory-conservative by design, since it doesn't + store the entire XML document tree in memory, but only the current + token at the time it is reported. In addition, XmlStreamReader + avoids the many small string allocations that it normally takes to + map an XML document to a convenient and Qt-ish API. It does this by + reporting all string data as QStringRef rather than real QString + objects. QStringRef is a thin wrapper around QString substrings that + provides a subset of the QString API without the memory allocation + and reference-counting overhead. Calling + \l{QStringRef::toString()}{toString()} on any of those objects + returns an equivalent real QString object. + +*/ + + +/*! + Constructs a stream reader. + + \sa setDevice(), addData() + */ +XmlStreamReader::XmlStreamReader() + : d_ptr(new XmlStreamReaderPrivate(this)) +{ +} + +/*! Creates a new stream reader that reads from \a device. + +\sa setDevice(), clear() + */ +XmlStreamReader::XmlStreamReader(QIODevice *device) + : d_ptr(new XmlStreamReaderPrivate(this)) +{ + setDevice(device); +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +XmlStreamReader::XmlStreamReader(const QByteArray &data) + : d_ptr(new XmlStreamReaderPrivate(this)) +{ + Q_D(XmlStreamReader); + d->dataBuffer = data; +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +XmlStreamReader::XmlStreamReader(const QString &data) + : d_ptr(new XmlStreamReaderPrivate(this)) +{ + Q_D(XmlStreamReader); +#ifdef QT_NO_TEXTCODEC + d->dataBuffer = data.toLatin1(); +#else + d->dataBuffer = d->codec->fromUnicode(data); + d->decoder = d->codec->makeDecoder(); +#endif + d->lockEncoding = true; + +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +XmlStreamReader::XmlStreamReader(const char *data) + : d_ptr(new XmlStreamReaderPrivate(this)) +{ + Q_D(XmlStreamReader); + d->dataBuffer = QByteArray(data); +} + +/*! + Destructs the reader. + */ +XmlStreamReader::~XmlStreamReader() +{ + Q_D(XmlStreamReader); + if (d->deleteDevice) + delete d->device; +} + +/*! \fn bool XmlStreamReader::hasError() const + Returns \c true if an error has occurred, otherwise \c false. + + \sa errorString(), error() + */ + +/*! + Sets the current device to \a device. Setting the device resets + the stream to its initial state. + + \sa device(), clear() +*/ +void XmlStreamReader::setDevice(QIODevice *device) +{ + Q_D(XmlStreamReader); + if (d->deleteDevice) { + delete d->device; + d->deleteDevice = false; + } + d->device = device; + d->init(); + +} + +/*! + Returns the current device associated with the XmlStreamReader, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *XmlStreamReader::device() const +{ + Q_D(const XmlStreamReader); + return d->device; +} + + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void XmlStreamReader::addData(const QByteArray &data) +{ + Q_D(XmlStreamReader); + if (d->device) { + qWarning("XmlStreamReader: addData() with device()"); + return; + } + d->dataBuffer += data; +} + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void XmlStreamReader::addData(const QString &data) +{ + Q_D(XmlStreamReader); + d->lockEncoding = true; +#ifdef QT_NO_TEXTCODEC + addData(data.toLatin1()); +#else + addData(d->codec->fromUnicode(data)); +#endif +} + +/*! + Adds more \a data for the reader to read. This function does + nothing if the reader has a device(). + + \sa readNext(), clear() + */ +void XmlStreamReader::addData(const char *data) +{ + addData(QByteArray(data)); +} + +/*! + Removes any device() or data from the reader and resets its + internal state to the initial state. + + \sa addData() + */ +void XmlStreamReader::clear() +{ + Q_D(XmlStreamReader); + d->init(); + if (d->device) { + if (d->deleteDevice) + delete d->device; + d->device = 0; + } +} + +/*! + Returns true if the reader has read until the end of the XML + document, or if an error() has occurred and reading has been + aborted. Otherwise, it returns false. + + When atEnd() and hasError() return true and error() returns + PrematureEndOfDocumentError, it means the XML has been well-formed + so far, but a complete XML document has not been parsed. The next + chunk of XML can be added with addData(), if the XML is being read + from a QByteArray, or by waiting for more data to arrive if the + XML is being read from a QIODevice. Either way, atEnd() will + return false once more data is available. + + \sa hasError(), error(), device(), QIODevice::atEnd() + */ +bool XmlStreamReader::atEnd() const +{ + Q_D(const XmlStreamReader); + if (d->atEnd + && ((d->type == XmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError) + || (d->type == XmlStreamReader::EndDocument))) { + if (d->device) + return d->device->atEnd(); + else + return !d->dataBuffer.size(); + } + return (d->atEnd || d->type == XmlStreamReader::Invalid); +} + + +/*! + Reads the next token and returns its type. + + With one exception, once an error() is reported by readNext(), + further reading of the XML stream is not possible. Then atEnd() + returns true, hasError() returns true, and this function returns + XmlStreamReader::Invalid. + + The exception is when error() returns PrematureEndOfDocumentError. + This error is reported when the end of an otherwise well-formed + chunk of XML is reached, but the chunk doesn't represent a complete + XML document. In that case, parsing \e can be resumed by calling + addData() to add the next chunk of XML, when the stream is being + read from a QByteArray, or by waiting for more data to arrive when + the stream is being read from a device(). + + \sa tokenType(), tokenString() + */ +XmlStreamReader::TokenType XmlStreamReader::readNext() +{ + Q_D(XmlStreamReader); + if (d->type != Invalid) { + if (!d->hasCheckedStartDocument) + if (!d->checkStartDocument()) + return d->type; // synthetic StartDocument or error + d->parse(); + if (d->atEnd && d->type != EndDocument && d->type != Invalid) + d->raiseError(PrematureEndOfDocumentError); + else if (!d->atEnd && d->type == EndDocument) + d->raiseWellFormedError(XmlStream::tr("Extra content at end of document.")); + } else if (d->error == PrematureEndOfDocumentError) { + // resume error + d->type = NoToken; + d->atEnd = false; + d->token = -1; + return readNext(); + } + return d->type; +} + + +/*! + Returns the type of the current token. + + The current token can also be queried with the convenience functions + isStartDocument(), isEndDocument(), isStartElement(), + isEndElement(), isCharacters(), isComment(), isDTD(), + isEntityReference(), and isProcessingInstruction(). + + \sa tokenString() + */ +XmlStreamReader::TokenType XmlStreamReader::tokenType() const +{ + Q_D(const XmlStreamReader); + return d->type; +} + +/*! + Reads until the next start element within the current element. Returns true + when a start element was reached. When the end element was reached, or when + an error occurred, false is returned. + + The current element is the element matching the most recently parsed start + element of which a matching end element has not yet been reached. When the + parser has reached the end element, the current element becomes the parent + element. + + You can traverse a document by repeatedly calling this function while + ensuring that the stream reader is not at the end of the document: + + \snippet doc/src/snippets/xml/streamreader/traverse.cpp traverse document + + This is a convenience function for when you're only concerned with parsing + XML elements. The \l{XmlStream Bookmarks Example} makes extensive use of + this function. + + \since 4.6 + \sa readNext() + */ +bool XmlStreamReader::readNextStartElement() +{ + while (readNext() != Invalid) { + if (isEndElement()) + return false; + else if (isStartElement()) + return true; + } + return false; +} + +/*! + Reads until the end of the current element, skipping any child nodes. + This function is useful for skipping unknown elements. + + The current element is the element matching the most recently parsed start + element of which a matching end element has not yet been reached. When the + parser has reached the end element, the current element becomes the parent + element. + + \since 4.6 + */ +void XmlStreamReader::skipCurrentElement() +{ + int depth = 1; + while (depth && readNext() != Invalid) { + if (isEndElement()) + --depth; + else if (isStartElement()) + ++depth; + } +} + +/* + * Use the following Perl script to generate the error string index list: +===== PERL SCRIPT ==== +print "static const char XmlStreamReader_tokenTypeString_string[] =\n"; +$counter = 0; +$i = 0; +while () { + chomp; + print " \"$_\\0\"\n"; + $sizes[$i++] = $counter; + $counter += length 1 + $_; +} +print " \"\\0\";\n\nstatic const short XmlStreamReader_tokenTypeString_indices[] = {\n "; +for ($j = 0; $j < $i; ++$j) { + printf "$sizes[$j], "; +} +print "0\n};\n"; +===== PERL SCRIPT ==== + + * The input data is as follows (copied from qxmlstream.h): +NoToken +Invalid +StartDocument +EndDocument +StartElement +EndElement +Characters +Comment +DTD +EntityReference +ProcessingInstruction +*/ +static const char XmlStreamReader_tokenTypeString_string[] = + "NoToken\0" + "Invalid\0" + "StartDocument\0" + "EndDocument\0" + "StartElement\0" + "EndElement\0" + "Characters\0" + "Comment\0" + "DTD\0" + "EntityReference\0" + "ProcessingInstruction\0"; + +static const short XmlStreamReader_tokenTypeString_indices[] = { + 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 +}; + + +/*! + \property XmlStreamReader::namespaceProcessing + the namespace-processing flag of the stream reader + + This property controls whether or not the stream reader processes + namespaces. If enabled, the reader processes namespaces, otherwise + it does not. + + By default, namespace-processing is enabled. +*/ + + +void XmlStreamReader::setNamespaceProcessing(bool enable) +{ + Q_D(XmlStreamReader); + d->namespaceProcessing = enable; +} + +bool XmlStreamReader::namespaceProcessing() const +{ + Q_D(const XmlStreamReader); + return d->namespaceProcessing; +} + +/*! Returns the reader's current token as string. + +\sa tokenType() +*/ +QString XmlStreamReader::tokenString() const +{ + Q_D(const XmlStreamReader); + return QLatin1String(XmlStreamReader_tokenTypeString_string + + XmlStreamReader_tokenTypeString_indices[d->type]); +} + +#endif // QT_NO_XMLSTREAMREADER + +XmlStreamPrivateTagStack::XmlStreamPrivateTagStack() +{ + tagStack.reserve(16); + tagStackStringStorage.reserve(32); + tagStackStringStorageSize = 0; + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix = addToStringStorage(QLatin1String("xml")); + namespaceDeclaration.namespaceUri = addToStringStorage(QLatin1String("http://www.w3.org/XML/1998/namespace")); +} + +#ifndef QT_NO_XMLSTREAMREADER + +XmlStreamReaderPrivate::XmlStreamReaderPrivate(XmlStreamReader *q) + :q_ptr(q) +{ + device = 0; + deleteDevice = false; +#ifndef QT_NO_TEXTCODEC + decoder = 0; +#endif + stack_size = 64; + sym_stack = 0; + state_stack = 0; + reallocateStack(); + entityResolver = 0; + init(); + entityHash.insert(QLatin1String("lt"), Entity::createLiteral(QLatin1String("<"))); + entityHash.insert(QLatin1String("gt"), Entity::createLiteral(QLatin1String(">"))); + entityHash.insert(QLatin1String("amp"), Entity::createLiteral(QLatin1String("&"))); + entityHash.insert(QLatin1String("apos"), Entity::createLiteral(QLatin1String("'"))); + entityHash.insert(QLatin1String("quot"), Entity::createLiteral(QLatin1String("\""))); +} + +void XmlStreamReaderPrivate::init() +{ + tos = 0; + scanDtd = false; + token = -1; + token_char = 0; + isEmptyElement = false; + isWhitespace = true; + isCDATA = false; + standalone = false; + tos = 0; + resumeReduction = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + putStack.clear(); + putStack.reserve(32); + textBuffer.clear(); + textBuffer.reserve(256); + tagStack.clear(); + tagsDone = false; + attributes.clear(); + attributes.reserve(16); + lineNumber = lastLineStart = characterOffset = 0; + readBufferPos = 0; + nbytesread = 0; +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForMib(106); // utf8 + delete decoder; + decoder = 0; +#endif + attributeStack.clear(); + attributeStack.reserve(16); + entityParser = 0; + hasCheckedStartDocument = false; + normalizeLiterals = false; + hasSeenTag = false; + atEnd = false; + inParseEntity = false; + referenceToUnparsedEntityDetected = false; + referenceToParameterEntityDetected = false; + hasExternalDtdSubset = false; + lockEncoding = false; + namespaceProcessing = true; + rawReadBuffer.clear(); + dataBuffer.clear(); + readBuffer.clear(); + + type = XmlStreamReader::NoToken; + error = XmlStreamReader::NoError; +} + +/* + Well-formed requires that we verify entity values. We do this with a + standard parser. + */ +void XmlStreamReaderPrivate::parseEntity(const QString &value) +{ + Q_Q(XmlStreamReader); + + if (value.isEmpty()) + return; + + + if (!entityParser) + entityParser = new XmlStreamReaderPrivate(q); + else + entityParser->init(); + entityParser->inParseEntity = true; + entityParser->readBuffer = value; + entityParser->injectToken(PARSE_ENTITY); + while (!entityParser->atEnd && entityParser->type != XmlStreamReader::Invalid) + entityParser->parse(); + if (entityParser->type == XmlStreamReader::Invalid || entityParser->tagStack.size()) + raiseWellFormedError(XmlStream::tr("Invalid entity value.")); + +} + +inline void XmlStreamReaderPrivate::reallocateStack() +{ + stack_size <<= 1; + sym_stack = reinterpret_cast (qRealloc(sym_stack, stack_size * sizeof(Value))); + Q_CHECK_PTR(sym_stack); + state_stack = reinterpret_cast (qRealloc(state_stack, stack_size * sizeof(int))); + Q_CHECK_PTR(sym_stack); +} + + +XmlStreamReaderPrivate::~XmlStreamReaderPrivate() +{ +#ifndef QT_NO_TEXTCODEC + delete decoder; +#endif + qFree(sym_stack); + qFree(state_stack); + delete entityParser; +} + + +inline uint XmlStreamReaderPrivate::filterCarriageReturn() +{ + uint peekc = peekChar(); + if (peekc == '\n') { + if (putStack.size()) + putStack.pop(); + else + ++readBufferPos; + return peekc; + } + if (peekc == 0) { + putChar('\r'); + return 0; + } + return '\n'; +} + +/*! + \internal + If the end of the file is encountered, 0 is returned. + */ +inline uint XmlStreamReaderPrivate::getChar() +{ + uint c; + if (putStack.size()) { + c = atEnd ? 0 : putStack.pop(); + } else { + if (readBufferPos < readBuffer.size()) + c = readBuffer.at(readBufferPos++).unicode(); + else + c = getChar_helper(); + } + + return c; +} + +inline uint XmlStreamReaderPrivate::peekChar() +{ + uint c; + if (putStack.size()) { + c = putStack.top(); + } else if (readBufferPos < readBuffer.size()) { + c = readBuffer.at(readBufferPos).unicode(); + } else { + if ((c = getChar_helper())) + --readBufferPos; + } + + return c; +} + +/*! + \internal + + Scans characters until \a str is encountered, and validates the characters + as according to the Char[2] production and do the line-ending normalization. + If any character is invalid, false is returned, otherwise true upon success. + + If \a tokenToInject is not less than zero, injectToken() is called with + \a tokenToInject when \a str is found. + + If any error occurred, false is returned, otherwise true. + */ +bool XmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject) +{ + int pos = textBuffer.size(); + int oldLineNumber = lineNumber; + + while (uint c = getChar()) { + /* First, we do the validation & normalization. */ + switch (c) { + case '\r': + if ((c = filterCarriageReturn()) == 0) + break; + // fall through + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + // fall through + case '\t': + textBuffer += QChar(c); + continue; + default: + if(c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > 0x10FFFF ) { + raiseWellFormedError(XmlStream::tr("Invalid XML character.")); + lineNumber = oldLineNumber; + return false; + } + textBuffer += QChar(c); + } + + + /* Second, attempt to lookup str. */ + if (c == uint(*str)) { + if (!*(str + 1)) { + if (tokenToInject >= 0) + injectToken(tokenToInject); + return true; + } else { + if (scanString(str + 1, tokenToInject, false)) + return true; + } + } + } + putString(textBuffer, pos); + textBuffer.resize(pos); + lineNumber = oldLineNumber; + return false; +} + +bool XmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace) +{ + int n = 0; + while (str[n]) { + ushort c = getChar(); + if (c != ushort(str[n])) { + if (c) + putChar(c); + while (n--) { + putChar(ushort(str[n])); + } + return false; + } + ++n; + } + for (int i = 0; i < n; ++i) + textBuffer += QChar(ushort(str[i])); + if (requireSpace) { + int s = fastScanSpace(); + if (!s || atEnd) { + int pos = textBuffer.size() - n - s; + putString(textBuffer, pos); + textBuffer.resize(pos); + return false; + } + } + if (tokenToInject >= 0) + injectToken(tokenToInject); + return true; +} + +bool XmlStreamReaderPrivate::scanAfterLangleBang() +{ + switch (peekChar()) { + case '[': + return scanString(spell[CDATA_START], CDATA_START, false); + case 'D': + return scanString(spell[DOCTYPE], DOCTYPE); + case 'A': + return scanString(spell[ATTLIST], ATTLIST); + case 'N': + return scanString(spell[NOTATION], NOTATION); + case 'E': + if (scanString(spell[ELEMENT], ELEMENT)) + return true; + return scanString(spell[ENTITY], ENTITY); + + default: + ; + }; + return false; +} + +bool XmlStreamReaderPrivate::scanPublicOrSystem() +{ + switch (peekChar()) { + case 'S': + return scanString(spell[SYSTEM], SYSTEM); + case 'P': + return scanString(spell[PUBLIC], PUBLIC); + default: + ; + } + return false; +} + +bool XmlStreamReaderPrivate::scanNData() +{ + if (fastScanSpace()) { + if (scanString(spell[NDATA], NDATA)) + return true; + putChar(' '); + } + return false; +} + +bool XmlStreamReaderPrivate::scanAfterDefaultDecl() +{ + switch (peekChar()) { + case 'R': + return scanString(spell[REQUIRED], REQUIRED, false); + case 'I': + return scanString(spell[IMPLIED], IMPLIED, false); + case 'F': + return scanString(spell[FIXED], FIXED, false); + default: + ; + } + return false; +} + +bool XmlStreamReaderPrivate::scanAttType() +{ + switch (peekChar()) { + case 'C': + return scanString(spell[CDATA], CDATA); + case 'I': + if (scanString(spell[ID], ID)) + return true; + if (scanString(spell[IDREF], IDREF)) + return true; + return scanString(spell[IDREFS], IDREFS); + case 'E': + if (scanString(spell[ENTITY], ENTITY)) + return true; + return scanString(spell[ENTITIES], ENTITIES); + case 'N': + if (scanString(spell[NOTATION], NOTATION)) + return true; + if (scanString(spell[NMTOKEN], NMTOKEN)) + return true; + return scanString(spell[NMTOKENS], NMTOKENS); + default: + ; + } + return false; +} + +/*! + \internal + + Scan strings with quotes or apostrophes surround them. For instance, + attributes, the version and encoding field in the XML prolog and + entity declarations. + + If normalizeLiterals is set to true, the function also normalizes + whitespace. It is set to true when the first start tag is + encountered. + + */ +inline int XmlStreamReaderPrivate::fastScanLiteralContent() +{ + int n = 0; + uint c; + while ((c = getChar())) { + switch (ushort(c)) { + case 0xfffe: + case 0xffff: + case 0: + /* The putChar() call is necessary so the parser re-gets + * the character from the input source, when raising an error. */ + putChar(c); + return n; + case '\r': + if (filterCarriageReturn() == 0) + return n; + // fall through + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + // fall through + case ' ': + case '\t': + if (normalizeLiterals) + textBuffer += QLatin1Char(' '); + else + textBuffer += QChar(c); + ++n; + break; + case '&': + case '<': + case '\"': + case '\'': + if (!(c & 0xff0000)) { + putChar(c); + return n; + } + // fall through + default: + textBuffer += QChar(c); + ++n; + } + } + return n; +} + +inline int XmlStreamReaderPrivate::fastScanSpace() +{ + int n = 0; + ushort c; + while ((c = getChar())) { + switch (c) { + case '\r': + if ((c = filterCarriageReturn()) == 0) + return n; + // fall through + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + // fall through + case ' ': + case '\t': + textBuffer += QChar(c); + ++n; + break; + default: + putChar(c); + return n; + } + } + return n; +} + +/*! + \internal + + Used for text nodes essentially. That is, characters appearing + inside elements. + */ +inline int XmlStreamReaderPrivate::fastScanContentCharList() +{ + int n = 0; + uint c; + while ((c = getChar())) { + switch (ushort(c)) { + case 0xfffe: + case 0xffff: + case 0: + putChar(c); + return n; + case ']': { + isWhitespace = false; + int pos = textBuffer.size(); + textBuffer += QChar(ushort(c)); + ++n; + while ((c = getChar()) == ']') { + textBuffer += QChar(ushort(c)); + ++n; + } + if (c == 0) { + putString(textBuffer, pos); + textBuffer.resize(pos); + } else if (c == '>' && textBuffer.at(textBuffer.size()-2) == QLatin1Char(']')) { + raiseWellFormedError(XmlStream::tr("Sequence ']]>' not allowed in content.")); + } else { + putChar(c); + break; + } + return n; + } break; + case '\r': + if ((c = filterCarriageReturn()) == 0) + return n; + // fall through + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + // fall through + case ' ': + case '\t': + textBuffer += QChar(ushort(c)); + ++n; + break; + case '&': + case '<': + if (!(c & 0xff0000)) { + putChar(c); + return n; + } + // fall through + default: + if (c < 0x20) { + putChar(c); + return n; + } + isWhitespace = false; + textBuffer += QChar(ushort(c)); + ++n; + } + } + return n; +} + +inline int XmlStreamReaderPrivate::fastScanName(int *prefix) +{ + int n = 0; + ushort c; + while ((c = getChar())) { + switch (c) { + case '\n': + case ' ': + case '\t': + case '\r': + case '&': + case '#': + case '\'': + case '\"': + case '<': + case '>': + case '[': + case ']': + case '=': + case '%': + case '/': + case ';': + case '?': + case '!': + case '^': + case '|': + case ',': + case '(': + case ')': + case '+': + case '*': + putChar(c); + if (prefix && *prefix == n+1) { + *prefix = 0; + putChar(':'); + --n; + } + return n; + case ':': + if (prefix) { + if (*prefix == 0) { + *prefix = n+2; + } else { // only one colon allowed according to the namespace spec. + putChar(c); + return n; + } + } else { + putChar(c); + return n; + } + // fall through + default: + textBuffer += QChar(c); + ++n; + } + } + + if (prefix) + *prefix = 0; + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); + return 0; +} + +enum NameChar { NameBeginning, NameNotBeginning, NotName }; + +static const char Begi = static_cast(NameBeginning); +static const char NtBg = static_cast(NameNotBeginning); +static const char NotN = static_cast(NotName); + +static const char nameCharTable[128] = +{ +// 0x00 + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, +// 0x10 + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, +// 0x20 (0x2D is '-', 0x2E is '.') + NotN, NotN, NotN, NotN, NotN, NotN, NotN, NotN, + NotN, NotN, NotN, NotN, NotN, NtBg, NtBg, NotN, +// 0x30 (0x30..0x39 are '0'..'9', 0x3A is ':') + NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, NtBg, + NtBg, NtBg, Begi, NotN, NotN, NotN, NotN, NotN, +// 0x40 (0x41..0x5A are 'A'..'Z') + NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, +// 0x50 (0x5F is '_') + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, NotN, NotN, NotN, NotN, Begi, +// 0x60 (0x61..0x7A are 'a'..'z') + NotN, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, +// 0x70 + Begi, Begi, Begi, Begi, Begi, Begi, Begi, Begi, + Begi, Begi, Begi, NotN, NotN, NotN, NotN, NotN +}; + +static inline NameChar fastDetermineNameChar(QChar ch) +{ + ushort uc = ch.unicode(); + if (!(uc & ~0x7f)) // uc < 128 + return static_cast(nameCharTable[uc]); + + QChar::Category cat = ch.category(); + // ### some these categories might be slightly wrong + if ((cat >= QChar::Letter_Uppercase && cat <= QChar::Letter_Other) + || cat == QChar::Number_Letter) + return NameBeginning; + if ((cat >= QChar::Number_DecimalDigit && cat <= QChar::Number_Other) + || (cat >= QChar::Mark_NonSpacing && cat <= QChar::Mark_Enclosing)) + return NameNotBeginning; + return NotName; +} + +inline int XmlStreamReaderPrivate::fastScanNMTOKEN() +{ + int n = 0; + uint c; + while ((c = getChar())) { + if (fastDetermineNameChar(c) == NotName) { + putChar(c); + return n; + } else { + ++n; + textBuffer += QChar(c); + } + } + + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); + + return n; +} + +void XmlStreamReaderPrivate::putString(const QString &s, int from) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= from; --i) + putStack.rawPush() = s.at(i).unicode(); +} + +void XmlStreamReaderPrivate::putStringLiteral(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) + putStack.rawPush() = ((LETTER << 16) | s.at(i).unicode()); +} + +void XmlStreamReaderPrivate::putReplacement(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) { + ushort c = s.at(i).unicode(); + if (c == '\n' || c == '\r') + putStack.rawPush() = ((LETTER << 16) | c); + else + putStack.rawPush() = c; + } +} +void XmlStreamReaderPrivate::putReplacementInAttributeValue(const QString &s) +{ + putStack.reserve(s.size()); + for (int i = s.size()-1; i >= 0; --i) { + ushort c = s.at(i).unicode(); + if (c == '&' || c == ';') + putStack.rawPush() = c; + else if (c == '\n' || c == '\r') + putStack.rawPush() = ' '; + else + putStack.rawPush() = ((LETTER << 16) | c); + } +} + +ushort XmlStreamReaderPrivate::getChar_helper() +{ + const int BUFFER_SIZE = 8192; + characterOffset += readBufferPos; + readBufferPos = 0; + readBuffer.resize(0); +#ifndef QT_NO_TEXTCODEC + if (decoder) +#endif + nbytesread = 0; + if (device) { + rawReadBuffer.resize(BUFFER_SIZE); + int nbytesreadOrMinus1 = device->read(rawReadBuffer.data() + nbytesread, BUFFER_SIZE - nbytesread); + nbytesread += qMax(nbytesreadOrMinus1, 0); + } else { + if (nbytesread) + rawReadBuffer += dataBuffer; + else + rawReadBuffer = dataBuffer; + nbytesread = rawReadBuffer.size(); + dataBuffer.clear(); + } + if (!nbytesread) { + atEnd = true; + return 0; + } + +#ifndef QT_NO_TEXTCODEC + if (!decoder) { + if (nbytesread < 4) { // the 4 is to cover 0xef 0xbb 0xbf plus + // one extra for the utf8 codec + atEnd = true; + return 0; + } + int mib = 106; // UTF-8 + + // look for byte order mark + uchar ch1 = rawReadBuffer.at(0); + uchar ch2 = rawReadBuffer.at(1); + uchar ch3 = rawReadBuffer.at(2); + uchar ch4 = rawReadBuffer.at(3); + + if ((ch1 == 0 && ch2 == 0 && ch3 == 0xfe && ch4 == 0xff) || + (ch1 == 0xff && ch2 == 0xfe && ch3 == 0 && ch4 == 0)) + mib = 1017; // UTF-32 with byte order mark + else if (ch1 == 0x3c && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x00) + mib = 1019; // UTF-32LE + else if (ch1 == 0x00 && ch2 == 0x00 && ch3 == 0x00 && ch4 == 0x3c) + mib = 1018; // UTF-32BE + else if ((ch1 == 0xfe && ch2 == 0xff) || (ch1 == 0xff && ch2 == 0xfe)) + mib = 1015; // UTF-16 with byte order mark + else if (ch1 == 0x3c && ch2 == 0x00) + mib = 1014; // UTF-16LE + else if (ch1 == 0x00 && ch2 == 0x3c) + mib = 1013; // UTF-16BE + codec = QTextCodec::codecForMib(mib); + Q_ASSERT(codec); + decoder = codec->makeDecoder(); + } + + decoder->toUnicode(&readBuffer, rawReadBuffer.constData(), nbytesread); + + if(lockEncoding && decoder->hasFailure()) { + raiseWellFormedError(XmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return 0; + } +#else + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#endif // QT_NO_TEXTCODEC + + readBuffer.reserve(1); // keep capacity when calling resize() next time + + if (readBufferPos < readBuffer.size()) { + ushort c = readBuffer.at(readBufferPos++).unicode(); + return c; + } + + atEnd = true; + return 0; +} + +QStringRef XmlStreamReaderPrivate::namespaceForPrefix(const QStringRef &prefix) +{ + for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { + const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(j); + if (namespaceDeclaration.prefix == prefix) { + return namespaceDeclaration.namespaceUri; + } + } + +#if 1 + if (namespaceProcessing && !prefix.isEmpty()) + raiseWellFormedError(XmlStream::tr("Namespace prefix '%1' not declared").arg(prefix.toString())); +#endif + + return QStringRef(); +} + +/* + uses namespaceForPrefix and builds the attribute vector + */ +void XmlStreamReaderPrivate::resolveTag() +{ + int n = attributeStack.size(); + + if (namespaceProcessing) { + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isNamespaceAttribute + || dtdAttribute.defaultValue.isNull() + || dtdAttribute.tagName != qualifiedName + || dtdAttribute.attributeQualifiedName.isNull()) + continue; + int i = 0; + while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) + ++i; + if (i != n) + continue; + if (dtdAttribute.attributePrefix.isEmpty() && dtdAttribute.attributeName == QLatin1String("xmlns")) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(dtdAttribute.defaultValue); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(XmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = ns; + } else if (dtdAttribute.attributePrefix == QLatin1String("xmlns")) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = dtdAttribute.attributeName; + QStringRef namespaceUri = dtdAttribute.defaultValue; + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(XmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = namespacePrefix; + namespaceDeclaration.namespaceUri = namespaceUri; + } + } + } + + tagStack.top().namespaceDeclaration.namespaceUri = namespaceUri = namespaceForPrefix(prefix); + + attributes.resize(n); + + for (int i = 0; i < n; ++i) { + XmlStreamAttribute &attribute = attributes[i]; + Attribute &attrib = attributeStack[i]; + QStringRef prefix(symPrefix(attrib.key)); + QStringRef name(symString(attrib.key)); + QStringRef qualifiedName(symName(attrib.key)); + QStringRef value(symString(attrib.value)); + + attribute.m_name = XmlStreamStringRef(name); + attribute.m_qualifiedName = XmlStreamStringRef(qualifiedName); + attribute.m_value = XmlStreamStringRef(value); + + if (!prefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(prefix); + attribute.m_namespaceUri = XmlStreamStringRef(attributeNamespaceUri); + } + + for (int j = 0; j < i; ++j) { + if (attributes[j].name() == attribute.name() + && attributes[j].namespaceUri() == attribute.namespaceUri() + && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName())) + raiseWellFormedError(XmlStream::tr("Attribute redefined.")); + } + } + + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (dtdAttribute.isNamespaceAttribute + || dtdAttribute.defaultValue.isNull() + || dtdAttribute.tagName != qualifiedName + || dtdAttribute.attributeQualifiedName.isNull()) + continue; + int i = 0; + while (i < n && symName(attributeStack[i].key) != dtdAttribute.attributeQualifiedName) + ++i; + if (i != n) + continue; + + + + XmlStreamAttribute attribute; + attribute.m_name = XmlStreamStringRef(dtdAttribute.attributeName); + attribute.m_qualifiedName = XmlStreamStringRef(dtdAttribute.attributeQualifiedName); + attribute.m_value = XmlStreamStringRef(dtdAttribute.defaultValue); + + if (!dtdAttribute.attributePrefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix); + attribute.m_namespaceUri = XmlStreamStringRef(attributeNamespaceUri); + } + attribute.m_isDefault = true; + attributes.append(attribute); + } + + attributeStack.clear(); +} + +void XmlStreamReaderPrivate::resolvePublicNamespaces() +{ + const Tag &tag = tagStack.top(); + int n = namespaceDeclarations.size() - tag.namespaceDeclarationsSize; + publicNamespaceDeclarations.resize(n); + for (int i = 0; i < n; ++i) { + const NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(tag.namespaceDeclarationsSize + i); + XmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i]; + publicNamespaceDeclaration.m_prefix = XmlStreamStringRef(namespaceDeclaration.prefix); + publicNamespaceDeclaration.m_namespaceUri = XmlStreamStringRef(namespaceDeclaration.namespaceUri); + } +} + +void XmlStreamReaderPrivate::resolveDtd() +{ + publicNotationDeclarations.resize(notationDeclarations.size()); + for (int i = 0; i < notationDeclarations.size(); ++i) { + const XmlStreamReaderPrivate::NotationDeclaration ¬ationDeclaration = notationDeclarations.at(i); + XmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i]; + publicNotationDeclaration.m_name = XmlStreamStringRef(notationDeclaration.name); + publicNotationDeclaration.m_systemId = XmlStreamStringRef(notationDeclaration.systemId); + publicNotationDeclaration.m_publicId = XmlStreamStringRef(notationDeclaration.publicId); + + } + notationDeclarations.clear(); + publicEntityDeclarations.resize(entityDeclarations.size()); + for (int i = 0; i < entityDeclarations.size(); ++i) { + const XmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i); + XmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i]; + publicEntityDeclaration.m_name = XmlStreamStringRef(entityDeclaration.name); + publicEntityDeclaration.m_notationName = XmlStreamStringRef(entityDeclaration.notationName); + publicEntityDeclaration.m_systemId = XmlStreamStringRef(entityDeclaration.systemId); + publicEntityDeclaration.m_publicId = XmlStreamStringRef(entityDeclaration.publicId); + publicEntityDeclaration.m_value = XmlStreamStringRef(entityDeclaration.value); + } + entityDeclarations.clear(); + parameterEntityHash.clear(); +} + +uint XmlStreamReaderPrivate::resolveCharRef(int symbolIndex) +{ + bool ok = true; + uint s; + // ### add toXShort to QStringRef? + if (sym(symbolIndex).c == 'x') + s = symString(symbolIndex, 1).toString().toUInt(&ok, 16); + else + s = symString(symbolIndex).toString().toUInt(&ok, 10); + + ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff) + || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= 0x10ffff)); + + return ok ? s : 0; +} + + +void XmlStreamReaderPrivate::checkPublicLiteral(const QStringRef &publicId) +{ +//#x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] + + const ushort *data = reinterpret_cast(publicId.constData()); + uchar c = 0; + int i; + for (i = publicId.size() - 1; i >= 0; --i) { + if (data[i] < 256) + switch ((c = data[i])) { + case ' ': case '\n': case '\r': case '-': case '(': case ')': + case '+': case ',': case '.': case '/': case ':': case '=': + case '?': case ';': case '!': case '*': case '#': case '@': + case '$': case '_': case '%': case '\'': case '\"': + continue; + default: + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9')) + continue; + } + break; + } + if (i >= 0) + raiseWellFormedError(XmlStream::tr("Unexpected character '%1' in public id literal.").arg(QChar(QLatin1Char(c)))); +} + +/* + Checks whether the document starts with an xml declaration. If it + does, this function returns true; otherwise it sets up everything + for a synthetic start document event and returns false. + */ +bool XmlStreamReaderPrivate::checkStartDocument() +{ + hasCheckedStartDocument = true; + + if (scanString(spell[XML], XML)) + return true; + + type = XmlStreamReader::StartDocument; + if (atEnd) { + hasCheckedStartDocument = false; + raiseError(XmlStreamReader::PrematureEndOfDocumentError); + } + return false; +} + +void XmlStreamReaderPrivate::startDocument() +{ + QString err; + if (documentVersion != QLatin1String("1.0")) { + if (documentVersion.toString().contains(QLatin1Char(' '))) + err = XmlStream::tr("Invalid XML version string."); + else + err = XmlStream::tr("Unsupported XML version."); + } + int n = attributeStack.size(); + + /* We use this bool to ensure that the pesudo attributes are in the + * proper order: + * + * [23] XMLDecl ::= '' */ + bool hasStandalone = false; + + for (int i = 0; err.isNull() && i < n; ++i) { + Attribute &attrib = attributeStack[i]; + QStringRef prefix(symPrefix(attrib.key)); + QStringRef key(symString(attrib.key)); + QStringRef value(symString(attrib.value)); + + if (prefix.isEmpty() && key == QLatin1String("encoding")) { + const QString name(value.toString()); + documentEncoding = value; + + if(hasStandalone) + err = XmlStream::tr("The standalone pseudo attribute must appear after the encoding."); + if(!XmlUtils::isEncName(name)) + err = XmlStream::tr("%1 is an invalid encoding name.").arg(name); + else { +#ifdef QT_NO_TEXTCODEC + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#else + QTextCodec *const newCodec = QTextCodec::codecForName(name.toLatin1()); + if (!newCodec) + err = XmlStream::tr("Encoding %1 is unsupported").arg(name); + else if (newCodec != codec && !lockEncoding) { + codec = newCodec; + delete decoder; + decoder = codec->makeDecoder(); + decoder->toUnicode(&readBuffer, rawReadBuffer.data(), nbytesread); + } +#endif // QT_NO_TEXTCODEC + } + } else if (prefix.isEmpty() && key == QLatin1String("standalone")) { + hasStandalone = true; + if (value == QLatin1String("yes")) + standalone = true; + else if (value == QLatin1String("no")) + standalone = false; + else + err = XmlStream::tr("Standalone accepts only yes or no."); + } else { + err = XmlStream::tr("Invalid attribute in XML declaration."); + } + } + + if (!err.isNull()) + raiseWellFormedError(err); + attributeStack.clear(); +} + + +void XmlStreamReaderPrivate::raiseError(XmlStreamReader::Error error, const QString& message) +{ + this->error = error; + errorString = message; + if (errorString.isNull()) { + if (error == XmlStreamReader::PrematureEndOfDocumentError) + errorString = XmlStream::tr("Premature end of document."); + else if (error == XmlStreamReader::CustomError) + errorString = XmlStream::tr("Invalid document."); + } + + type = XmlStreamReader::Invalid; +} + +void XmlStreamReaderPrivate::raiseWellFormedError(const QString &message) +{ + raiseError(XmlStreamReader::NotWellFormedError, message); +} + +void XmlStreamReaderPrivate::parseError() +{ + + if (token == EOF_SYMBOL) { + raiseError(XmlStreamReader::PrematureEndOfDocumentError); + return; + } + const int nmax = 4; + QString error_message; + int ers = state_stack[tos]; + int nexpected = 0; + int expected[nmax]; + if (token != ERROR) + for (int tk = 0; tk < TERMINAL_COUNT; ++tk) { + int k = t_action(ers, tk); + if (k <= 0) + continue; + if (spell[tk]) { + if (nexpected < nmax) + expected[nexpected++] = tk; + } + } + + error_message.clear (); + if (nexpected && nexpected < nmax) { + bool first = true; + + for (int s = 0; s < nexpected; ++s) { + if (first) + error_message += XmlStream::tr ("Expected "); + else if (s == nexpected - 1) + error_message += QLatin1String (nexpected > 2 ? ", or " : " or "); + else + error_message += QLatin1String (", "); + + first = false; + error_message += QLatin1String("\'"); + error_message += QLatin1String (spell [expected[s]]); + error_message += QLatin1String("\'"); + } + error_message += XmlStream::tr(", but got \'"); + error_message += QLatin1String(spell [token]); + error_message += QLatin1String("\'"); + } else { + error_message += XmlStream::tr("Unexpected \'"); + error_message += QLatin1String(spell [token]); + error_message += QLatin1String("\'"); + } + error_message += QLatin1Char('.'); + + raiseWellFormedError(error_message); +} + +void XmlStreamReaderPrivate::resume(int rule) { + resumeReduction = rule; + if (error == XmlStreamReader::NoError) + raiseError(XmlStreamReader::PrematureEndOfDocumentError); +} + +/*! Returns the current line number, starting with 1. + +\sa columnNumber(), characterOffset() + */ +qint64 XmlStreamReader::lineNumber() const +{ + Q_D(const XmlStreamReader); + return d->lineNumber + 1; // in public we start with 1 +} + +/*! Returns the current column number, starting with 0. + +\sa lineNumber(), characterOffset() + */ +qint64 XmlStreamReader::columnNumber() const +{ + Q_D(const XmlStreamReader); + return d->characterOffset - d->lastLineStart + d->readBufferPos; +} + +/*! Returns the current character offset, starting with 0. + +\sa lineNumber(), columnNumber() +*/ +qint64 XmlStreamReader::characterOffset() const +{ + Q_D(const XmlStreamReader); + return d->characterOffset + d->readBufferPos; +} + + +/*! Returns the text of \l Characters, \l Comment, \l DTD, or + EntityReference. + */ +QStringRef XmlStreamReader::text() const +{ + Q_D(const XmlStreamReader); + return d->text; +} + + +/*! If the state() is \l DTD, this function returns the DTD's + notation declarations. Otherwise an empty vector is returned. + + The XmlStreamNotationDeclarations class is defined to be a QVector + of XmlStreamNotationDeclaration. + */ +XmlStreamNotationDeclarations XmlStreamReader::notationDeclarations() const +{ + Q_D(const XmlStreamReader); + if (d->notationDeclarations.size()) + const_cast(d)->resolveDtd(); + return d->publicNotationDeclarations; +} + + +/*! If the state() is \l DTD, this function returns the DTD's + unparsed (external) entity declarations. Otherwise an empty vector is returned. + + The XmlStreamEntityDeclarations class is defined to be a QVector + of XmlStreamEntityDeclaration. + */ +XmlStreamEntityDeclarations XmlStreamReader::entityDeclarations() const +{ + Q_D(const XmlStreamReader); + if (d->entityDeclarations.size()) + const_cast(d)->resolveDtd(); + return d->publicEntityDeclarations; +} + +/*! + \since 4.4 + + If the state() is \l DTD, this function returns the DTD's + name. Otherwise an empty string is returned. + + */ +QStringRef XmlStreamReader::dtdName() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::DTD) + return d->dtdName; + return QStringRef(); +} + +/*! + \since 4.4 + + If the state() is \l DTD, this function returns the DTD's + public identifier. Otherwise an empty string is returned. + + */ +QStringRef XmlStreamReader::dtdPublicId() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::DTD) + return d->dtdPublicId; + return QStringRef(); +} + +/*! + \since 4.4 + + If the state() is \l DTD, this function returns the DTD's + system identifier. Otherwise an empty string is returned. + + */ +QStringRef XmlStreamReader::dtdSystemId() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::DTD) + return d->dtdSystemId; + return QStringRef(); +} + +/*! If the state() is \l StartElement, this function returns the + element's namespace declarations. Otherwise an empty vector is + returned. + + The XmlStreamNamespaceDeclaration class is defined to be a QVector + of XmlStreamNamespaceDeclaration. + + \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations() + */ +XmlStreamNamespaceDeclarations XmlStreamReader::namespaceDeclarations() const +{ + Q_D(const XmlStreamReader); + if (d->publicNamespaceDeclarations.isEmpty() && d->type == StartElement) + const_cast(d)->resolvePublicNamespaces(); + return d->publicNamespaceDeclarations; +} + + +/*! + \since 4.4 + + Adds an \a extraNamespaceDeclaration. The declaration will be + valid for children of the current element, or - should the function + be called before any elements are read - for the entire XML + document. + + \sa namespaceDeclarations(), addExtraNamespaceDeclarations(), setNamespaceProcessing() + */ +void XmlStreamReader::addExtraNamespaceDeclaration(const XmlStreamNamespaceDeclaration &extraNamespaceDeclaration) +{ + Q_D(XmlStreamReader); + XmlStreamReaderPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix = d->addToStringStorage(extraNamespaceDeclaration.prefix()); + namespaceDeclaration.namespaceUri = d->addToStringStorage(extraNamespaceDeclaration.namespaceUri()); +} + +/*! + \since 4.4 + + Adds a vector of declarations specified by \a extraNamespaceDeclarations. + + \sa namespaceDeclarations(), addExtraNamespaceDeclaration() + */ +void XmlStreamReader::addExtraNamespaceDeclarations(const XmlStreamNamespaceDeclarations &extraNamespaceDeclarations) +{ + for (int i = 0; i < extraNamespaceDeclarations.size(); ++i) + addExtraNamespaceDeclaration(extraNamespaceDeclarations.at(i)); +} + + +/*! Convenience function to be called in case a StartElement was + read. Reads until the corresponding EndElement and returns all text + in-between. In case of no error, the current token (see tokenType()) + after having called this function is EndElement. + + The function concatenates text() when it reads either \l Characters + or EntityReference tokens, but skips ProcessingInstruction and \l + Comment. If the current token is not StartElement, an empty string is + returned. + + The \a behaviour defines what happens in case anything else is + read before reaching EndElement. The function can include the text from + child elements (useful for example for HTML), ignore child elements, or + raise an UnexpectedElementError and return what was read so far. + + \since 4.6 + */ +QString XmlStreamReader::readElementText(ReadElementTextBehaviour behaviour) +{ + Q_D(XmlStreamReader); + if (isStartElement()) { + QString result; + forever { + switch (readNext()) { + case Characters: + case EntityReference: + result.insert(result.size(), d->text.unicode(), d->text.size()); + break; + case EndElement: + return result; + case ProcessingInstruction: + case Comment: + break; + case StartElement: + if (behaviour == SkipChildElements) { + skipCurrentElement(); + break; + } else if (behaviour == IncludeChildElements) { + result += readElementText(behaviour); + break; + } + // Fall through (for ErrorOnUnexpectedElement) + default: + if (d->error || behaviour == ErrorOnUnexpectedElement) { + if (!d->error) + d->raiseError(UnexpectedElementError, XmlStream::tr("Expected character data.")); + return result; + } + } + } + } + return QString(); +} + +/*! + \overload readElementText() + + Calling this function is equivalent to calling readElementText(ErrorOnUnexpectedElement). + */ +QString XmlStreamReader::readElementText() +{ + return readElementText(ErrorOnUnexpectedElement); +} + +/*! Raises a custom error with an optional error \a message. + + \sa error(), errorString() + */ +void XmlStreamReader::raiseError(const QString& message) +{ + Q_D(XmlStreamReader); + d->raiseError(CustomError, message); +} + +/*! + Returns the error message that was set with raiseError(). + + \sa error(), lineNumber(), columnNumber(), characterOffset() + */ +QString XmlStreamReader::errorString() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::Invalid) + return d->errorString; + return QString(); +} + +/*! Returns the type of the current error, or NoError if no error occurred. + + \sa errorString(), raiseError() + */ +XmlStreamReader::Error XmlStreamReader::error() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::Invalid) + return d->error; + return NoError; +} + +/*! + Returns the target of a ProcessingInstruction. + */ +QStringRef XmlStreamReader::processingInstructionTarget() const +{ + Q_D(const XmlStreamReader); + return d->processingInstructionTarget; +} + +/*! + Returns the data of a ProcessingInstruction. + */ +QStringRef XmlStreamReader::processingInstructionData() const +{ + Q_D(const XmlStreamReader); + return d->processingInstructionData; +} + + + +/*! + Returns the local name of a StartElement, EndElement, or an EntityReference. + + \sa namespaceUri(), qualifiedName() + */ +QStringRef XmlStreamReader::name() const +{ + Q_D(const XmlStreamReader); + return d->name; +} + +/*! + Returns the namespaceUri of a StartElement or EndElement. + + \sa name(), qualifiedName() + */ +QStringRef XmlStreamReader::namespaceUri() const +{ + Q_D(const XmlStreamReader); + return d->namespaceUri; +} + +/*! + Returns the qualified name of a StartElement or EndElement; + + A qualified name is the raw name of an element in the XML data. It + consists of the namespace prefix, followed by colon, followed by the + element's local name. Since the namespace prefix is not unique (the + same prefix can point to different namespaces and different prefixes + can point to the same namespace), you shouldn't use qualifiedName(), + but the resolved namespaceUri() and the attribute's local name(). + + \sa name(), prefix(), namespaceUri() + */ +QStringRef XmlStreamReader::qualifiedName() const +{ + Q_D(const XmlStreamReader); + return d->qualifiedName; +} + + + +/*! + \since 4.4 + + Returns the prefix of a StartElement or EndElement. + + \sa name(), qualifiedName() +*/ +QStringRef XmlStreamReader::prefix() const +{ + Q_D(const XmlStreamReader); + return d->prefix; +} + +/*! + Returns the attributes of a StartElement. + */ +XmlStreamAttributes XmlStreamReader::attributes() const +{ + Q_D(const XmlStreamReader); + return d->attributes; +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class XmlStreamAttribute + \since 4.3 + \reentrant + \brief The XmlStreamAttribute class represents a single XML attribute + + \ingroup xml-tools + + An attribute consists of an optionally empty namespaceUri(), a + name(), a value(), and an isDefault() attribute. + + The raw XML attribute name is returned as qualifiedName(). +*/ + +/*! + Creates an empty attribute. + */ +XmlStreamAttribute::XmlStreamAttribute() +{ + m_isDefault = false; +} + +/*! + Destructs an attribute. + */ +XmlStreamAttribute::~XmlStreamAttribute() +{ +} + +/*! Constructs an attribute in the namespace described with \a + namespaceUri with \a name and value \a value. + */ +XmlStreamAttribute::XmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + m_namespaceUri = XmlStreamStringRef(QStringRef(&namespaceUri)); + m_name = m_qualifiedName = XmlStreamStringRef(QStringRef(&name)); + m_value = XmlStreamStringRef(QStringRef(&value)); + m_namespaceUri = XmlStreamStringRef(QStringRef(&namespaceUri)); +} + +/*! + Constructs an attribute with qualified name \a qualifiedName and value \a value. + */ +XmlStreamAttribute::XmlStreamAttribute(const QString &qualifiedName, const QString &value) +{ + int colon = qualifiedName.indexOf(QLatin1Char(':')); + m_name = XmlStreamStringRef(QStringRef(&qualifiedName, + colon + 1, + qualifiedName.size() - (colon + 1))); + m_qualifiedName = XmlStreamStringRef(QStringRef(&qualifiedName)); + m_value = XmlStreamStringRef(QStringRef(&value)); +} + +/*! \fn QStringRef XmlStreamAttribute::namespaceUri() const + + Returns the attribute's resolved namespaceUri, or an empty string + reference if the attribute does not have a defined namespace. + */ +/*! \fn QStringRef XmlStreamAttribute::name() const + Returns the attribute's local name. + */ +/*! \fn QStringRef XmlStreamAttribute::qualifiedName() const + Returns the attribute's qualified name. + + A qualified name is the raw name of an attribute in the XML + data. It consists of the namespace prefix(), followed by colon, + followed by the attribute's local name(). Since the namespace prefix + is not unique (the same prefix can point to different namespaces + and different prefixes can point to the same namespace), you + shouldn't use qualifiedName(), but the resolved namespaceUri() and + the attribute's local name(). + */ +/*! + \fn QStringRef XmlStreamAttribute::prefix() const + \since 4.4 + Returns the attribute's namespace prefix. + + \sa name(), qualifiedName() + +*/ + +/*! \fn QStringRef XmlStreamAttribute::value() const + Returns the attribute's value. + */ + +/*! \fn bool XmlStreamAttribute::isDefault() const + + Returns true if the parser added this attribute with a default + value following an ATTLIST declaration in the DTD; otherwise + returns false. +*/ +/*! \fn bool XmlStreamAttribute::operator==(const XmlStreamAttribute &other) const + + Compares this attribute with \a other and returns true if they are + equal; otherwise returns false. + */ +/*! \fn bool XmlStreamAttribute::operator!=(const XmlStreamAttribute &other) const + + Compares this attribute with \a other and returns true if they are + not equal; otherwise returns false. + */ + + +/*! + Creates a copy of \a other. + */ +XmlStreamAttribute::XmlStreamAttribute(const XmlStreamAttribute &other) +{ + *this = other; +} + +/*! + Assigns \a other to this attribute. + */ +XmlStreamAttribute& XmlStreamAttribute::operator=(const XmlStreamAttribute &other) +{ + m_name = other.m_name; + m_namespaceUri = other.m_namespaceUri; + m_qualifiedName = other.m_qualifiedName; + m_value = other.m_value; + m_isDefault = other.m_isDefault; + return *this; +} + + +/*! + \class XmlStreamAttributes + \since 4.3 + \reentrant + \brief The XmlStreamAttributes class represents a vector of XmlStreamAttribute. + + Attributes are returned by a XmlStreamReader in + \l{XmlStreamReader::attributes()} {attributes()} when the reader + reports a \l {XmlStreamReader::StartElement}{start element}. The + class can also be used with a XmlStreamWriter as an argument to + \l {XmlStreamWriter::writeAttributes()}{writeAttributes()}. + + The convenience function value() loops over the vector and returns + an attribute value for a given namespaceUri and an attribute's + name. + + New attributes can be added with append(). + + \ingroup xml-tools +*/ + +/*! + \fn void XmlStreamAttributes::append(const XmlStreamAttribute &attribute) + + Appends the given \a attribute to the end of the vector. + + \sa QVector::append() +*/ + + +/*! + \typedef XmlStreamNotationDeclarations + \relates XmlStreamNotationDeclaration + + Synonym for QVector. +*/ + + +/*! + \class XmlStreamNotationDeclaration + \since 4.3 + \reentrant + \brief The XmlStreamNotationDeclaration class represents a DTD notation declaration. + + \ingroup xml-tools + + An notation declaration consists of a name(), a systemId(), and a publicId(). +*/ + +/*! + Creates an empty notation declaration. +*/ +XmlStreamNotationDeclaration::XmlStreamNotationDeclaration() +{ +} +/*! + Creates a copy of \a other. + */ +XmlStreamNotationDeclaration::XmlStreamNotationDeclaration(const XmlStreamNotationDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this notation declaration. + */ +XmlStreamNotationDeclaration& XmlStreamNotationDeclaration::operator=(const XmlStreamNotationDeclaration &other) +{ + m_name = other.m_name; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + return *this; +} + +/*! +Destructs this notation declaration. +*/ +XmlStreamNotationDeclaration::~XmlStreamNotationDeclaration() +{ +} + +/*! \fn QStringRef XmlStreamNotationDeclaration::name() const + +Returns the notation name. +*/ +/*! \fn QStringRef XmlStreamNotationDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef XmlStreamNotationDeclaration::publicId() const + +Returns the public identifier. +*/ + +/*! \fn inline bool XmlStreamNotationDeclaration::operator==(const XmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns true + if they are equal; otherwise returns false. + */ +/*! \fn inline bool XmlStreamNotationDeclaration::operator!=(const XmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns true + if they are not equal; otherwise returns false. + */ + +/*! + \typedef XmlStreamNamespaceDeclarations + \relates XmlStreamNamespaceDeclaration + + Synonym for QVector. +*/ + +/*! + \class XmlStreamNamespaceDeclaration + \since 4.3 + \reentrant + \brief The XmlStreamNamespaceDeclaration class represents a namespace declaration. + + \ingroup xml-tools + + An namespace declaration consists of a prefix() and a namespaceUri(). +*/ +/*! \fn inline bool XmlStreamNamespaceDeclaration::operator==(const XmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns true + if they are equal; otherwise returns false. + */ +/*! \fn inline bool XmlStreamNamespaceDeclaration::operator!=(const XmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns true + if they are not equal; otherwise returns false. + */ + +/*! + Creates an empty namespace declaration. +*/ +XmlStreamNamespaceDeclaration::XmlStreamNamespaceDeclaration() +{ +} + +/*! + \since 4.4 + + Creates a namespace declaration with \a prefix and \a namespaceUri. +*/ +XmlStreamNamespaceDeclaration::XmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri) +{ + m_prefix = prefix; + m_namespaceUri = namespaceUri; +} + +/*! + Creates a copy of \a other. + */ +XmlStreamNamespaceDeclaration::XmlStreamNamespaceDeclaration(const XmlStreamNamespaceDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this namespace declaration. + */ +XmlStreamNamespaceDeclaration& XmlStreamNamespaceDeclaration::operator=(const XmlStreamNamespaceDeclaration &other) +{ + m_prefix = other.m_prefix; + m_namespaceUri = other.m_namespaceUri; + return *this; +} +/*! +Destructs this namespace declaration. +*/ +XmlStreamNamespaceDeclaration::~XmlStreamNamespaceDeclaration() +{ +} + +/*! \fn QStringRef XmlStreamNamespaceDeclaration::prefix() const + +Returns the prefix. +*/ +/*! \fn QStringRef XmlStreamNamespaceDeclaration::namespaceUri() const + +Returns the namespaceUri. +*/ + + + + +/*! + \typedef XmlStreamEntityDeclarations + \relates XmlStreamEntityDeclaration + + Synonym for QVector. +*/ + +/*! + \class XmlStreamStringRef + \since 4.3 + \internal +*/ + +/*! + \class XmlStreamEntityDeclaration + \since 4.3 + \reentrant + \brief The XmlStreamEntityDeclaration class represents a DTD entity declaration. + + \ingroup xml-tools + + An entity declaration consists of a name(), a notationName(), a + systemId(), a publicId(), and a value(). +*/ + +/*! + Creates an empty entity declaration. +*/ +XmlStreamEntityDeclaration::XmlStreamEntityDeclaration() +{ +} + +/*! + Creates a copy of \a other. + */ +XmlStreamEntityDeclaration::XmlStreamEntityDeclaration(const XmlStreamEntityDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this entity declaration. + */ +XmlStreamEntityDeclaration& XmlStreamEntityDeclaration::operator=(const XmlStreamEntityDeclaration &other) +{ + m_name = other.m_name; + m_notationName = other.m_notationName; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + m_value = other.m_value; + return *this; +} + +/*! + Destructs this entity declaration. +*/ +XmlStreamEntityDeclaration::~XmlStreamEntityDeclaration() +{ +} + +/*! \fn QStringRef XmlStreamEntityDeclaration::name() const + +Returns the entity name. +*/ +/*! \fn QStringRef XmlStreamEntityDeclaration::notationName() const + +Returns the notation name. +*/ +/*! \fn QStringRef XmlStreamEntityDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef XmlStreamEntityDeclaration::publicId() const + +Returns the public identifier. +*/ +/*! \fn QStringRef XmlStreamEntityDeclaration::value() const + +Returns the entity's value. +*/ + +/*! \fn bool XmlStreamEntityDeclaration::operator==(const XmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns true if + they are equal; otherwise returns false. + */ +/*! \fn bool XmlStreamEntityDeclaration::operator!=(const XmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns true if + they are not equal; otherwise returns false. + */ + +/*! Returns the value of the attribute \a name in the namespace + described with \a namespaceUri, or an empty string reference if the + attribute is not defined. The \a namespaceUri can be empty. + */ +QStringRef XmlStreamAttributes::value(const QString &namespaceUri, const QString &name) const +{ + for (int i = 0; i < size(); ++i) { + const XmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + Returns the value of the attribute \a name in the namespace + described with \a namespaceUri, or an empty string reference if the + attribute is not defined. The \a namespaceUri can be empty. + */ +QStringRef XmlStreamAttributes::value(const QString &namespaceUri, const QLatin1String &name) const +{ + for (int i = 0; i < size(); ++i) { + const XmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + Returns the value of the attribute \a name in the namespace + described with \a namespaceUri, or an empty string reference if the + attribute is not defined. The \a namespaceUri can be empty. + */ +QStringRef XmlStreamAttributes::value(const QLatin1String &namespaceUri, const QLatin1String &name) const +{ + for (int i = 0; i < size(); ++i) { + const XmlStreamAttribute &attribute = at(i); + if (attribute.name() == name && attribute.namespaceUri() == namespaceUri) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + + Returns the value of the attribute with qualified name \a + qualifiedName , or an empty string reference if the attribute is not + defined. A qualified name is the raw name of an attribute in the XML + data. It consists of the namespace prefix, followed by colon, + followed by the attribute's local name. Since the namespace prefix + is not unique (the same prefix can point to different namespaces and + different prefixes can point to the same namespace), you shouldn't + use qualified names, but a resolved namespaceUri and the attribute's + local name. + */ +QStringRef XmlStreamAttributes::value(const QString &qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const XmlStreamAttribute &attribute = at(i); + if (attribute.qualifiedName() == qualifiedName) + return attribute.value(); + } + return QStringRef(); +} + +/*!\overload + + Returns the value of the attribute with qualified name \a + qualifiedName , or an empty string reference if the attribute is not + defined. A qualified name is the raw name of an attribute in the XML + data. It consists of the namespace prefix, followed by colon, + followed by the attribute's local name. Since the namespace prefix + is not unique (the same prefix can point to different namespaces and + different prefixes can point to the same namespace), you shouldn't + use qualified names, but a resolved namespaceUri and the attribute's + local name. + */ +QStringRef XmlStreamAttributes::value(const QLatin1String &qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const XmlStreamAttribute &attribute = at(i); + if (attribute.qualifiedName() == qualifiedName) + return attribute.value(); + } + return QStringRef(); +} + +/*!Appends a new attribute with \a name in the namespace + described with \a namespaceUri, and value \a value. The \a + namespaceUri can be empty. + */ +void XmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value) +{ + append(XmlStreamAttribute(namespaceUri, name, value)); +} + +/*!\overload + Appends a new attribute with qualified name \a qualifiedName and + value \a value. + */ +void XmlStreamAttributes::append(const QString &qualifiedName, const QString &value) +{ + append(XmlStreamAttribute(qualifiedName, value)); +} + +#ifndef QT_NO_XMLSTREAMREADER + +/*! \fn bool XmlStreamReader::isStartDocument() const + Returns true if tokenType() equals \l StartDocument; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isEndDocument() const + Returns true if tokenType() equals \l EndDocument; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isStartElement() const + Returns true if tokenType() equals \l StartElement; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isEndElement() const + Returns true if tokenType() equals \l EndElement; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isCharacters() const + Returns true if tokenType() equals \l Characters; otherwise returns false. + + \sa isWhitespace(), isCDATA() +*/ +/*! \fn bool XmlStreamReader::isComment() const + Returns true if tokenType() equals \l Comment; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isDTD() const + Returns true if tokenType() equals \l DTD; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isEntityReference() const + Returns true if tokenType() equals \l EntityReference; otherwise returns false. +*/ +/*! \fn bool XmlStreamReader::isProcessingInstruction() const + Returns true if tokenType() equals \l ProcessingInstruction; otherwise returns false. +*/ + +/*! Returns true if the reader reports characters that only consist + of white-space; otherwise returns false. + + \sa isCharacters(), text() +*/ +bool XmlStreamReader::isWhitespace() const +{ + Q_D(const XmlStreamReader); + return d->type == XmlStreamReader::Characters && d->isWhitespace; +} + +/*! Returns true if the reader reports characters that stem from a + CDATA section; otherwise returns false. + + \sa isCharacters(), text() +*/ +bool XmlStreamReader::isCDATA() const +{ + Q_D(const XmlStreamReader); + return d->type == XmlStreamReader::Characters && d->isCDATA; +} + + + +/*! + Returns true if this document has been declared standalone in the + XML declaration; otherwise returns false. + + If no XML declaration has been parsed, this function returns false. + */ +bool XmlStreamReader::isStandaloneDocument() const +{ + Q_D(const XmlStreamReader); + return d->standalone; +} + + +/*! + \since 4.4 + + If the state() is \l StartDocument, this function returns the + version string as specified in the XML declaration. + Otherwise an empty string is returned. + */ +QStringRef XmlStreamReader::documentVersion() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::StartDocument) + return d->documentVersion; + return QStringRef(); +} + +/*! + \since 4.4 + + If the state() is \l StartDocument, this function returns the + encoding string as specified in the XML declaration. + Otherwise an empty string is returned. + */ +QStringRef XmlStreamReader::documentEncoding() const +{ + Q_D(const XmlStreamReader); + if (d->type == XmlStreamReader::StartDocument) + return d->documentEncoding; + return QStringRef(); +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class XmlStreamWriter + \since 4.3 + \reentrant + + \brief The XmlStreamWriter class provides an XML writer with a + simple streaming API. + + \ingroup xml-tools + + XmlStreamWriter is the counterpart to XmlStreamReader for writing + XML. Like its related class, it operates on a QIODevice specified + with setDevice(). The API is simple and straightforward: for every + XML token or event you want to write, the writer provides a + specialized function. + + You start a document with writeStartDocument() and end it with + writeEndDocument(). This will implicitly close all remaining open + tags. + + Element tags are opened with writeStartElement() followed by + writeAttribute() or writeAttributes(), element content, and then + writeEndElement(). A shorter form writeEmptyElement() can be used + to write empty elements, followed by writeAttributes(). + + Element content consists of either characters, entity references or + nested elements. It is written with writeCharacters(), which also + takes care of escaping all forbidden characters and character + sequences, writeEntityReference(), or subsequent calls to + writeStartElement(). A convenience method writeTextElement() can be + used for writing terminal elements that contain nothing but text. + + The following abridged code snippet shows the basic use of the class + to write formatted XML with indentation: + + \snippet doc/src/snippets/qxmlstreamwriter/main.cpp start stream + \dots + \snippet doc/src/snippets/qxmlstreamwriter/main.cpp write element + \dots + \snippet doc/src/snippets/qxmlstreamwriter/main.cpp finish stream + + XmlStreamWriter takes care of prefixing namespaces, all you have to + do is specify the \c namespaceUri when writing elements or + attributes. If you must conform to certain prefixes, you can force + the writer to use them by declaring the namespaces manually with + either writeNamespace() or writeDefaultNamespace(). Alternatively, + you can bypass the stream writer's namespace support and use + overloaded methods that take a qualified name instead. The namespace + \e http://www.w3.org/XML/1998/namespace is implicit and mapped to the + prefix \e xml. + + The stream writer can automatically format the generated XML data by + adding line-breaks and indentation to empty sections between + elements, making the XML data more readable for humans and easier to + work with for most source code management systems. The feature can + be turned on with the \l autoFormatting property, and customized + with the \l autoFormattingIndent property. + + Other functions are writeCDATA(), writeComment(), + writeProcessingInstruction(), and writeDTD(). Chaining of XML + streams is supported with writeCurrentToken(). + + By default, XmlStreamWriter encodes XML in UTF-8. Different + encodings can be enforced using setCodec(). + + If an error occurs while writing to the underlying device, hasError() + starts returning true and subsequent writes are ignored. + + The \l{XmlStream Bookmarks Example} illustrates how to use a + stream writer to write an XML bookmark file (XBEL) that + was previously read in by a XmlStreamReader. + +*/ + +#ifndef QT_NO_XMLSTREAMWRITER + +class XmlStreamWriterPrivate : public XmlStreamPrivateTagStack { + XmlStreamWriter *q_ptr; + Q_DECLARE_PUBLIC(XmlStreamWriter) +public: + XmlStreamWriterPrivate(XmlStreamWriter *q); + ~XmlStreamWriterPrivate() { + if (deleteDevice) + delete device; +#ifndef QT_NO_TEXTCODEC + delete encoder; +#endif + } + + void write(const QStringRef &); + void write(const QString &); + void writeEscaped(const QString &, bool escapeWhitespace = false); + void write(const char *s, int len); + template void write(const char (&s)[N]) { write(s, N - 1); } + bool finishStartElement(bool contents = true); + void writeStartElement(const QString &namespaceUri, const QString &name); + QIODevice *device; + QString *stringDevice; + uint deleteDevice :1; + uint inStartElement :1; + uint inEmptyElement :1; + uint lastWasStartElement :1; + uint wroteSomething :1; + uint hasError :1; + uint autoFormatting :1; + uint isCodecASCIICompatible :1; + QByteArray autoFormattingIndent; + NamespaceDeclaration emptyNamespace; + int lastNamespaceDeclaration; + +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextEncoder *encoder; +#endif + void checkIfASCIICompatibleCodec(); + + NamespaceDeclaration &findNamespace(const QString &namespaceUri, bool writeDeclaration = false, bool noDefault = false); + void writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration); + + int namespacePrefixCount; + + void indent(int level); +}; + + +XmlStreamWriterPrivate::XmlStreamWriterPrivate(XmlStreamWriter *q) + :autoFormattingIndent(4, ' ') +{ + q_ptr = q; + device = 0; + stringDevice = 0; + deleteDevice = false; +#ifndef QT_NO_TEXTCODEC + codec = QTextCodec::codecForMib(106); // utf8 + encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 +#endif + checkIfASCIICompatibleCodec(); + inStartElement = inEmptyElement = false; + wroteSomething = false; + hasError = false; + lastWasStartElement = false; + lastNamespaceDeclaration = 1; + autoFormatting = false; + namespacePrefixCount = 0; +} + +void XmlStreamWriterPrivate::checkIfASCIICompatibleCodec() +{ +#ifndef QT_NO_TEXTCODEC + Q_ASSERT(encoder); + // assumes ASCII-compatibility for all 8-bit encodings + const QByteArray bytes = encoder->fromUnicode(QLatin1String(" ")); + isCodecASCIICompatible = (bytes.count() == 1); +#else + isCodecASCIICompatible = true; +#endif +} + +void XmlStreamWriterPrivate::write(const QStringRef &s) +{ + if (device) { + if (hasError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); +#endif + if (device->write(bytes) != bytes.size()) + hasError = true; + } + else if (stringDevice) + s.appendTo(stringDevice); + else + qWarning("XmlStreamWriter: No device"); +} + +void XmlStreamWriterPrivate::write(const QString &s) +{ + if (device) { + if (hasError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s); +#endif + if (device->write(bytes) != bytes.size()) + hasError = true; + } + else if (stringDevice) + stringDevice->append(s); + else + qWarning("XmlStreamWriter: No device"); +} + +void XmlStreamWriterPrivate::writeEscaped(const QString &s, bool escapeWhitespace) +{ + QString escaped; + escaped.reserve(s.size()); + for ( int i = 0; i < s.size(); ++i ) { + QChar c = s.at(i); + if (c.unicode() == '<' ) + escaped.append(QLatin1String("<")); + else if (c.unicode() == '>' ) + escaped.append(QLatin1String(">")); + else if (c.unicode() == '&' ) + escaped.append(QLatin1String("&")); + else if (c.unicode() == '\"' ) + escaped.append(QLatin1String(""")); + else if (escapeWhitespace && c.isSpace()) { + if (c.unicode() == '\n') + escaped.append(QLatin1String(" ")); + else if (c.unicode() == '\r') + escaped.append(QLatin1String(" ")); + else if (c.unicode() == '\t') + escaped.append(QLatin1String(" ")); + else + escaped += c; + } else { + escaped += QChar(c); + } + } + write(escaped); +} + +// Converts from ASCII to output encoding +void XmlStreamWriterPrivate::write(const char *s, int len) +{ + if (device) { + if (hasError) + return; + if (isCodecASCIICompatible) { + if (device->write(s, len) != len) + hasError = true; + return; + } + } + + write(QString::fromLatin1(s, len)); +} + +void XmlStreamWriterPrivate::writeNamespaceDeclaration(const NamespaceDeclaration &namespaceDeclaration) { + if (namespaceDeclaration.prefix.isEmpty()) { + write(" xmlns=\""); + write(namespaceDeclaration.namespaceUri); + write("\""); + } else { + write(" xmlns:"); + write(namespaceDeclaration.prefix); + write("=\""); + write(namespaceDeclaration.namespaceUri); + write("\""); + } +} + +bool XmlStreamWriterPrivate::finishStartElement(bool contents) +{ + bool hadSomethingWritten = wroteSomething; + wroteSomething = contents; + if (!inStartElement) + return hadSomethingWritten; + + if (inEmptyElement) { + write("/>"); + XmlStreamWriterPrivate::Tag &tag = tagStack_pop(); + lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + lastWasStartElement = false; + } else { + write(">"); + } + inStartElement = inEmptyElement = false; + lastNamespaceDeclaration = namespaceDeclarations.size(); + return hadSomethingWritten; +} + +XmlStreamPrivateTagStack::NamespaceDeclaration &XmlStreamWriterPrivate::findNamespace(const QString &namespaceUri, bool writeDeclaration, bool noDefault) +{ + for (int j = namespaceDeclarations.size() - 1; j >= 0; --j) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations[j]; + if (namespaceDeclaration.namespaceUri == namespaceUri) { + if (!noDefault || !namespaceDeclaration.prefix.isEmpty()) + return namespaceDeclaration; + } + } + if (namespaceUri.isEmpty()) + return emptyNamespace; + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + if (namespaceUri.isEmpty()) { + namespaceDeclaration.prefix.clear(); + } else { + QString s; + int n = ++namespacePrefixCount; + forever { + s = QLatin1Char('n') + QString::number(n++); + int j = namespaceDeclarations.size() - 2; + while (j >= 0 && namespaceDeclarations.at(j).prefix != s) + --j; + if (j < 0) + break; + } + namespaceDeclaration.prefix = addToStringStorage(s); + } + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + if (writeDeclaration) + writeNamespaceDeclaration(namespaceDeclaration); + return namespaceDeclaration; +} + + + +void XmlStreamWriterPrivate::indent(int level) +{ + write("\n"); + for (int i = level; i > 0; --i) + write(autoFormattingIndent.constData(), autoFormattingIndent.length()); +} + + +/*! + Constructs a stream writer. + + \sa setDevice() + */ +XmlStreamWriter::XmlStreamWriter() + : d_ptr(new XmlStreamWriterPrivate(this)) +{ +} + +/*! + Constructs a stream writer that writes into \a device; + */ +XmlStreamWriter::XmlStreamWriter(QIODevice *device) + : d_ptr(new XmlStreamWriterPrivate(this)) +{ + Q_D(XmlStreamWriter); + d->device = device; +} + +/*! Constructs a stream writer that writes into \a array. This is the + same as creating an xml writer that operates on a QBuffer device + which in turn operates on \a array. + */ +XmlStreamWriter::XmlStreamWriter(QByteArray *array) + : d_ptr(new XmlStreamWriterPrivate(this)) +{ + Q_D(XmlStreamWriter); + d->device = new QBuffer(array); + d->device->open(QIODevice::WriteOnly); + d->deleteDevice = true; +} + + +/*! Constructs a stream writer that writes into \a string. + */ +XmlStreamWriter::XmlStreamWriter(QString *string) + : d_ptr(new XmlStreamWriterPrivate(this)) +{ + Q_D(XmlStreamWriter); + d->stringDevice = string; +} + +/*! + Destructor. +*/ +XmlStreamWriter::~XmlStreamWriter() +{ +} + + +/*! + Sets the current device to \a device. If you want the stream to + write into a QByteArray, you can create a QBuffer device. + + \sa device() +*/ +void XmlStreamWriter::setDevice(QIODevice *device) +{ + Q_D(XmlStreamWriter); + if (device == d->device) + return; + d->stringDevice = 0; + if (d->deleteDevice) { + delete d->device; + d->deleteDevice = false; + } + d->device = device; +} + +/*! + Returns the current device associated with the XmlStreamWriter, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *XmlStreamWriter::device() const +{ + Q_D(const XmlStreamWriter); + return d->device; +} + + +#ifndef QT_NO_TEXTCODEC +/*! + Sets the codec for this stream to \a codec. The codec is used for + encoding any data that is written. By default, XmlStreamWriter + uses UTF-8. + + The encoding information is stored in the initial xml tag which + gets written when you call writeStartDocument(). Call this + function before calling writeStartDocument(). + + \sa codec() +*/ +void XmlStreamWriter::setCodec(QTextCodec *codec) +{ + Q_D(XmlStreamWriter); + if (codec) { + d->codec = codec; + delete d->encoder; + d->encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 + d->checkIfASCIICompatibleCodec(); + } +} + +/*! + Sets the codec for this stream to the QTextCodec for the encoding + specified by \a codecName. Common values for \c codecName include + "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't + recognized, nothing happens. + + \sa QTextCodec::codecForName() +*/ +void XmlStreamWriter::setCodec(const char *codecName) +{ + setCodec(QTextCodec::codecForName(codecName)); +} + +/*! + Returns the codec that is currently assigned to the stream. + + \sa setCodec() +*/ +QTextCodec *XmlStreamWriter::codec() const +{ + Q_D(const XmlStreamWriter); + return d->codec; +} +#endif // QT_NO_TEXTCODEC + +/*! + \property XmlStreamWriter::autoFormatting + \since 4.4 + the auto-formatting flag of the stream writer + + This property controls whether or not the stream writer + automatically formats the generated XML data. If enabled, the + writer automatically adds line-breaks and indentation to empty + sections between elements (ignorable whitespace). The main purpose + of auto-formatting is to split the data into several lines, and to + increase readability for a human reader. The indentation depth can + be controlled through the \l autoFormattingIndent property. + + By default, auto-formatting is disabled. +*/ + +/*! + \since 4.4 + + Enables auto formatting if \a enable is \c true, otherwise + disables it. + + The default value is \c false. + */ +void XmlStreamWriter::setAutoFormatting(bool enable) +{ + Q_D(XmlStreamWriter); + d->autoFormatting = enable; +} + +/*! + \since 4.4 + + Returns \c true if auto formattting is enabled, otherwise \c false. + */ +bool XmlStreamWriter::autoFormatting() const +{ + Q_D(const XmlStreamWriter); + return d->autoFormatting; +} + +/*! + \property XmlStreamWriter::autoFormattingIndent + \since 4.4 + + \brief the number of spaces or tabs used for indentation when + auto-formatting is enabled. Positive numbers indicate spaces, + negative numbers tabs. + + The default indentation is 4. + + \sa autoFormatting +*/ + + +void XmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs) +{ + Q_D(XmlStreamWriter); + d->autoFormattingIndent = QByteArray(qAbs(spacesOrTabs), spacesOrTabs >= 0 ? ' ' : '\t'); +} + +int XmlStreamWriter::autoFormattingIndent() const +{ + Q_D(const XmlStreamWriter); + return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); +} + +/*! + \since 4.8 + + Returns true if the stream failed to write to the underlying device; + otherwise returns false. + + The error status is never reset. Writes happening after the error + occurred are ignored, even if the error condition is cleared. + */ +bool XmlStreamWriter::hasError() const +{ + Q_D(const XmlStreamWriter); + return d->hasError; +} + +/*! + \overload + Writes an attribute with \a qualifiedName and \a value. + + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void XmlStreamWriter::writeAttribute(const QString &qualifiedName, const QString &value) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->write(" "); + d->write(qualifiedName); + d->write("=\""); + d->writeEscaped(value, true); + d->write("\""); +} + +/*! Writes an attribute with \a name and \a value, prefixed for + the specified \a namespaceUri. If the namespace has not been + declared yet, XmlStreamWriter will generate a namespace declaration + for it. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void XmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + XmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->findNamespace(namespaceUri, true, true); + d->write(" "); + if (!namespaceDeclaration.prefix.isEmpty()) { + d->write(namespaceDeclaration.prefix); + d->write(":"); + } + d->write(name); + d->write("=\""); + d->writeEscaped(value, true); + d->write("\""); +} + +/*! + \overload + + Writes the \a attribute. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void XmlStreamWriter::writeAttribute(const XmlStreamAttribute& attribute) +{ + if (attribute.namespaceUri().isEmpty()) + writeAttribute(attribute.qualifiedName().toString(), + attribute.value().toString()); + else + writeAttribute(attribute.namespaceUri().toString(), + attribute.name().toString(), + attribute.value().toString()); +} + + +/*! Writes the attribute vector \a attributes. If a namespace + referenced in an attribute not been declared yet, XmlStreamWriter + will generate a namespace declaration for it. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + + \sa writeAttribute(), writeNamespace() + */ +void XmlStreamWriter::writeAttributes(const XmlStreamAttributes& attributes) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_UNUSED(d); + for (int i = 0; i < attributes.size(); ++i) + writeAttribute(attributes.at(i)); +} + + +/*! Writes \a text as CDATA section. If \a text contains the + forbidden character sequence "]]>", it is split into different CDATA + sections. + + This function mainly exists for completeness. Normally you should + not need use it, because writeCharacters() automatically escapes all + non-content characters. + */ +void XmlStreamWriter::writeCDATA(const QString &text) +{ + Q_D(XmlStreamWriter); + d->finishStartElement(); + QString copy(text); + copy.replace(QLatin1String("]]>"), QLatin1String("]]]]>")); + d->write("write(copy); + d->write("]]>"); +} + + +/*! Writes \a text. The characters "<", "&", and "\"" are escaped as entity + references "<", "&, and """. To avoid the forbidden sequence + "]]>", ">" is also escaped as ">". + + \sa writeEntityReference() + */ +void XmlStreamWriter::writeCharacters(const QString &text) +{ + Q_D(XmlStreamWriter); + d->finishStartElement(); + d->writeEscaped(text); +} + + +/*! Writes \a text as XML comment, where \a text must not contain the + forbidden sequence "--" or end with "-". Note that XML does not + provide any way to escape "-" in a comment. + */ +void XmlStreamWriter::writeComment(const QString &text) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(!text.contains(QLatin1String("--")) && !text.endsWith(QLatin1Char('-'))); + if (!d->finishStartElement(false) && d->autoFormatting) + d->indent(d->tagStack.size()); + d->write(""); + d->inStartElement = d->lastWasStartElement = false; +} + + +/*! Writes a DTD section. The \a dtd represents the entire + doctypedecl production from the XML 1.0 specification. + */ +void XmlStreamWriter::writeDTD(const QString &dtd) +{ + Q_D(XmlStreamWriter); + d->finishStartElement(); + if (d->autoFormatting) + d->write("\n"); + d->write(dtd); + if (d->autoFormatting) + d->write("\n"); +} + + + +/*! \overload + Writes an empty element with qualified name \a qualifiedName. + Subsequent calls to writeAttribute() will add attributes to this element. +*/ +void XmlStreamWriter::writeEmptyElement(const QString &qualifiedName) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->writeStartElement(QString(), qualifiedName); + d->inEmptyElement = true; +} + + +/*! Writes an empty element with \a name, prefixed for the specified + \a namespaceUri. If the namespace has not been declared, + XmlStreamWriter will generate a namespace declaration for it. + Subsequent calls to writeAttribute() will add attributes to this element. + + \sa writeNamespace() + */ +void XmlStreamWriter::writeEmptyElement(const QString &namespaceUri, const QString &name) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + d->writeStartElement(namespaceUri, name); + d->inEmptyElement = true; +} + + +/*!\overload + Writes a text element with \a qualifiedName and \a text. + + + This is a convenience function equivalent to: + \snippet doc/src/snippets/code/src_corelib_xml_qxmlstream.cpp 1 + +*/ +void XmlStreamWriter::writeTextElement(const QString &qualifiedName, const QString &text) +{ + writeStartElement(qualifiedName); + writeCharacters(text); + writeEndElement(); +} + +/*! Writes a text element with \a name, prefixed for the specified \a + namespaceUri, and \a text. If the namespace has not been + declared, XmlStreamWriter will generate a namespace declaration + for it. + + + This is a convenience function equivalent to: + \snippet doc/src/snippets/code/src_corelib_xml_qxmlstream.cpp 2 + +*/ +void XmlStreamWriter::writeTextElement(const QString &namespaceUri, const QString &name, const QString &text) +{ + writeStartElement(namespaceUri, name); + writeCharacters(text); + writeEndElement(); +} + + +/*! + Closes all remaining open start elements and writes a newline. + + \sa writeStartDocument() + */ +void XmlStreamWriter::writeEndDocument() +{ + Q_D(XmlStreamWriter); + while (d->tagStack.size()) + writeEndElement(); + d->write("\n"); +} + +/*! + Closes the previous start element. + + \sa writeStartElement() + */ +void XmlStreamWriter::writeEndElement() +{ + Q_D(XmlStreamWriter); + if (d->tagStack.isEmpty()) + return; + + // shortcut: if nothing was written, close as empty tag + if (d->inStartElement && !d->inEmptyElement) { + d->write("/>"); + d->lastWasStartElement = d->inStartElement = false; + XmlStreamWriterPrivate::Tag &tag = d->tagStack_pop(); + d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + return; + } + + if (!d->finishStartElement(false) && !d->lastWasStartElement && d->autoFormatting) + d->indent(d->tagStack.size()-1); + if (d->tagStack.isEmpty()) + return; + d->lastWasStartElement = false; + XmlStreamWriterPrivate::Tag &tag = d->tagStack_pop(); + d->lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + d->write("write(tag.namespaceDeclaration.prefix); + d->write(":"); + } + d->write(tag.name); + d->write(">"); +} + + + +/*! + Writes the entity reference \a name to the stream, as "&\a{name};". + */ +void XmlStreamWriter::writeEntityReference(const QString &name) +{ + Q_D(XmlStreamWriter); + d->finishStartElement(); + d->write("&"); + d->write(name); + d->write(";"); +} + + +/*! Writes a namespace declaration for \a namespaceUri with \a + prefix. If \a prefix is empty, XmlStreamWriter assigns a unique + prefix consisting of the letter 'n' followed by a number. + + If writeStartElement() or writeEmptyElement() was called, the + declaration applies to the current element; otherwise it applies to + the next child element. + + Note that the prefix \e xml is both predefined and reserved for + \e http://www.w3.org/XML/1998/namespace, which in turn cannot be + bound to any other prefix. The prefix \e xmlns and its URI + \e http://www.w3.org/2000/xmlns/ are used for the namespace mechanism + itself and thus completely forbidden in declarations. + + */ +void XmlStreamWriter::writeNamespace(const QString &namespaceUri, const QString &prefix) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(!namespaceUri.isEmpty()); + Q_ASSERT(prefix != QLatin1String("xmlns")); + if (prefix.isEmpty()) { + d->findNamespace(namespaceUri, d->inStartElement); + } else { + Q_ASSERT(!((prefix == QLatin1String("xml")) ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace")))); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + XmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix = d->addToStringStorage(prefix); + namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); + if (d->inStartElement) + d->writeNamespaceDeclaration(namespaceDeclaration); + } +} + + +/*! Writes a default namespace declaration for \a namespaceUri. + + If writeStartElement() or writeEmptyElement() was called, the + declaration applies to the current element; otherwise it applies to + the next child element. + + Note that the namespaces \e http://www.w3.org/XML/1998/namespace + (bound to \e xmlns) and \e http://www.w3.org/2000/xmlns/ (bound to + \e xml) by definition cannot be declared as default. + */ +void XmlStreamWriter::writeDefaultNamespace(const QString &namespaceUri) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/XML/1998/namespace")); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + XmlStreamWriterPrivate::NamespaceDeclaration &namespaceDeclaration = d->namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + namespaceDeclaration.namespaceUri = d->addToStringStorage(namespaceUri); + if (d->inStartElement) + d->writeNamespaceDeclaration(namespaceDeclaration); +} + + +/*! + Writes an XML processing instruction with \a target and \a data, + where \a data must not contain the sequence "?>". + */ +void XmlStreamWriter::writeProcessingInstruction(const QString &target, const QString &data) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(!data.contains(QLatin1String("?>"))); + if (!d->finishStartElement(false) && d->autoFormatting) + d->indent(d->tagStack.size()); + d->write("write(target); + if (!data.isNull()) { + d->write(" "); + d->write(data); + } + d->write("?>"); +} + + + +/*!\overload + + Writes a document start with XML version number "1.0". This also + writes the encoding information. + + \sa writeEndDocument(), setCodec() + \since 4.5 + */ +void XmlStreamWriter::writeStartDocument() +{ + writeStartDocument(QLatin1String("1.0")); +} + + +/*! + Writes a document start with the XML version number \a version. + + \sa writeEndDocument() + */ +void XmlStreamWriter::writeStartDocument(const QString &version) +{ + Q_D(XmlStreamWriter); + d->finishStartElement(false); + d->write("write(version); + if (d->device) { // stringDevice does not get any encoding + d->write("\" encoding=\""); +#ifdef QT_NO_TEXTCODEC + d->write("iso-8859-1"); +#else + d->write(d->codec->name().constData(), d->codec->name().length()); +#endif + } + d->write("\"?>"); +} + +/*! Writes a document start with the XML version number \a version + and a standalone attribute \a standalone. + + \sa writeEndDocument() + \since 4.5 + */ +void XmlStreamWriter::writeStartDocument(const QString &version, bool standalone) +{ + Q_D(XmlStreamWriter); + d->finishStartElement(false); + d->write("write(version); + if (d->device) { // stringDevice does not get any encoding + d->write("\" encoding=\""); +#ifdef QT_NO_TEXTCODEC + d->write("iso-8859-1"); +#else + d->write(d->codec->name().constData(), d->codec->name().length()); +#endif + } + if (standalone) + d->write("\" standalone=\"yes\"?>"); + else + d->write("\" standalone=\"no\"?>"); +} + + +/*!\overload + + Writes a start element with \a qualifiedName. Subsequent calls to + writeAttribute() will add attributes to this element. + + \sa writeEndElement(), writeEmptyElement() + */ +void XmlStreamWriter::writeStartElement(const QString &qualifiedName) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(qualifiedName.count(QLatin1Char(':')) <= 1); + d->writeStartElement(QString(), qualifiedName); +} + + +/*! Writes a start element with \a name, prefixed for the specified + \a namespaceUri. If the namespace has not been declared yet, + XmlStreamWriter will generate a namespace declaration for + it. Subsequent calls to writeAttribute() will add attributes to this + element. + + \sa writeNamespace(), writeEndElement(), writeEmptyElement() + */ +void XmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name) +{ + Q_D(XmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + d->writeStartElement(namespaceUri, name); +} + +void XmlStreamWriterPrivate::writeStartElement(const QString &namespaceUri, const QString &name) +{ + if (!finishStartElement(false) && autoFormatting) + indent(tagStack.size()); + + Tag &tag = tagStack_push(); + tag.name = addToStringStorage(name); + tag.namespaceDeclaration = findNamespace(namespaceUri); + write("<"); + if (!tag.namespaceDeclaration.prefix.isEmpty()) { + write(tag.namespaceDeclaration.prefix); + write(":"); + } + write(tag.name); + inStartElement = lastWasStartElement = true; + + for (int i = lastNamespaceDeclaration; i < namespaceDeclarations.size(); ++i) + writeNamespaceDeclaration(namespaceDeclarations[i]); + tag.namespaceDeclarationsSize = lastNamespaceDeclaration; +} + +#ifndef QT_NO_XMLSTREAMREADER +/*! Writes the current state of the \a reader. All possible valid + states are supported. + + The purpose of this function is to support chained processing of XML data. + + \sa XmlStreamReader::tokenType() + */ +void XmlStreamWriter::writeCurrentToken(const XmlStreamReader &reader) +{ + switch (reader.tokenType()) { + case XmlStreamReader::NoToken: + break; + case XmlStreamReader::StartDocument: + writeStartDocument(); + break; + case XmlStreamReader::EndDocument: + writeEndDocument(); + break; + case XmlStreamReader::StartElement: { + XmlStreamNamespaceDeclarations namespaceDeclarations = reader.namespaceDeclarations(); + for (int i = 0; i < namespaceDeclarations.size(); ++i) { + const XmlStreamNamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(i); + writeNamespace(namespaceDeclaration.namespaceUri().toString(), + namespaceDeclaration.prefix().toString()); + } + writeStartElement(reader.namespaceUri().toString(), reader.name().toString()); + writeAttributes(reader.attributes()); + } break; + case XmlStreamReader::EndElement: + writeEndElement(); + break; + case XmlStreamReader::Characters: + if (reader.isCDATA()) + writeCDATA(reader.text().toString()); + else + writeCharacters(reader.text().toString()); + break; + case XmlStreamReader::Comment: + writeComment(reader.text().toString()); + break; + case XmlStreamReader::DTD: + writeDTD(reader.text().toString()); + break; + case XmlStreamReader::EntityReference: + writeEntityReference(reader.name().toString()); + break; + case XmlStreamReader::ProcessingInstruction: + writeProcessingInstruction(reader.processingInstructionTarget().toString(), + reader.processingInstructionData().toString()); + break; + default: + Q_ASSERT(reader.tokenType() != XmlStreamReader::Invalid); + qWarning("XmlStreamWriter: writeCurrentToken() with invalid state."); + break; + } +} + +/*! + \fn bool XmlStreamAttributes::hasAttribute(const QString &qualifiedName) const + \since 4.5 + + Returns true if this XmlStreamAttributes has an attribute whose + qualified name is \a qualifiedName; otherwise returns false. + + Note that this is not namespace aware. For instance, if this + XmlStreamAttributes contains an attribute whose lexical name is "xlink:href" + this doesn't tell that an attribute named \c href in the XLink namespace is + present, since the \c xlink prefix can be bound to any namespace. Use the + overload that takes a namespace URI and a local name as parameter, for + namespace aware code. +*/ + +/*! + \fn bool XmlStreamAttributes::hasAttribute(const QLatin1String &qualifiedName) const + \overload + \since 4.5 +*/ + +/*! + \fn bool XmlStreamAttributes::hasAttribute(const QString &namespaceUri, + const QString &name) const + \overload + \since 4.5 + + Returns true if this XmlStreamAttributes has an attribute whose + namespace URI and name correspond to \a namespaceUri and \a name; + otherwise returns false. +*/ + +#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMWRITER + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAM diff --git a/xml/qxmlstream.g b/xml/qxmlstream.g new file mode 100644 index 0000000000..3c97a4f7f5 --- /dev/null +++ b/xml/qxmlstream.g @@ -0,0 +1,1850 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +-- Contact: http://www.qt-project.org/legal +-- +-- This file is part of the QtCore 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 Digia. For licensing terms and +-- conditions see http://qt.digia.com/licensing. For further information +-- use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 2.1 requirements +-- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +-- +-- In addition, as a special exception, Digia gives you certain additional +-- rights. These rights are described in the Digia Qt LGPL Exception +-- version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +-- +-- GNU General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU +-- General Public License version 3.0 as published by the Free Software +-- Foundation and appearing in the file LICENSE.GPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU General Public License version 3.0 requirements will be +-- met: http://www.gnu.org/copyleft/gpl.html. +-- +-- +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser XmlStreamReader_Table + +%merged_output qxmlstream_p.h + +%token NOTOKEN +%token SPACE " " +%token LANGLE "<" +%token RANGLE ">" +%token AMPERSAND "&" +%token HASH "#" +%token QUOTE "\'" +%token DBLQUOTE "\"" +%token LBRACK "[" +%token RBRACK "]" +%token LPAREN "(" +%token RPAREN ")" +%token PIPE "|" +%token EQ "=" +%token PERCENT "%" +%token SLASH "/" +%token COLON ":" +%token SEMICOLON ";" +%token COMMA "," +%token DASH "-" +%token PLUS "+" +%token STAR "*" +%token DOT "." +%token QUESTIONMARK "?" +%token BANG "!" +%token LETTER "[a-zA-Z]" +%token DIGIT "[0-9]" + +-- after langle_bang +%token CDATA_START "[CDATA[" +%token DOCTYPE "DOCTYPE" +%token ELEMENT "ELEMENT" +%token ATTLIST "ATTLIST" +%token ENTITY "ENTITY" +%token NOTATION "NOTATION" + +-- entity decl +%token SYSTEM "SYSTEM" +%token PUBLIC "PUBLIC" +%token NDATA "NDATA" + +-- default decl +%token REQUIRED "REQUIRED" +%token IMPLIED "IMPLIED" +%token FIXED "FIXED" + +-- conent spec +%token EMPTY "EMPTY" +%token ANY "ANY" +%token PCDATA "PCDATA" + +-- error +%token ERROR + +-- entities +%token PARSE_ENTITY +%token ENTITY_DONE +%token UNRESOLVED_ENTITY + +-- att type +%token CDATA "CDATA" +%token ID "ID" +%token IDREF "IDREF" +%token IDREFS "IDREFS" +%token ENTITY "ENTITY" +%token ENTITIES "ENTITIES" +%token NMTOKEN "NMTOKEN" +%token NMTOKENS "NMTOKENS" + +-- xml declaration +%token XML " class XmlStreamSimpleStack { + T *data; + int tos, cap; +public: + inline XmlStreamSimpleStack():data(0), tos(-1), cap(0){} + inline ~XmlStreamSimpleStack(){ if (data) qFree(data); } + + inline void reserve(int extraCapacity) { + if (tos + extraCapacity + 1 > cap) { + cap = qMax(tos + extraCapacity + 1, cap << 1 ); + data = reinterpret_cast(qRealloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); + } + } + + inline T &push() { reserve(1); return data[++tos]; } + inline T &rawPush() { return data[++tos]; } + inline const T &top() const { return data[tos]; } + inline T &top() { return data[tos]; } + inline T &pop() { return data[tos--]; } + inline T &operator[](int index) { return data[index]; } + inline const T &at(int index) const { return data[index]; } + inline int size() const { return tos + 1; } + inline void resize(int s) { tos = s - 1; } + inline bool isEmpty() const { return tos < 0; } + inline void clear() { tos = -1; } +}; + + +class XmlStream +{ + Q_DECLARE_TR_FUNCTIONS(XmlStream) +}; + +class XmlStreamPrivateTagStack { +public: + struct NamespaceDeclaration + { + QStringRef prefix; + QStringRef namespaceUri; + }; + + struct Tag + { + QStringRef name; + QStringRef qualifiedName; + NamespaceDeclaration namespaceDeclaration; + int tagStackStringStorageSize; + int namespaceDeclarationsSize; + }; + + + XmlStreamPrivateTagStack(); + XmlStreamSimpleStack namespaceDeclarations; + QString tagStackStringStorage; + int tagStackStringStorageSize; + bool tagsDone; + + inline QStringRef addToStringStorage(const QStringRef &s) { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.insert(pos, s.unicode(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + inline QStringRef addToStringStorage(const QString &s) { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.insert(pos, s.unicode(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + + XmlStreamSimpleStack tagStack; + + + inline Tag &tagStack_pop() { + Tag& tag = tagStack.pop(); + tagStackStringStorageSize = tag.tagStackStringStorageSize; + namespaceDeclarations.resize(tag.namespaceDeclarationsSize); + tagsDone = tagStack.isEmpty(); + return tag; + } + inline Tag &tagStack_push() { + Tag &tag = tagStack.push(); + tag.tagStackStringStorageSize = tagStackStringStorageSize; + tag.namespaceDeclarationsSize = namespaceDeclarations.size(); + return tag; + } +}; + + +class XmlStreamEntityResolver; +#ifndef QT_NO_XMLSTREAMREADER +class XmlStreamReaderPrivate : public XmlStreamReader_Table, public XmlStreamPrivateTagStack{ + XmlStreamReader *q_ptr; + Q_DECLARE_PUBLIC(XmlStreamReader) +public: + XmlStreamReaderPrivate(XmlStreamReader *q); + ~XmlStreamReaderPrivate(); + void init(); + + QByteArray rawReadBuffer; + QByteArray dataBuffer; + uchar firstByte; + qint64 nbytesread; + QString readBuffer; + int readBufferPos; + XmlStreamSimpleStack putStack; + struct Entity { + Entity(const QString& str = QString()) + :value(str), external(false), unparsed(false), literal(false), + hasBeenParsed(false), isCurrentlyReferenced(false){} + static inline Entity createLiteral(const QString &entity) + { Entity result(entity); result.literal = result.hasBeenParsed = true; return result; } + QString value; + uint external : 1; + uint unparsed : 1; + uint literal : 1; + uint hasBeenParsed : 1; + uint isCurrentlyReferenced : 1; + }; + QHash entityHash; + QHash parameterEntityHash; + XmlStreamSimpleStackentityReferenceStack; + inline bool referenceEntity(Entity &entity) { + if (entity.isCurrentlyReferenced) { + raiseWellFormedError(XmlStream::tr("Recursive entity detected.")); + return false; + } + entity.isCurrentlyReferenced = true; + entityReferenceStack.push() = &entity; + injectToken(ENTITY_DONE); + return true; + } + + + QIODevice *device; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextDecoder *decoder; +#endif + bool atEnd; + + /*! + \sa setType() + */ + XmlStreamReader::TokenType type; + XmlStreamReader::Error error; + QString errorString; + QString unresolvedEntity; + + qint64 lineNumber, lastLineStart, characterOffset; + + + void write(const QString &); + void write(const char *); + + + XmlStreamAttributes attributes; + QStringRef namespaceForPrefix(const QStringRef &prefix); + void resolveTag(); + void resolvePublicNamespaces(); + void resolveDtd(); + uint resolveCharRef(int symbolIndex); + bool checkStartDocument(); + void startDocument(); + void parseError(); + void checkPublicLiteral(const QStringRef &publicId); + + bool scanDtd; + QStringRef lastAttributeValue; + bool lastAttributeIsCData; + struct DtdAttribute { + QStringRef tagName; + QStringRef attributeQualifiedName; + QStringRef attributePrefix; + QStringRef attributeName; + QStringRef defaultValue; + bool isCDATA; + bool isNamespaceAttribute; + }; + XmlStreamSimpleStack dtdAttributes; + struct NotationDeclaration { + QStringRef name; + QStringRef publicId; + QStringRef systemId; + }; + XmlStreamSimpleStack notationDeclarations; + XmlStreamNotationDeclarations publicNotationDeclarations; + XmlStreamNamespaceDeclarations publicNamespaceDeclarations; + + struct EntityDeclaration { + QStringRef name; + QStringRef notationName; + QStringRef publicId; + QStringRef systemId; + QStringRef value; + bool parameter; + bool external; + inline void clear() { + name.clear(); + notationName.clear(); + publicId.clear(); + systemId.clear(); + value.clear(); + parameter = external = false; + } + }; + XmlStreamSimpleStack entityDeclarations; + XmlStreamEntityDeclarations publicEntityDeclarations; + + QStringRef text; + + QStringRef prefix, namespaceUri, qualifiedName, name; + QStringRef processingInstructionTarget, processingInstructionData; + QStringRef dtdName, dtdPublicId, dtdSystemId; + QStringRef documentVersion, documentEncoding; + uint isEmptyElement : 1; + uint isWhitespace : 1; + uint isCDATA : 1; + uint standalone : 1; + uint hasCheckedStartDocument : 1; + uint normalizeLiterals : 1; + uint hasSeenTag : 1; + uint inParseEntity : 1; + uint referenceToUnparsedEntityDetected : 1; + uint referenceToParameterEntityDetected : 1; + uint hasExternalDtdSubset : 1; + uint lockEncoding : 1; + uint namespaceProcessing : 1; + + int resumeReduction; + void resume(int rule); + + inline bool entitiesMustBeDeclared() const { + return (!inParseEntity + && (standalone + || (!referenceToUnparsedEntityDetected + && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 + && !hasExternalDtdSubset))); + } + + // qlalr parser + int tos; + int stack_size; + struct Value { + int pos; + int len; + int prefix; + ushort c; + }; + + Value *sym_stack; + int *state_stack; + inline void reallocateStack(); + inline Value &sym(int index) const + { return sym_stack[tos + index - 1]; } + QString textBuffer; + inline void clearTextBuffer() { + if (!scanDtd) { + textBuffer.resize(0); + textBuffer.reserve(256); + } + } + struct Attribute { + Value key; + Value value; + }; + XmlStreamSimpleStack attributeStack; + + inline QStringRef symString(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symString(int index, int offset) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); + } + inline QStringRef symPrefix(int index) { + const Value &symbol = sym(index); + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + inline QStringRef symString(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symPrefix(const Value &symbol) { + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + + inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } + + + short token; + ushort token_char; + + uint filterCarriageReturn(); + inline uint getChar(); + inline uint peekChar(); + inline void putChar(uint c) { putStack.push() = c; } + inline void putChar(QChar c) { putStack.push() = c.unicode(); } + void putString(const QString &s, int from = 0); + void putStringLiteral(const QString &s); + void putReplacement(const QString &s); + void putReplacementInAttributeValue(const QString &s); + ushort getChar_helper(); + + bool scanUntil(const char *str, short tokenToInject = -1); + bool scanString(const char *str, short tokenToInject, bool requireSpace = true); + inline void injectToken(ushort tokenToInject) { + putChar(int(tokenToInject) << 16); + } + + QString resolveUndeclaredEntity(const QString &name); + void parseEntity(const QString &value); + XmlStreamReaderPrivate *entityParser; + + bool scanAfterLangleBang(); + bool scanPublicOrSystem(); + bool scanNData(); + bool scanAfterDefaultDecl(); + bool scanAttType(); + + + // scan optimization functions. Not strictly necessary but LALR is + // not very well suited for scanning fast + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); + int fastScanName(int *prefix = 0); + inline int fastScanNMTOKEN(); + + + bool parse(); + inline void consumeRule(int); + + void raiseError(XmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); + + XmlStreamEntityResolver *entityResolver; + +private: + /*! \internal + Never assign to variable type directly. Instead use this function. + + This prevents errors from being ignored. + */ + inline void setType(const XmlStreamReader::TokenType t) + { + if(type != XmlStreamReader::Invalid) + type = t; + } +}; + +bool XmlStreamReaderPrivate::parse() +{ + // cleanup currently reported token + + switch (type) { + case XmlStreamReader::StartElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + if (publicNamespaceDeclarations.size()) + publicNamespaceDeclarations.clear(); + if (attributes.size()) + attributes.resize(0); + if (isEmptyElement) { + setType(XmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + isEmptyElement = false; + return true; + } + clearTextBuffer(); + break; + case XmlStreamReader::EndElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::DTD: + publicNotationDeclarations.clear(); + publicEntityDeclarations.clear(); + dtdName.clear(); + dtdPublicId.clear(); + dtdSystemId.clear(); + // fall through + case XmlStreamReader::Comment: + case XmlStreamReader::Characters: + isCDATA = false; + isWhitespace = true; + text.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::EntityReference: + text.clear(); + name.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::ProcessingInstruction: + processingInstructionTarget.clear(); + processingInstructionData.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::NoToken: + case XmlStreamReader::Invalid: + break; + case XmlStreamReader::StartDocument: + lockEncoding = true; + documentVersion.clear(); + documentEncoding.clear(); +#ifndef QT_NO_TEXTCODEC + if(decoder->hasFailure()) { + raiseWellFormedError(XmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return false; + } +#endif + // fall through + default: + clearTextBuffer(); + ; + } + + setType(XmlStreamReader::NoToken); + + + // the main parse loop + int act, r; + + if (resumeReduction) { + act = state_stack[tos-1]; + r = resumeReduction; + resumeReduction = 0; + goto ResumeReduction; + } + + act = state_stack[tos]; + + forever { + if (token == -1 && - TERMINAL_COUNT != action_index[act]) { + uint cu = getChar(); + token = NOTOKEN; + token_char = cu; + if (cu & 0xff0000) { + token = cu >> 16; + } else switch (token_char) { + case 0xfffe: + case 0xffff: + token = ERROR; + break; + case '\r': + token = SPACE; + if (cu == '\r') { + if ((token_char = filterCarriageReturn())) { + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + break; + } + } else { + break; + } + // fall through + case '\0': { + token = EOF_SYMBOL; + if (!tagsDone && !inParseEntity) { + int a = t_action(act, token); + if (a < 0) { + raiseError(XmlStreamReader::PrematureEndOfDocumentError); + return false; + } + } + + } break; + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + case ' ': + case '\t': + token = SPACE; + break; + case '&': + token = AMPERSAND; + break; + case '#': + token = HASH; + break; + case '\'': + token = QUOTE; + break; + case '\"': + token = DBLQUOTE; + break; + case '<': + token = LANGLE; + break; + case '>': + token = RANGLE; + break; + case '[': + token = LBRACK; + break; + case ']': + token = RBRACK; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '|': + token = PIPE; + break; + case '=': + token = EQ; + break; + case '%': + token = PERCENT; + break; + case '/': + token = SLASH; + break; + case ':': + token = COLON; + break; + case ';': + token = SEMICOLON; + break; + case ',': + token = COMMA; + break; + case '-': + token = DASH; + break; + case '+': + token = PLUS; + break; + case '*': + token = STAR; + break; + case '.': + token = DOT; + break; + case '?': + token = QUESTIONMARK; + break; + case '!': + token = BANG; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token = DIGIT; + break; + case 0x3: + token = LETTER; + break; + default: + if (cu < 0x20) + token = NOTOKEN; + else + token = LETTER; + break; + } + } + + act = t_action (act, token); + if (act == ACCEPT_STATE) { + // reset the parser in case someone resumes (process instructions can follow a valid document) + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return true; + } else if (act > 0) { + if (++tos == stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; + val.c = token_char; + val.pos = textBuffer.size(); + val.prefix = 0; + val.len = 1; + if (token_char) + textBuffer += QChar(token_char); + + state_stack[tos] = act; + token = -1; + + + } else if (act < 0) { + r = - act - 1; + +#if defined (QLALR_DEBUG) + int ridx = rule_index[r]; + printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); + ++ridx; + for (int i = ridx; i < ridx + rhs[r]; ++i) { + int symbol = rule_info[i]; + if (const char *name = spell[symbol]) + printf (" %s", name); + else + printf (" #%d", symbol); + } + printf ("\n"); +#endif + + tos -= rhs[r]; + act = state_stack[tos++]; + ResumeReduction: + switch (r) { +./ + +document ::= PARSE_ENTITY content; +/. + case $rule_number: + setType(XmlStreamReader::EndDocument); + break; +./ + +document ::= prolog; +/. + case $rule_number: + if (type != XmlStreamReader::Invalid) { + if (hasSeenTag || inParseEntity) { + setType(XmlStreamReader::EndDocument); + } else { + raiseError(XmlStreamReader::NotWellFormedError, XmlStream::tr("Start tag expected.")); + // reset the parser + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return false; + } + } + break; +./ + + +prolog ::= prolog stag content etag; +prolog ::= prolog empty_element_tag; +prolog ::= prolog comment; +prolog ::= prolog xml_decl; +prolog ::= prolog processing_instruction; +prolog ::= prolog doctype_decl; +prolog ::= prolog SPACE; +prolog ::=; + +entity_done ::= ENTITY_DONE; +/. + case $rule_number: + entityReferenceStack.pop()->isCurrentlyReferenced = false; + clearSym(); + break; +./ + + +xml_decl_start ::= XML; +/. + case $rule_number: + if (!scanString(spell[VERSION], VERSION, false) && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +xml_decl ::= xml_decl_start VERSION space_opt EQ space_opt literal attribute_list_opt QUESTIONMARK RANGLE; +/. + case $rule_number: + setType(XmlStreamReader::StartDocument); + documentVersion = symString(6); + startDocument(); + break; +./ + +external_id ::= SYSTEM literal; +/. + case $rule_number: + hasExternalDtdSubset = true; + dtdSystemId = symString(2); + break; +./ +external_id ::= PUBLIC public_literal space literal; +/. + case $rule_number: + checkPublicLiteral(symString(2)); + dtdPublicId = symString(2); + dtdSystemId = symString(4); + hasExternalDtdSubset = true; + break; +./ +external_id ::=; + +doctype_decl_start ::= langle_bang DOCTYPE qname space; +/. + case $rule_number: + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + dtdName = symString(3); + break; +./ + +doctype_decl ::= langle_bang DOCTYPE qname RANGLE; +/. + case $rule_number:./ +doctype_decl ::= langle_bang DOCTYPE qname markup space_opt RANGLE; +/. + case $rule_number: + dtdName = symString(3); + // fall through +./ +doctype_decl ::= doctype_decl_start external_id space_opt markup space_opt RANGLE; +/. + case $rule_number:./ +doctype_decl ::= doctype_decl_start external_id space_opt RANGLE; +/. + case $rule_number: + setType(XmlStreamReader::DTD); + text = &textBuffer; + break; +./ + +markup_start ::= LBRACK; +/. + case $rule_number: + scanDtd = true; + break; +./ + +markup ::= markup_start markup_list RBRACK; +/. + case $rule_number: + scanDtd = false; + break; +./ + + +markup_list ::= markup_decl | space | pereference; +markup_list ::= markup_list markup_decl | markup_list space | markup_list pereference; +markup_list ::=; + +markup_decl ::= element_decl | attlist_decl | entity_decl | entity_done | notation_decl | processing_instruction | comment; + + +element_decl_start ::= langle_bang ELEMENT qname space; +/. + case $rule_number: + if (!scanString(spell[EMPTY], EMPTY, false) + && !scanString(spell[ANY], ANY, false) + && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +element_decl ::= element_decl_start content_spec space_opt RANGLE; + + +content_spec ::= EMPTY | ANY | mixed | children; + +pcdata_start ::= HASH; +/. + case $rule_number: + if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +pcdata ::= pcdata_start PCDATA; + +questionmark_or_star_or_plus_opt ::= QUESTIONMARK | STAR | PLUS; +questionmark_or_star_or_plus_opt ::=; + +cp ::= qname questionmark_or_star_or_plus_opt | choice_or_seq questionmark_or_star_or_plus_opt; + +cp_pipe_or_comma_list ::= cp space_opt; +cp_pipe_or_comma_list ::= cp space_opt PIPE space_opt cp_pipe_list space_opt; +cp_pipe_or_comma_list ::= cp space_opt COMMA space_opt cp_comma_list space_opt; +cp_pipe_list ::= cp | cp_pipe_list space_opt PIPE space_opt cp; +cp_comma_list ::= cp | cp_comma_list space_opt COMMA space_opt cp; + + +name_pipe_list ::= PIPE space_opt qname; +name_pipe_list ::= name_pipe_list space_opt PIPE space_opt qname; + +star_opt ::= | STAR; + +mixed ::= LPAREN space_opt pcdata space_opt RPAREN star_opt; +mixed ::= LPAREN space_opt pcdata space_opt name_pipe_list space_opt RPAREN STAR; + +choice_or_seq ::= LPAREN space_opt cp_pipe_or_comma_list RPAREN; + +children ::= choice_or_seq questionmark_or_star_or_plus_opt; + + +nmtoken_pipe_list ::= nmtoken; +nmtoken_pipe_list ::= nmtoken_pipe_list space_opt PIPE space_opt nmtoken; + + +att_type ::= CDATA; +/. + case $rule_number: { + lastAttributeIsCData = true; + } break; +./ +att_type ::= ID | IDREF | IDREFS | ENTITY | ENTITIES | NMTOKEN | NMTOKENS; +att_type ::= LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; +att_type ::= NOTATION LPAREN space_opt nmtoken_pipe_list space_opt RPAREN space; + + +default_declhash ::= HASH; +/. + case $rule_number: + if (!scanAfterDefaultDecl() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +default_decl ::= default_declhash REQUIRED; +default_decl ::= default_declhash IMPLIED; +default_decl ::= attribute_value; +default_decl ::= default_declhash FIXED space attribute_value; +attdef_start ::= space qname space; +/. + case $rule_number: + sym(1) = sym(2); + lastAttributeValue.clear(); + lastAttributeIsCData = false; + if (!scanAttType() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +attdef ::= attdef_start att_type default_decl; +/. + case $rule_number: { + DtdAttribute &dtdAttribute = dtdAttributes.push(); + dtdAttribute.tagName.clear(); + dtdAttribute.isCDATA = lastAttributeIsCData; + dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); + dtdAttribute.attributeName = addToStringStorage(symString(1)); + dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); + dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") + || (dtdAttribute.attributePrefix.isEmpty() + && dtdAttribute.attributeName == QLatin1String("xmlns"))); + if (lastAttributeValue.isNull()) { + dtdAttribute.defaultValue.clear(); + } else { + if (dtdAttribute.isCDATA) + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); + else + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); + + } + } break; +./ + +attdef_list ::= attdef; +attdef_list ::= attdef_list attdef; + +attlist_decl ::= langle_bang ATTLIST qname space_opt RANGLE; +attlist_decl ::= langle_bang ATTLIST qname attdef_list space_opt RANGLE; +/. + case $rule_number: { + if (referenceToUnparsedEntityDetected && !standalone) + break; + int n = dtdAttributes.size(); + QStringRef tagName = addToStringStorage(symName(3)); + while (n--) { + DtdAttribute &dtdAttribute = dtdAttributes[n]; + if (!dtdAttribute.tagName.isNull()) + break; + dtdAttribute.tagName = tagName; + for (int i = 0; i < n; ++i) { + if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) + && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { + dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it + break; + } + } + } + } break; +./ + +entity_decl_start ::= langle_bang ENTITY name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(3); + } break; +./ + +entity_decl_start ::= langle_bang ENTITY PERCENT space name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(5); + entityDeclaration.parameter = true; + } break; +./ + +entity_decl_external ::= entity_decl_start SYSTEM literal; +/. + case $rule_number: { + if (!scanNData() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.systemId = symString(3); + entityDeclaration.external = true; + } break; +./ + +entity_decl_external ::= entity_decl_start PUBLIC public_literal space literal; +/. + case $rule_number: { + if (!scanNData() && atEnd) { + resume($rule_number); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + checkPublicLiteral((entityDeclaration.publicId = symString(3))); + entityDeclaration.systemId = symString(5); + entityDeclaration.external = true; + } break; +./ + +entity_decl ::= entity_decl_external NDATA name space_opt RANGLE; +/. + case $rule_number: { + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.notationName = symString(3); + if (entityDeclaration.parameter) + raiseWellFormedError(XmlStream::tr("NDATA in parameter entity declaration.")); + } + //fall through +./ + +entity_decl ::= entity_decl_external space_opt RANGLE; +/. + case $rule_number:./ + +entity_decl ::= entity_decl_start entity_value space_opt RANGLE; +/. + case $rule_number: { + if (referenceToUnparsedEntityDetected && !standalone) { + entityDeclarations.pop(); + break; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + if (!entityDeclaration.external) + entityDeclaration.value = symString(2); + QString entityName = entityDeclaration.name.toString(); + QHash &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; + if (!hash.contains(entityName)) { + Entity entity(entityDeclaration.value.toString()); + entity.unparsed = (!entityDeclaration.notationName.isNull()); + entity.external = entityDeclaration.external; + hash.insert(entityName, entity); + } + } break; +./ + + +processing_instruction ::= LANGLE QUESTIONMARK name space; +/. + case $rule_number: { + setType(XmlStreamReader::ProcessingInstruction); + int pos = sym(4).pos + sym(4).len; + processingInstructionTarget = symString(3); + if (scanUntil("?>")) { + processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); + const QString piTarget(processingInstructionTarget.toString()); + if (!piTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { + raiseWellFormedError(XmlStream::tr("XML declaration not at start of document.")); + } + else if(!XmlUtils::isNCName(piTarget)) + raiseWellFormedError(XmlStream::tr("%1 is an invalid processing instruction name.").arg(piTarget)); + } else if (type != XmlStreamReader::Invalid){ + resume($rule_number); + return false; + } + } break; +./ + +processing_instruction ::= LANGLE QUESTIONMARK name QUESTIONMARK RANGLE; +/. + case $rule_number: + setType(XmlStreamReader::ProcessingInstruction); + processingInstructionTarget = symString(3); + if (!processingInstructionTarget.toString().compare(QLatin1String("xml"), Qt::CaseInsensitive)) + raiseWellFormedError(XmlStream::tr("Invalid processing instruction name.")); + break; +./ + + +langle_bang ::= LANGLE BANG; +/. + case $rule_number: + if (!scanAfterLangleBang() && atEnd) { + resume($rule_number); + return false; + } + break; +./ + +comment_start ::= langle_bang DASH DASH; +/. + case $rule_number: + if (!scanUntil("--")) { + resume($rule_number); + return false; + } + break; +./ + +comment ::= comment_start RANGLE; +/. + case $rule_number: { + setType(XmlStreamReader::Comment); + int pos = sym(1).pos + 4; + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } break; +./ + + +cdata ::= langle_bang CDATA_START; +/. + case $rule_number: { + setType(XmlStreamReader::Characters); + isCDATA = true; + isWhitespace = false; + int pos = sym(2).pos; + if (scanUntil("]]>", -1)) { + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } else { + resume($rule_number); + return false; + } + } break; +./ + +notation_decl_start ::= langle_bang NOTATION name space; +/. + case $rule_number: { + if (!scanPublicOrSystem() && atEnd) { + resume($rule_number); + return false; + } + NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); + notationDeclaration.name = symString(3); + } break; +./ + +notation_decl ::= notation_decl_start SYSTEM literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId = symString(3); + notationDeclaration.publicId.clear(); + } break; +./ + +notation_decl ::= notation_decl_start PUBLIC public_literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId.clear(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + } break; +./ + +notation_decl ::= notation_decl_start PUBLIC public_literal space literal space_opt RANGLE; +/. + case $rule_number: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + notationDeclaration.systemId = symString(5); + } break; +./ + + + +content_char ::= RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG | QUOTE | DBLQUOTE | LETTER | DIGIT; + +scan_content_char ::= content_char; +/. + case $rule_number: + isWhitespace = false; + // fall through +./ + +scan_content_char ::= SPACE; +/. + case $rule_number: + sym(1).len += fastScanContentCharList(); + if (atEnd && !inParseEntity) { + resume($rule_number); + return false; + } + break; +./ + +content_char_list ::= content_char_list char_ref; +content_char_list ::= content_char_list entity_ref; +content_char_list ::= content_char_list entity_done; +content_char_list ::= content_char_list scan_content_char; +content_char_list ::= char_ref; +content_char_list ::= entity_ref; +content_char_list ::= entity_done; +content_char_list ::= scan_content_char; + + +character_content ::= content_char_list %prec SHIFT_THERE; +/. + case $rule_number: + if (!textBuffer.isEmpty()) { + setType(XmlStreamReader::Characters); + text = &textBuffer; + } + break; +./ + +literal ::= QUOTE QUOTE; +/. + case $rule_number:./ +literal ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ +literal ::= QUOTE literal_content_with_dblquote QUOTE; +/. + case $rule_number:./ +literal ::= DBLQUOTE literal_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + break; +./ + +literal_content_with_dblquote ::= literal_content_with_dblquote literal_content; +/. + case $rule_number:./ +literal_content_with_quote ::= literal_content_with_quote literal_content; +/. + case $rule_number:./ +literal_content_with_dblquote ::= literal_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +literal_content_with_quote ::= literal_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +literal_content_with_dblquote ::= literal_content; +literal_content_with_quote ::= literal_content; +literal_content_with_dblquote ::= DBLQUOTE; +literal_content_with_quote ::= QUOTE; + +literal_content_start ::= LETTER | DIGIT | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | PERCENT | SLASH | COLON | SEMICOLON | COMMA | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; + +literal_content_start ::= SPACE; +/. + case $rule_number: + if (normalizeLiterals) + textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); + break; +./ + +literal_content ::= literal_content_start; +/. + case $rule_number: + sym(1).len += fastScanLiteralContent(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + + +public_literal ::= literal; +/. + case $rule_number: { + if (!XmlUtils::isPublicID(symString(1).toString())) { + raiseWellFormedError(XmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1).toString())); + resume($rule_number); + return false; + } + } break; +./ + +entity_value ::= QUOTE QUOTE; +/. + case $rule_number:./ +entity_value ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ + +entity_value ::= QUOTE entity_value_content_with_dblquote QUOTE; +/. + case $rule_number:./ +entity_value ::= DBLQUOTE entity_value_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + break; +./ + +entity_value_content_with_dblquote ::= entity_value_content_with_dblquote entity_value_content; +/. + case $rule_number:./ +entity_value_content_with_quote ::= entity_value_content_with_quote entity_value_content; +/. + case $rule_number:./ +entity_value_content_with_dblquote ::= entity_value_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +entity_value_content_with_quote ::= entity_value_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +entity_value_content_with_dblquote ::= entity_value_content; +entity_value_content_with_quote ::= entity_value_content; +entity_value_content_with_dblquote ::= DBLQUOTE; +entity_value_content_with_quote ::= QUOTE; + +entity_value_content ::= LETTER | DIGIT | LANGLE | RANGLE | HASH | LBRACK | RBRACK | LPAREN | RPAREN | PIPE | EQ | SLASH | COLON | SEMICOLON | COMMA | SPACE | DASH | PLUS | STAR | DOT | QUESTIONMARK | BANG; +entity_value_content ::= char_ref | entity_ref_in_entity_value | entity_done; + + +attribute_value ::= QUOTE QUOTE; +/. + case $rule_number:./ +attribute_value ::= DBLQUOTE DBLQUOTE; +/. + case $rule_number: + clearSym(); + break; +./ +attribute_value ::= QUOTE attribute_value_content_with_dblquote QUOTE; +/. + case $rule_number:./ +attribute_value ::= DBLQUOTE attribute_value_content_with_quote DBLQUOTE; +/. + case $rule_number: + sym(1) = sym(2); + lastAttributeValue = symString(1); + break; +./ + +attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote attribute_value_content; +/. + case $rule_number:./ +attribute_value_content_with_quote ::= attribute_value_content_with_quote attribute_value_content; +/. + case $rule_number:./ +attribute_value_content_with_dblquote ::= attribute_value_content_with_dblquote DBLQUOTE; +/. + case $rule_number:./ +attribute_value_content_with_quote ::= attribute_value_content_with_quote QUOTE; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ +attribute_value_content_with_dblquote ::= attribute_value_content | DBLQUOTE; +attribute_value_content_with_quote ::= attribute_value_content | QUOTE; + +attribute_value_content ::= literal_content | char_ref | entity_ref_in_attribute_value | entity_done; + +attribute ::= qname space_opt EQ space_opt attribute_value; +/. + case $rule_number: { + QStringRef prefix = symPrefix(1); + if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(symString(5)); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(XmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = addToStringStorage(ns); + } else { + Attribute &attribute = attributeStack.push(); + attribute.key = sym(1); + attribute.value = sym(5); + + QStringRef attributeQualifiedName = symName(1); + bool normalize = false; + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isCDATA + && dtdAttribute.tagName == qualifiedName + && dtdAttribute.attributeQualifiedName == attributeQualifiedName + ) { + normalize = true; + break; + } + } + if (normalize) { + // normalize attribute value (simplify and trim) + int pos = textBuffer.size(); + int n = 0; + bool wasSpace = true; + for (int i = 0; i < attribute.value.len; ++i) { + QChar c = textBuffer.at(attribute.value.pos + i); + if (c.unicode() == ' ') { + if (wasSpace) + continue; + wasSpace = true; + } else { + wasSpace = false; + } + textBuffer += textBuffer.at(attribute.value.pos + i); + ++n; + } + if (wasSpace) + while (n && textBuffer.at(pos + n - 1).unicode() == ' ') + --n; + attribute.value.pos = pos; + attribute.value.len = n; + } + if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = symString(attribute.key); + QStringRef namespaceUri = symString(attribute.value); + attributeStack.pop(); + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(XmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + } + } + } break; +./ + + + +attribute_list_opt ::= | space | space attribute_list space_opt; +attribute_list ::= attribute | attribute_list space attribute; + +stag_start ::= LANGLE qname; +/. + case $rule_number: { + normalizeLiterals = true; + Tag &tag = tagStack_push(); + prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); + name = tag.name = addToStringStorage(symString(2)); + qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); + if ((!prefix.isEmpty() && !XmlUtils::isNCName(prefix)) || !XmlUtils::isNCName(name)) + raiseWellFormedError(XmlStream::tr("Invalid XML name.")); + } break; +./ + + +empty_element_tag ::= stag_start attribute_list_opt SLASH RANGLE; +/. + case $rule_number: + isEmptyElement = true; + // fall through +./ + + +stag ::= stag_start attribute_list_opt RANGLE; +/. + case $rule_number: + setType(XmlStreamReader::StartElement); + resolveTag(); + if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) + raiseWellFormedError(XmlStream::tr("Extra content at end of document.")); + hasSeenTag = true; + break; +./ + + +etag ::= LANGLE SLASH qname space_opt RANGLE; +/. + case $rule_number: { + setType(XmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + if (qualifiedName != symName(3)) + raiseWellFormedError(XmlStream::tr("Opening and ending tag mismatch.")); + } break; +./ + + +unresolved_entity ::= UNRESOLVED_ENTITY; +/. + case $rule_number: + if (entitiesMustBeDeclared()) { + raiseWellFormedError(XmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); + break; + } + setType(XmlStreamReader::EntityReference); + name = &unresolvedEntity; + break; +./ + +entity_ref ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QString reference = symString(2).toString(); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed) { + raiseWellFormedError(XmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); + } else { + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacement(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + + injectToken(UNRESOLVED_ENTITY); + unresolvedEntity = symString(2).toString(); + textBuffer.chop(2 + sym(2).len); + clearSym(); + + } break; +./ + +pereference ::= PERCENT name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QString reference = symString(2).toString(); + if (parameterEntityHash.contains(reference)) { + referenceToParameterEntityDetected = true; + Entity &entity = parameterEntityHash[reference]; + if (entity.unparsed || entity.external) { + referenceToUnparsedEntityDetected = true; + } else { + if (referenceEntity(entity)) + putString(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + } else if (entitiesMustBeDeclared()) { + raiseWellFormedError(XmlStream::tr("Entity '%1' not declared.").arg(symString(2).toString())); + } + } break; +./ + + + +entity_ref_in_entity_value ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: + sym(1).len += sym(2).len + 1; + break; +./ + +entity_ref_in_attribute_value ::= AMPERSAND name SEMICOLON; +/. + case $rule_number: { + sym(1).len += sym(2).len + 1; + QString reference = symString(2).toString(); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed || entity.value.isNull()) { + raiseWellFormedError(XmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); + break; + } + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacementInAttributeValue(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + if (entitiesMustBeDeclared()) { + raiseWellFormedError(XmlStream::tr("Entity '%1' not declared.").arg(reference)); + } + } break; +./ + +char_ref ::= AMPERSAND HASH char_ref_value SEMICOLON; +/. + case $rule_number: { + if (uint s = resolveCharRef(3)) { + if (s >= 0xffff) + putStringLiteral(QString::fromUcs4(&s, 1)); + else + putChar((LETTER << 16) | s); + + textBuffer.chop(3 + sym(3).len); + clearSym(); + } else { + raiseWellFormedError(XmlStream::tr("Invalid character reference.")); + } + } break; +./ + + +char_ref_value ::= LETTER | DIGIT; +char_ref_value ::= char_ref_value LETTER; +/. + case $rule_number:./ +char_ref_value ::= char_ref_value DIGIT; +/. + case $rule_number: + sym(1).len += sym(2).len; + break; +./ + + +content ::= content character_content; +content ::= content stag content etag; +content ::= content empty_element_tag; +content ::= content comment; +content ::= content cdata; +content ::= content xml_decl; +content ::= content processing_instruction; +content ::= content doctype_decl; +content ::= content unresolved_entity; +content ::= ; + + +space ::= SPACE; +/. + case $rule_number: + sym(1).len += fastScanSpace(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + + +space_opt ::=; +space_opt ::= space; + +qname ::= LETTER; +/. + case $rule_number: { + sym(1).len += fastScanName(&sym(1).prefix); + if (atEnd) { + resume($rule_number); + return false; + } + } break; +./ + +name ::= LETTER; +/. + case $rule_number: + sym(1).len += fastScanName(); + if (atEnd) { + resume($rule_number); + return false; + } + break; +./ + +nmtoken ::= LETTER; +/. + case $rule_number:./ +nmtoken ::= DIGIT; +/. + case $rule_number:./ +nmtoken ::= DOT; +/. + case $rule_number:./ +nmtoken ::= DASH; +/. + case $rule_number:./ +nmtoken ::= COLON; +/. + case $rule_number: + sym(1).len += fastScanNMTOKEN(); + if (atEnd) { + resume($rule_number); + return false; + } + + break; +./ + + +/. + default: + ; + } // switch + act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); + if (type != XmlStreamReader::NoToken) + return true; + } else { + parseError(); + break; + } + } + return false; +} +#endif //QT_NO_XMLSTREAMREADER.xml + +./ diff --git a/xml/qxmlstream.h b/xml/qxmlstream.h new file mode 100644 index 0000000000..10c57a5fca --- /dev/null +++ b/xml/qxmlstream.h @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __QXMLSTREAM_H__ +#define __QXMLSTREAM_H__ + +#include +#include +#include +#include + +class XmlStreamStringRef { + QString m_string; + int m_position, m_size; +public: + inline XmlStreamStringRef():m_position(0), m_size(0){} + inline XmlStreamStringRef(const QStringRef &aString) + :m_string(aString.string()?*aString.string():QString()), m_position(aString.position()), m_size(aString.size()){} + inline XmlStreamStringRef(const QString &aString):m_string(aString), m_position(0), m_size(aString.size()){} + inline ~XmlStreamStringRef(){} + inline void clear() { m_string.clear(); m_position = m_size = 0; } + inline operator QStringRef() const { return QStringRef(&m_string, m_position, m_size); } + inline const QString *string() const { return &m_string; } + inline int position() const { return m_position; } + inline int size() const { return m_size; } +}; + + +class XmlStreamReaderPrivate; +class XmlStreamAttributes; +class Q_XMLSTREAM_EXPORT XmlStreamAttribute { + XmlStreamStringRef m_name, m_namespaceUri, m_qualifiedName, m_value; + void *reserved; + uint m_isDefault : 1; + friend class XmlStreamReaderPrivate; + friend class XmlStreamAttributes; +public: + XmlStreamAttribute(); + XmlStreamAttribute(const QString &qualifiedName, const QString &value); + XmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value); + XmlStreamAttribute(const XmlStreamAttribute &); + XmlStreamAttribute& operator=(const XmlStreamAttribute &); + ~XmlStreamAttribute(); + inline QStringRef namespaceUri() const { return m_namespaceUri; } + inline QStringRef name() const { return m_name; } + inline QStringRef qualifiedName() const { return m_qualifiedName; } + inline QStringRef prefix() const { + return QStringRef(m_qualifiedName.string(), + m_qualifiedName.position(), + qMax(0, m_qualifiedName.size() - m_name.size() - 1)); + } + inline QStringRef value() const { return m_value; } + inline bool isDefault() const { return m_isDefault; } + inline bool operator==(const XmlStreamAttribute &other) const { + return (value() == other.value() + && (namespaceUri().isNull() ? (qualifiedName() == other.qualifiedName()) + : (namespaceUri() == other.namespaceUri() && name() == other.name()))); + } + inline bool operator!=(const XmlStreamAttribute &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(XmlStreamAttribute, Q_MOVABLE_TYPE); + +class Q_XMLSTREAM_EXPORT XmlStreamAttributes : public QVector +{ +public: + inline XmlStreamAttributes() {} + QStringRef value(const QString &namespaceUri, const QString &name) const; + QStringRef value(const QString &namespaceUri, const QLatin1String &name) const; + QStringRef value(const QLatin1String &namespaceUri, const QLatin1String &name) const; + QStringRef value(const QString &qualifiedName) const; + QStringRef value(const QLatin1String &qualifiedName) const; + void append(const QString &namespaceUri, const QString &name, const QString &value); + void append(const QString &qualifiedName, const QString &value); + + inline bool hasAttribute(const QString &qualifiedName) const + { + return !value(qualifiedName).isNull(); + } + + inline bool hasAttribute(const QLatin1String &qualifiedName) const + { + return !value(qualifiedName).isNull(); + } + + inline bool hasAttribute(const QString &namespaceUri, const QString &name) const + { + return !value(namespaceUri, name).isNull(); + } + +#if !defined(Q_NO_USING_KEYWORD) + using QVector::append; +#else + inline void append(const XmlStreamAttribute &attribute) + { QVector::append(attribute); } +#endif +}; + +class Q_XMLSTREAM_EXPORT XmlStreamNamespaceDeclaration { + XmlStreamStringRef m_prefix, m_namespaceUri; + void *reserved; + + friend class XmlStreamReaderPrivate; +public: + XmlStreamNamespaceDeclaration(); + XmlStreamNamespaceDeclaration(const XmlStreamNamespaceDeclaration &); + XmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri); + ~XmlStreamNamespaceDeclaration(); + XmlStreamNamespaceDeclaration& operator=(const XmlStreamNamespaceDeclaration &); + inline QStringRef prefix() const { return m_prefix; } + inline QStringRef namespaceUri() const { return m_namespaceUri; } + inline bool operator==(const XmlStreamNamespaceDeclaration &other) const { + return (prefix() == other.prefix() && namespaceUri() == other.namespaceUri()); + } + inline bool operator!=(const XmlStreamNamespaceDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(XmlStreamNamespaceDeclaration, Q_MOVABLE_TYPE); +typedef QVector XmlStreamNamespaceDeclarations; + +class Q_XMLSTREAM_EXPORT XmlStreamNotationDeclaration { + XmlStreamStringRef m_name, m_systemId, m_publicId; + void *reserved; + + friend class XmlStreamReaderPrivate; +public: + XmlStreamNotationDeclaration(); + ~XmlStreamNotationDeclaration(); + XmlStreamNotationDeclaration(const XmlStreamNotationDeclaration &); + XmlStreamNotationDeclaration& operator=(const XmlStreamNotationDeclaration &); + inline QStringRef name() const { return m_name; } + inline QStringRef systemId() const { return m_systemId; } + inline QStringRef publicId() const { return m_publicId; } + inline bool operator==(const XmlStreamNotationDeclaration &other) const { + return (name() == other.name() && systemId() == other.systemId() + && publicId() == other.publicId()); + } + inline bool operator!=(const XmlStreamNotationDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(XmlStreamNotationDeclaration, Q_MOVABLE_TYPE); +typedef QVector XmlStreamNotationDeclarations; + +class Q_XMLSTREAM_EXPORT XmlStreamEntityDeclaration { + XmlStreamStringRef m_name, m_notationName, m_systemId, m_publicId, m_value; + void *reserved; + + friend class XmlStreamReaderPrivate; +public: + XmlStreamEntityDeclaration(); + ~XmlStreamEntityDeclaration(); + XmlStreamEntityDeclaration(const XmlStreamEntityDeclaration &); + XmlStreamEntityDeclaration& operator=(const XmlStreamEntityDeclaration &); + inline QStringRef name() const { return m_name; } + inline QStringRef notationName() const { return m_notationName; } + inline QStringRef systemId() const { return m_systemId; } + inline QStringRef publicId() const { return m_publicId; } + inline QStringRef value() const { return m_value; } + inline bool operator==(const XmlStreamEntityDeclaration &other) const { + return (name() == other.name() + && notationName() == other.notationName() + && systemId() == other.systemId() + && publicId() == other.publicId() + && value() == other.value()); + } + inline bool operator!=(const XmlStreamEntityDeclaration &other) const + { return !operator==(other); } +}; + +Q_DECLARE_TYPEINFO(XmlStreamEntityDeclaration, Q_MOVABLE_TYPE); +typedef QVector XmlStreamEntityDeclarations; + + +class Q_XMLSTREAM_EXPORT XmlStreamEntityResolver +{ +public: + virtual ~XmlStreamEntityResolver(); + virtual QString resolveEntity(const QString& publicId, const QString& systemId); + virtual QString resolveUndeclaredEntity(const QString &name); +}; + +class XmlStreamReader { + QDOC_PROPERTY(bool namespaceProcessing READ namespaceProcessing WRITE setNamespaceProcessing) +public: + enum TokenType { + NoToken = 0, + Invalid, + StartDocument, + EndDocument, + StartElement, + EndElement, + Characters, + Comment, + DTD, + EntityReference, + ProcessingInstruction + }; + + + XmlStreamReader(); + XmlStreamReader(QIODevice *device); + XmlStreamReader(const QByteArray &data); + XmlStreamReader(const QString &data); + XmlStreamReader(const char * data); + ~XmlStreamReader(); + + void setDevice(QIODevice *device); + QIODevice *device() const; + void addData(const QByteArray &data); + void addData(const QString &data); + void addData(const char *data); + void clear(); + + + bool atEnd() const; + TokenType readNext(); + + bool readNextStartElement(); + void skipCurrentElement(); + + TokenType tokenType() const; + QString tokenString() const; + + void setNamespaceProcessing(bool); + bool namespaceProcessing() const; + + inline bool isStartDocument() const { return tokenType() == StartDocument; } + inline bool isEndDocument() const { return tokenType() == EndDocument; } + inline bool isStartElement() const { return tokenType() == StartElement; } + inline bool isEndElement() const { return tokenType() == EndElement; } + inline bool isCharacters() const { return tokenType() == Characters; } + bool isWhitespace() const; + bool isCDATA() const; + inline bool isComment() const { return tokenType() == Comment; } + inline bool isDTD() const { return tokenType() == DTD; } + inline bool isEntityReference() const { return tokenType() == EntityReference; } + inline bool isProcessingInstruction() const { return tokenType() == ProcessingInstruction; } + + bool isStandaloneDocument() const; + QStringRef documentVersion() const; + QStringRef documentEncoding() const; + + qint64 lineNumber() const; + qint64 columnNumber() const; + qint64 characterOffset() const; + + XmlStreamAttributes attributes() const; + + enum ReadElementTextBehaviour { + ErrorOnUnexpectedElement, + IncludeChildElements, + SkipChildElements + }; + QString readElementText(ReadElementTextBehaviour behaviour); + QString readElementText(); + + QStringRef name() const; + QStringRef namespaceUri() const; + QStringRef qualifiedName() const; + QStringRef prefix() const; + + QStringRef processingInstructionTarget() const; + QStringRef processingInstructionData() const; + + QStringRef text() const; + + XmlStreamNamespaceDeclarations namespaceDeclarations() const; + void addExtraNamespaceDeclaration(const XmlStreamNamespaceDeclaration &extraNamespaceDeclaraction); + void addExtraNamespaceDeclarations(const XmlStreamNamespaceDeclarations &extraNamespaceDeclaractions); + XmlStreamNotationDeclarations notationDeclarations() const; + XmlStreamEntityDeclarations entityDeclarations() const; + QStringRef dtdName() const; + QStringRef dtdPublicId() const; + QStringRef dtdSystemId() const; + + + enum Error { + NoError, + UnexpectedElementError, + CustomError, + NotWellFormedError, + PrematureEndOfDocumentError + }; + void raiseError(const QString& message = QString()); + QString errorString() const; + Error error() const; + + inline bool hasError() const + { + return error() != NoError; + } + + void setEntityResolver(XmlStreamEntityResolver *resolver); + XmlStreamEntityResolver *entityResolver() const; + +private: + Q_DISABLE_COPY(XmlStreamReader) + Q_DECLARE_PRIVATE(XmlStreamReader) + QScopedPointer d_ptr; + +}; + +class XmlStreamWriterPrivate; + +class Q_XMLSTREAM_EXPORT XmlStreamWriter +{ + QDOC_PROPERTY(bool autoFormatting READ autoFormatting WRITE setAutoFormatting) + QDOC_PROPERTY(int autoFormattingIndent READ autoFormattingIndent WRITE setAutoFormattingIndent) +public: + XmlStreamWriter(); + XmlStreamWriter(QIODevice *device); + XmlStreamWriter(QByteArray *array); + XmlStreamWriter(QString *string); + ~XmlStreamWriter(); + + void setDevice(QIODevice *device); + QIODevice *device() const; + +#ifndef QT_NO_TEXTCODEC + void setCodec(QTextCodec *codec); + void setCodec(const char *codecName); + QTextCodec *codec() const; +#endif + + void setAutoFormatting(bool); + bool autoFormatting() const; + + void setAutoFormattingIndent(int spacesOrTabs); + int autoFormattingIndent() const; + + void writeAttribute(const QString &qualifiedName, const QString &value); + void writeAttribute(const QString &namespaceUri, const QString &name, const QString &value); + void writeAttribute(const XmlStreamAttribute& attribute); + void writeAttributes(const XmlStreamAttributes& attributes); + + void writeCDATA(const QString &text); + void writeCharacters(const QString &text); + void writeComment(const QString &text); + + void writeDTD(const QString &dtd); + + void writeEmptyElement(const QString &qualifiedName); + void writeEmptyElement(const QString &namespaceUri, const QString &name); + + void writeTextElement(const QString &qualifiedName, const QString &text); + void writeTextElement(const QString &namespaceUri, const QString &name, const QString &text); + + void writeEndDocument(); + void writeEndElement(); + + void writeEntityReference(const QString &name); + void writeNamespace(const QString &namespaceUri, const QString &prefix = QString()); + void writeDefaultNamespace(const QString &namespaceUri); + void writeProcessingInstruction(const QString &target, const QString &data = QString()); + + void writeStartDocument(); + void writeStartDocument(const QString &version); + void writeStartDocument(const QString &version, bool standalone); + void writeStartElement(const QString &qualifiedName); + void writeStartElement(const QString &namespaceUri, const QString &name); + + void writeCurrentToken(const XmlStreamReader &reader); + + bool hasError() const; + +private: + Q_DISABLE_COPY(XmlStreamWriter) + Q_DECLARE_PRIVATE(XmlStreamWriter) + QScopedPointer d_ptr; +}; + +#endif + diff --git a/xml/qxmlstream_p.h b/xml/qxmlstream_p.h new file mode 100644 index 0000000000..6d949f7393 --- /dev/null +++ b/xml/qxmlstream_p.h @@ -0,0 +1,1968 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QXMLSTREAM_P_H +#define QXMLSTREAM_P_H + +#if defined(ERROR) +# undef ERROR +#endif + +class XmlStreamReader_Table +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + AMPERSAND = 5, + ANY = 41, + ATTLIST = 31, + BANG = 25, + CDATA = 47, + CDATA_START = 28, + COLON = 17, + COMMA = 19, + DASH = 20, + DBLQUOTE = 8, + DIGIT = 27, + DOCTYPE = 29, + DOT = 23, + ELEMENT = 30, + EMPTY = 40, + ENTITIES = 51, + ENTITY = 32, + ENTITY_DONE = 45, + EQ = 14, + ERROR = 43, + FIXED = 39, + HASH = 6, + ID = 48, + IDREF = 49, + IDREFS = 50, + IMPLIED = 38, + LANGLE = 3, + LBRACK = 9, + LETTER = 26, + LPAREN = 11, + NDATA = 36, + NMTOKEN = 52, + NMTOKENS = 53, + NOTATION = 33, + NOTOKEN = 1, + PARSE_ENTITY = 44, + PCDATA = 42, + PERCENT = 15, + PIPE = 13, + PLUS = 21, + PUBLIC = 35, + QUESTIONMARK = 24, + QUOTE = 7, + RANGLE = 4, + RBRACK = 10, + REQUIRED = 37, + RPAREN = 12, + SEMICOLON = 18, + SHIFT_THERE = 56, + SLASH = 16, + SPACE = 2, + STAR = 22, + SYSTEM = 34, + UNRESOLVED_ENTITY = 46, + VERSION = 55, + XML = 54, + + ACCEPT_STATE = 416, + RULE_COUNT = 270, + STATE_COUNT = 427, + TERMINAL_COUNT = 57, + NON_TERMINAL_COUNT = 84, + + GOTO_INDEX_OFFSET = 427, + GOTO_INFO_OFFSET = 1017, + GOTO_CHECK_OFFSET = 1017 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +const char *const XmlStreamReader_Table::spell [] = { + "end of file", 0, " ", "<", ">", "&", "#", "\'", "\"", "[", + "]", "(", ")", "|", "=", "%", "/", ":", ";", ",", + "-", "+", "*", ".", "?", "!", "[a-zA-Z]", "[0-9]", "[CDATA[", "DOCTYPE", + "ELEMENT", "ATTLIST", "ENTITY", "NOTATION", "SYSTEM", "PUBLIC", "NDATA", "REQUIRED", "IMPLIED", "FIXED", + "EMPTY", "ANY", "PCDATA", 0, 0, 0, 0, "CDATA", "ID", "IDREF", + "IDREFS", "ENTITIES", "NMTOKEN", "NMTOKENS", " class XmlStreamSimpleStack { + T *data; + int tos, cap; +public: + inline XmlStreamSimpleStack():data(0), tos(-1), cap(0){} + inline ~XmlStreamSimpleStack(){ if (data) qFree(data); } + + inline void reserve(int extraCapacity) { + if (tos + extraCapacity + 1 > cap) { + cap = qMax(tos + extraCapacity + 1, cap << 1 ); + data = reinterpret_cast(qRealloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); + } + } + + inline T &push() { reserve(1); return data[++tos]; } + inline T &rawPush() { return data[++tos]; } + inline const T &top() const { return data[tos]; } + inline T &top() { return data[tos]; } + inline T &pop() { return data[tos--]; } + inline T &operator[](int index) { return data[index]; } + inline const T &at(int index) const { return data[index]; } + inline int size() const { return tos + 1; } + inline void resize(int s) { tos = s - 1; } + inline bool isEmpty() const { return tos < 0; } + inline void clear() { tos = -1; } +}; + + +class XmlStream +{ + Q_DECLARE_TR_FUNCTIONS(XmlStream) +}; + +class XmlStreamPrivateTagStack { +public: + struct NamespaceDeclaration + { + QStringRef prefix; + QStringRef namespaceUri; + }; + + struct Tag + { + QStringRef name; + QStringRef qualifiedName; + NamespaceDeclaration namespaceDeclaration; + int tagStackStringStorageSize; + int namespaceDeclarationsSize; + }; + + + XmlStreamPrivateTagStack(); + XmlStreamSimpleStack namespaceDeclarations; + QString tagStackStringStorage; + int tagStackStringStorageSize; + bool tagsDone; + + inline QStringRef addToStringStorage(const QStringRef &s) { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.insert(pos, s.unicode(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + inline QStringRef addToStringStorage(const QString &s) { + int pos = tagStackStringStorageSize; + int sz = s.size(); + if (pos != tagStackStringStorage.size()) + tagStackStringStorage.resize(pos); + tagStackStringStorage.insert(pos, s.unicode(), sz); + tagStackStringStorageSize += sz; + return QStringRef(&tagStackStringStorage, pos, sz); + } + + XmlStreamSimpleStack tagStack; + + + inline Tag &tagStack_pop() { + Tag& tag = tagStack.pop(); + tagStackStringStorageSize = tag.tagStackStringStorageSize; + namespaceDeclarations.resize(tag.namespaceDeclarationsSize); + tagsDone = tagStack.isEmpty(); + return tag; + } + inline Tag &tagStack_push() { + Tag &tag = tagStack.push(); + tag.tagStackStringStorageSize = tagStackStringStorageSize; + tag.namespaceDeclarationsSize = namespaceDeclarations.size(); + return tag; + } +}; + + +class XmlStreamEntityResolver; +#ifndef QT_NO_XMLSTREAMREADER +class XmlStreamReaderPrivate : public XmlStreamReader_Table, public XmlStreamPrivateTagStack{ + XmlStreamReader *q_ptr; + Q_DECLARE_PUBLIC(XmlStreamReader) +public: + XmlStreamReaderPrivate(XmlStreamReader *q); + ~XmlStreamReaderPrivate(); + void init(); + + QByteArray rawReadBuffer; + QByteArray dataBuffer; + uchar firstByte; + qint64 nbytesread; + QString readBuffer; + int readBufferPos; + XmlStreamSimpleStack putStack; + struct Entity { + Entity(const QString& str = QString()) + :value(str), external(false), unparsed(false), literal(false), + hasBeenParsed(false), isCurrentlyReferenced(false){} + static inline Entity createLiteral(const QString &entity) + { Entity result(entity); result.literal = result.hasBeenParsed = true; return result; } + QString value; + uint external : 1; + uint unparsed : 1; + uint literal : 1; + uint hasBeenParsed : 1; + uint isCurrentlyReferenced : 1; + }; + QHash entityHash; + QHash parameterEntityHash; + XmlStreamSimpleStackentityReferenceStack; + inline bool referenceEntity(Entity &entity) { + if (entity.isCurrentlyReferenced) { + raiseWellFormedError(XmlStream::tr("Recursive entity detected.")); + return false; + } + entity.isCurrentlyReferenced = true; + entityReferenceStack.push() = &entity; + injectToken(ENTITY_DONE); + return true; + } + + + QIODevice *device; + bool deleteDevice; +#ifndef QT_NO_TEXTCODEC + QTextCodec *codec; + QTextDecoder *decoder; +#endif + bool atEnd; + + /*! + \sa setType() + */ + XmlStreamReader::TokenType type; + XmlStreamReader::Error error; + QString errorString; + QString unresolvedEntity; + + qint64 lineNumber, lastLineStart, characterOffset; + + + void write(const QString &); + void write(const char *); + + + XmlStreamAttributes attributes; + QStringRef namespaceForPrefix(const QStringRef &prefix); + void resolveTag(); + void resolvePublicNamespaces(); + void resolveDtd(); + uint resolveCharRef(int symbolIndex); + bool checkStartDocument(); + void startDocument(); + void parseError(); + void checkPublicLiteral(const QStringRef &publicId); + + bool scanDtd; + QStringRef lastAttributeValue; + bool lastAttributeIsCData; + struct DtdAttribute { + QStringRef tagName; + QStringRef attributeQualifiedName; + QStringRef attributePrefix; + QStringRef attributeName; + QStringRef defaultValue; + bool isCDATA; + bool isNamespaceAttribute; + }; + XmlStreamSimpleStack dtdAttributes; + struct NotationDeclaration { + QStringRef name; + QStringRef publicId; + QStringRef systemId; + }; + XmlStreamSimpleStack notationDeclarations; + XmlStreamNotationDeclarations publicNotationDeclarations; + XmlStreamNamespaceDeclarations publicNamespaceDeclarations; + + struct EntityDeclaration { + QStringRef name; + QStringRef notationName; + QStringRef publicId; + QStringRef systemId; + QStringRef value; + bool parameter; + bool external; + inline void clear() { + name.clear(); + notationName.clear(); + publicId.clear(); + systemId.clear(); + value.clear(); + parameter = external = false; + } + }; + XmlStreamSimpleStack entityDeclarations; + XmlStreamEntityDeclarations publicEntityDeclarations; + + QStringRef text; + + QStringRef prefix, namespaceUri, qualifiedName, name; + QStringRef processingInstructionTarget, processingInstructionData; + QStringRef dtdName, dtdPublicId, dtdSystemId; + QStringRef documentVersion, documentEncoding; + uint isEmptyElement : 1; + uint isWhitespace : 1; + uint isCDATA : 1; + uint standalone : 1; + uint hasCheckedStartDocument : 1; + uint normalizeLiterals : 1; + uint hasSeenTag : 1; + uint inParseEntity : 1; + uint referenceToUnparsedEntityDetected : 1; + uint referenceToParameterEntityDetected : 1; + uint hasExternalDtdSubset : 1; + uint lockEncoding : 1; + uint namespaceProcessing : 1; + + int resumeReduction; + void resume(int rule); + + inline bool entitiesMustBeDeclared() const { + return (!inParseEntity + && (standalone + || (!referenceToUnparsedEntityDetected + && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25 + && !hasExternalDtdSubset))); + } + + // qlalr parser + int tos; + int stack_size; + struct Value { + int pos; + int len; + int prefix; + ushort c; + }; + + Value *sym_stack; + int *state_stack; + inline void reallocateStack(); + inline Value &sym(int index) const + { return sym_stack[tos + index - 1]; } + QString textBuffer; + inline void clearTextBuffer() { + if (!scanDtd) { + textBuffer.resize(0); + textBuffer.reserve(256); + } + } + struct Attribute { + Value key; + Value value; + }; + XmlStreamSimpleStack attributeStack; + + inline QStringRef symString(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(int index) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symString(int index, int offset) { + const Value &symbol = sym(index); + return QStringRef(&textBuffer, symbol.pos + symbol.prefix + offset, symbol.len - symbol.prefix - offset); + } + inline QStringRef symPrefix(int index) { + const Value &symbol = sym(index); + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + inline QStringRef symString(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix); + } + inline QStringRef symName(const Value &symbol) { + return QStringRef(&textBuffer, symbol.pos, symbol.len); + } + inline QStringRef symPrefix(const Value &symbol) { + if (symbol.prefix) + return QStringRef(&textBuffer, symbol.pos, symbol.prefix - 1); + return QStringRef(); + } + + inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; } + + + short token; + ushort token_char; + + uint filterCarriageReturn(); + inline uint getChar(); + inline uint peekChar(); + inline void putChar(uint c) { putStack.push() = c; } + inline void putChar(QChar c) { putStack.push() = c.unicode(); } + void putString(const QString &s, int from = 0); + void putStringLiteral(const QString &s); + void putReplacement(const QString &s); + void putReplacementInAttributeValue(const QString &s); + ushort getChar_helper(); + + bool scanUntil(const char *str, short tokenToInject = -1); + bool scanString(const char *str, short tokenToInject, bool requireSpace = true); + inline void injectToken(ushort tokenToInject) { + putChar(int(tokenToInject) << 16); + } + + QString resolveUndeclaredEntity(const QString &name); + void parseEntity(const QString &value); + XmlStreamReaderPrivate *entityParser; + + bool scanAfterLangleBang(); + bool scanPublicOrSystem(); + bool scanNData(); + bool scanAfterDefaultDecl(); + bool scanAttType(); + + + // scan optimization functions. Not strictly necessary but LALR is + // not very well suited for scanning fast + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); + int fastScanName(int *prefix = 0); + inline int fastScanNMTOKEN(); + + + bool parse(); + inline void consumeRule(int); + + void raiseError(XmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); + + XmlStreamEntityResolver *entityResolver; + +private: + /*! \internal + Never assign to variable type directly. Instead use this function. + + This prevents errors from being ignored. + */ + inline void setType(const XmlStreamReader::TokenType t) + { + if(type != XmlStreamReader::Invalid) + type = t; + } +}; + +bool XmlStreamReaderPrivate::parse() +{ + // cleanup currently reported token + + switch (type) { + case XmlStreamReader::StartElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + if (publicNamespaceDeclarations.size()) + publicNamespaceDeclarations.clear(); + if (attributes.size()) + attributes.resize(0); + if (isEmptyElement) { + setType(XmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + isEmptyElement = false; + return true; + } + clearTextBuffer(); + break; + case XmlStreamReader::EndElement: + name.clear(); + prefix.clear(); + qualifiedName.clear(); + namespaceUri.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::DTD: + publicNotationDeclarations.clear(); + publicEntityDeclarations.clear(); + dtdName.clear(); + dtdPublicId.clear(); + dtdSystemId.clear(); + // fall through + case XmlStreamReader::Comment: + case XmlStreamReader::Characters: + isCDATA = false; + isWhitespace = true; + text.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::EntityReference: + text.clear(); + name.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::ProcessingInstruction: + processingInstructionTarget.clear(); + processingInstructionData.clear(); + clearTextBuffer(); + break; + case XmlStreamReader::NoToken: + case XmlStreamReader::Invalid: + break; + case XmlStreamReader::StartDocument: + lockEncoding = true; + documentVersion.clear(); + documentEncoding.clear(); +#ifndef QT_NO_TEXTCODEC + if(decoder->hasFailure()) { + raiseWellFormedError(XmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return false; + } +#endif + // fall through + default: + clearTextBuffer(); + ; + } + + setType(XmlStreamReader::NoToken); + + + // the main parse loop + int act, r; + + if (resumeReduction) { + act = state_stack[tos-1]; + r = resumeReduction; + resumeReduction = 0; + goto ResumeReduction; + } + + act = state_stack[tos]; + + forever { + if (token == -1 && - TERMINAL_COUNT != action_index[act]) { + uint cu = getChar(); + token = NOTOKEN; + token_char = cu; + if (cu & 0xff0000) { + token = cu >> 16; + } else switch (token_char) { + case 0xfffe: + case 0xffff: + token = ERROR; + break; + case '\r': + token = SPACE; + if (cu == '\r') { + if ((token_char = filterCarriageReturn())) { + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + break; + } + } else { + break; + } + // fall through + case '\0': { + token = EOF_SYMBOL; + if (!tagsDone && !inParseEntity) { + int a = t_action(act, token); + if (a < 0) { + raiseError(XmlStreamReader::PrematureEndOfDocumentError); + return false; + } + } + + } break; + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + case ' ': + case '\t': + token = SPACE; + break; + case '&': + token = AMPERSAND; + break; + case '#': + token = HASH; + break; + case '\'': + token = QUOTE; + break; + case '\"': + token = DBLQUOTE; + break; + case '<': + token = LANGLE; + break; + case '>': + token = RANGLE; + break; + case '[': + token = LBRACK; + break; + case ']': + token = RBRACK; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '|': + token = PIPE; + break; + case '=': + token = EQ; + break; + case '%': + token = PERCENT; + break; + case '/': + token = SLASH; + break; + case ':': + token = COLON; + break; + case ';': + token = SEMICOLON; + break; + case ',': + token = COMMA; + break; + case '-': + token = DASH; + break; + case '+': + token = PLUS; + break; + case '*': + token = STAR; + break; + case '.': + token = DOT; + break; + case '?': + token = QUESTIONMARK; + break; + case '!': + token = BANG; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token = DIGIT; + break; + case 0x3: + token = LETTER; + break; + default: + if (cu < 0x20) + token = NOTOKEN; + else + token = LETTER; + break; + } + } + + act = t_action (act, token); + if (act == ACCEPT_STATE) { + // reset the parser in case someone resumes (process instructions can follow a valid document) + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return true; + } else if (act > 0) { + if (++tos == stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; + val.c = token_char; + val.pos = textBuffer.size(); + val.prefix = 0; + val.len = 1; + if (token_char) + textBuffer += QChar(token_char); + + state_stack[tos] = act; + token = -1; + + + } else if (act < 0) { + r = - act - 1; + +#if defined (QLALR_DEBUG) + int ridx = rule_index[r]; + printf ("%3d) %s ::=", r + 1, spell[rule_info[ridx]]); + ++ridx; + for (int i = ridx; i < ridx + rhs[r]; ++i) { + int symbol = rule_info[i]; + if (const char *name = spell[symbol]) + printf (" %s", name); + else + printf (" #%d", symbol); + } + printf ("\n"); +#endif + + tos -= rhs[r]; + act = state_stack[tos++]; + ResumeReduction: + switch (r) { + + case 0: + setType(XmlStreamReader::EndDocument); + break; + + case 1: + if (type != XmlStreamReader::Invalid) { + if (hasSeenTag || inParseEntity) { + setType(XmlStreamReader::EndDocument); + } else { + raiseError(XmlStreamReader::NotWellFormedError, XmlStream::tr("Start tag expected.")); + // reset the parser + tos = 0; + state_stack[tos++] = 0; + state_stack[tos] = 0; + return false; + } + } + break; + + case 10: + entityReferenceStack.pop()->isCurrentlyReferenced = false; + clearSym(); + break; + + case 11: + if (!scanString(spell[VERSION], VERSION, false) && atEnd) { + resume(11); + return false; + } + break; + + case 12: + setType(XmlStreamReader::StartDocument); + documentVersion = symString(6); + startDocument(); + break; + + case 13: + hasExternalDtdSubset = true; + dtdSystemId = symString(2); + break; + + case 14: + checkPublicLiteral(symString(2)); + dtdPublicId = symString(2); + dtdSystemId = symString(4); + hasExternalDtdSubset = true; + break; + + case 16: + if (!scanPublicOrSystem() && atEnd) { + resume(16); + return false; + } + dtdName = symString(3); + break; + + case 17: + case 18: + dtdName = symString(3); + // fall through + + case 19: + case 20: + setType(XmlStreamReader::DTD); + text = &textBuffer; + break; + + case 21: + scanDtd = true; + break; + + case 22: + scanDtd = false; + break; + + case 37: + if (!scanString(spell[EMPTY], EMPTY, false) + && !scanString(spell[ANY], ANY, false) + && atEnd) { + resume(37); + return false; + } + break; + + case 43: + if (!scanString(spell[PCDATA], PCDATA, false) && atEnd) { + resume(43); + return false; + } + break; + + case 68: { + lastAttributeIsCData = true; + } break; + + case 78: + if (!scanAfterDefaultDecl() && atEnd) { + resume(78); + return false; + } + break; + + case 83: + sym(1) = sym(2); + lastAttributeValue.clear(); + lastAttributeIsCData = false; + if (!scanAttType() && atEnd) { + resume(83); + return false; + } + break; + + case 84: { + DtdAttribute &dtdAttribute = dtdAttributes.push(); + dtdAttribute.tagName.clear(); + dtdAttribute.isCDATA = lastAttributeIsCData; + dtdAttribute.attributePrefix = addToStringStorage(symPrefix(1)); + dtdAttribute.attributeName = addToStringStorage(symString(1)); + dtdAttribute.attributeQualifiedName = addToStringStorage(symName(1)); + dtdAttribute.isNamespaceAttribute = (dtdAttribute.attributePrefix == QLatin1String("xmlns") + || (dtdAttribute.attributePrefix.isEmpty() + && dtdAttribute.attributeName == QLatin1String("xmlns"))); + if (lastAttributeValue.isNull()) { + dtdAttribute.defaultValue.clear(); + } else { + if (dtdAttribute.isCDATA) + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue); + else + dtdAttribute.defaultValue = addToStringStorage(lastAttributeValue.toString().simplified()); + + } + } break; + + case 88: { + if (referenceToUnparsedEntityDetected && !standalone) + break; + int n = dtdAttributes.size(); + QStringRef tagName = addToStringStorage(symName(3)); + while (n--) { + DtdAttribute &dtdAttribute = dtdAttributes[n]; + if (!dtdAttribute.tagName.isNull()) + break; + dtdAttribute.tagName = tagName; + for (int i = 0; i < n; ++i) { + if ((dtdAttributes[i].tagName.isNull() || dtdAttributes[i].tagName == tagName) + && dtdAttributes[i].attributeQualifiedName == dtdAttribute.attributeQualifiedName) { + dtdAttribute.attributeQualifiedName.clear(); // redefined, delete it + break; + } + } + } + } break; + + case 89: { + if (!scanPublicOrSystem() && atEnd) { + resume(89); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(3); + } break; + + case 90: { + if (!scanPublicOrSystem() && atEnd) { + resume(90); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.push(); + entityDeclaration.clear(); + entityDeclaration.name = symString(5); + entityDeclaration.parameter = true; + } break; + + case 91: { + if (!scanNData() && atEnd) { + resume(91); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.systemId = symString(3); + entityDeclaration.external = true; + } break; + + case 92: { + if (!scanNData() && atEnd) { + resume(92); + return false; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + checkPublicLiteral((entityDeclaration.publicId = symString(3))); + entityDeclaration.systemId = symString(5); + entityDeclaration.external = true; + } break; + + case 93: { + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + entityDeclaration.notationName = symString(3); + if (entityDeclaration.parameter) + raiseWellFormedError(XmlStream::tr("NDATA in parameter entity declaration.")); + } + //fall through + + case 94: + case 95: { + if (referenceToUnparsedEntityDetected && !standalone) { + entityDeclarations.pop(); + break; + } + EntityDeclaration &entityDeclaration = entityDeclarations.top(); + if (!entityDeclaration.external) + entityDeclaration.value = symString(2); + QString entityName = entityDeclaration.name.toString(); + QHash &hash = entityDeclaration.parameter ? parameterEntityHash : entityHash; + if (!hash.contains(entityName)) { + Entity entity(entityDeclaration.value.toString()); + entity.unparsed = (!entityDeclaration.notationName.isNull()); + entity.external = entityDeclaration.external; + hash.insert(entityName, entity); + } + } break; + + case 96: { + setType(XmlStreamReader::ProcessingInstruction); + int pos = sym(4).pos + sym(4).len; + processingInstructionTarget = symString(3); + if (scanUntil("?>")) { + processingInstructionData = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 2); + const QString piTarget(processingInstructionTarget.toString()); + if (!piTarget.compare(QLatin1String("xml"), Qt::CaseInsensitive)) { + raiseWellFormedError(XmlStream::tr("XML declaration not at start of document.")); + } + else if(!XmlUtils::isNCName(piTarget)) + raiseWellFormedError(XmlStream::tr("%1 is an invalid processing instruction name.").arg(piTarget)); + } else if (type != XmlStreamReader::Invalid){ + resume(96); + return false; + } + } break; + + case 97: + setType(XmlStreamReader::ProcessingInstruction); + processingInstructionTarget = symString(3); + if (!processingInstructionTarget.toString().compare(QLatin1String("xml"), Qt::CaseInsensitive)) + raiseWellFormedError(XmlStream::tr("Invalid processing instruction name.")); + break; + + case 98: + if (!scanAfterLangleBang() && atEnd) { + resume(98); + return false; + } + break; + + case 99: + if (!scanUntil("--")) { + resume(99); + return false; + } + break; + + case 100: { + setType(XmlStreamReader::Comment); + int pos = sym(1).pos + 4; + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } break; + + case 101: { + setType(XmlStreamReader::Characters); + isCDATA = true; + isWhitespace = false; + int pos = sym(2).pos; + if (scanUntil("]]>", -1)) { + text = QStringRef(&textBuffer, pos, textBuffer.size() - pos - 3); + } else { + resume(101); + return false; + } + } break; + + case 102: { + if (!scanPublicOrSystem() && atEnd) { + resume(102); + return false; + } + NotationDeclaration ¬ationDeclaration = notationDeclarations.push(); + notationDeclaration.name = symString(3); + } break; + + case 103: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId = symString(3); + notationDeclaration.publicId.clear(); + } break; + + case 104: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + notationDeclaration.systemId.clear(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + } break; + + case 105: { + NotationDeclaration ¬ationDeclaration = notationDeclarations.top(); + checkPublicLiteral((notationDeclaration.publicId = symString(3))); + notationDeclaration.systemId = symString(5); + } break; + + case 129: + isWhitespace = false; + // fall through + + case 130: + sym(1).len += fastScanContentCharList(); + if (atEnd && !inParseEntity) { + resume(130); + return false; + } + break; + + case 139: + if (!textBuffer.isEmpty()) { + setType(XmlStreamReader::Characters); + text = &textBuffer; + } + break; + + case 140: + case 141: + clearSym(); + break; + + case 142: + case 143: + sym(1) = sym(2); + break; + + case 144: + case 145: + case 146: + case 147: + sym(1).len += sym(2).len; + break; + + case 173: + if (normalizeLiterals) + textBuffer.data()[textBuffer.size()-1] = QLatin1Char(' '); + break; + + case 174: + sym(1).len += fastScanLiteralContent(); + if (atEnd) { + resume(174); + return false; + } + break; + + case 175: { + if (!XmlUtils::isPublicID(symString(1).toString())) { + raiseWellFormedError(XmlStream::tr("%1 is an invalid PUBLIC identifier.").arg(symString(1).toString())); + resume(175); + return false; + } + } break; + + case 176: + case 177: + clearSym(); + break; + + case 178: + case 179: + sym(1) = sym(2); + break; + + case 180: + case 181: + case 182: + case 183: + sym(1).len += sym(2).len; + break; + + case 213: + case 214: + clearSym(); + break; + + case 215: + case 216: + sym(1) = sym(2); + lastAttributeValue = symString(1); + break; + + case 217: + case 218: + case 219: + case 220: + sym(1).len += sym(2).len; + break; + + case 229: { + QStringRef prefix = symPrefix(1); + if (prefix.isEmpty() && symString(1) == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix.clear(); + + const QStringRef ns(symString(5)); + if(ns == QLatin1String("http://www.w3.org/2000/xmlns/") || + ns == QLatin1String("http://www.w3.org/XML/1998/namespace")) + raiseWellFormedError(XmlStream::tr("Illegal namespace declaration.")); + else + namespaceDeclaration.namespaceUri = addToStringStorage(ns); + } else { + Attribute &attribute = attributeStack.push(); + attribute.key = sym(1); + attribute.value = sym(5); + + QStringRef attributeQualifiedName = symName(1); + bool normalize = false; + for (int a = 0; a < dtdAttributes.size(); ++a) { + DtdAttribute &dtdAttribute = dtdAttributes[a]; + if (!dtdAttribute.isCDATA + && dtdAttribute.tagName == qualifiedName + && dtdAttribute.attributeQualifiedName == attributeQualifiedName + ) { + normalize = true; + break; + } + } + if (normalize) { + // normalize attribute value (simplify and trim) + int pos = textBuffer.size(); + int n = 0; + bool wasSpace = true; + for (int i = 0; i < attribute.value.len; ++i) { + QChar c = textBuffer.at(attribute.value.pos + i); + if (c.unicode() == ' ') { + if (wasSpace) + continue; + wasSpace = true; + } else { + wasSpace = false; + } + textBuffer += textBuffer.at(attribute.value.pos + i); + ++n; + } + if (wasSpace) + while (n && textBuffer.at(pos + n - 1).unicode() == ' ') + --n; + attribute.value.pos = pos; + attribute.value.len = n; + } + if (prefix == QLatin1String("xmlns") && namespaceProcessing) { + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + QStringRef namespacePrefix = symString(attribute.key); + QStringRef namespaceUri = symString(attribute.value); + attributeStack.pop(); + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) + || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") + || namespaceUri.isEmpty() + || namespacePrefix == QLatin1String("xmlns")) + raiseWellFormedError(XmlStream::tr("Illegal namespace declaration.")); + + namespaceDeclaration.prefix = addToStringStorage(namespacePrefix); + namespaceDeclaration.namespaceUri = addToStringStorage(namespaceUri); + } + } + } break; + + case 235: { + normalizeLiterals = true; + Tag &tag = tagStack_push(); + prefix = tag.namespaceDeclaration.prefix = addToStringStorage(symPrefix(2)); + name = tag.name = addToStringStorage(symString(2)); + qualifiedName = tag.qualifiedName = addToStringStorage(symName(2)); + if ((!prefix.isEmpty() && !XmlUtils::isNCName(prefix)) || !XmlUtils::isNCName(name)) + raiseWellFormedError(XmlStream::tr("Invalid XML name.")); + } break; + + case 236: + isEmptyElement = true; + // fall through + + case 237: + setType(XmlStreamReader::StartElement); + resolveTag(); + if (tagStack.size() == 1 && hasSeenTag && !inParseEntity) + raiseWellFormedError(XmlStream::tr("Extra content at end of document.")); + hasSeenTag = true; + break; + + case 238: { + setType(XmlStreamReader::EndElement); + Tag &tag = tagStack_pop(); + + namespaceUri = tag.namespaceDeclaration.namespaceUri; + name = tag.name; + qualifiedName = tag.qualifiedName; + if (qualifiedName != symName(3)) + raiseWellFormedError(XmlStream::tr("Opening and ending tag mismatch.")); + } break; + + case 239: + if (entitiesMustBeDeclared()) { + raiseWellFormedError(XmlStream::tr("Entity '%1' not declared.").arg(unresolvedEntity)); + break; + } + setType(XmlStreamReader::EntityReference); + name = &unresolvedEntity; + break; + + case 240: { + sym(1).len += sym(2).len + 1; + QString reference = symString(2).toString(); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed) { + raiseWellFormedError(XmlStream::tr("Reference to unparsed entity '%1'.").arg(reference)); + } else { + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacement(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + + injectToken(UNRESOLVED_ENTITY); + unresolvedEntity = symString(2).toString(); + textBuffer.chop(2 + sym(2).len); + clearSym(); + + } break; + + case 241: { + sym(1).len += sym(2).len + 1; + QString reference = symString(2).toString(); + if (parameterEntityHash.contains(reference)) { + referenceToParameterEntityDetected = true; + Entity &entity = parameterEntityHash[reference]; + if (entity.unparsed || entity.external) { + referenceToUnparsedEntityDetected = true; + } else { + if (referenceEntity(entity)) + putString(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + } + } else if (entitiesMustBeDeclared()) { + raiseWellFormedError(XmlStream::tr("Entity '%1' not declared.").arg(symString(2).toString())); + } + } break; + + case 242: + sym(1).len += sym(2).len + 1; + break; + + case 243: { + sym(1).len += sym(2).len + 1; + QString reference = symString(2).toString(); + if (entityHash.contains(reference)) { + Entity &entity = entityHash[reference]; + if (entity.unparsed || entity.value.isNull()) { + raiseWellFormedError(XmlStream::tr("Reference to external entity '%1' in attribute value.").arg(reference)); + break; + } + if (!entity.hasBeenParsed) { + parseEntity(entity.value); + entity.hasBeenParsed = true; + } + if (entity.literal) + putStringLiteral(entity.value); + else if (referenceEntity(entity)) + putReplacementInAttributeValue(entity.value); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + + if (entityResolver) { + QString replacementText = resolveUndeclaredEntity(reference); + if (!replacementText.isNull()) { + putReplacement(replacementText); + textBuffer.chop(2 + sym(2).len); + clearSym(); + break; + } + } + if (entitiesMustBeDeclared()) { + raiseWellFormedError(XmlStream::tr("Entity '%1' not declared.").arg(reference)); + } + } break; + + case 244: { + if (uint s = resolveCharRef(3)) { + if (s >= 0xffff) + putStringLiteral(QString::fromUcs4(&s, 1)); + else + putChar((LETTER << 16) | s); + + textBuffer.chop(3 + sym(3).len); + clearSym(); + } else { + raiseWellFormedError(XmlStream::tr("Invalid character reference.")); + } + } break; + + case 247: + case 248: + sym(1).len += sym(2).len; + break; + + case 259: + sym(1).len += fastScanSpace(); + if (atEnd) { + resume(259); + return false; + } + break; + + case 262: { + sym(1).len += fastScanName(&sym(1).prefix); + if (atEnd) { + resume(262); + return false; + } + } break; + + case 263: + sym(1).len += fastScanName(); + if (atEnd) { + resume(263); + return false; + } + break; + + case 264: + case 265: + case 266: + case 267: + case 268: + sym(1).len += fastScanNMTOKEN(); + if (atEnd) { + resume(268); + return false; + } + + break; + + default: + ; + } // switch + act = state_stack[tos] = nt_action (act, lhs[r] - TERMINAL_COUNT); + if (type != XmlStreamReader::NoToken) + return true; + } else { + parseError(); + break; + } + } + return false; +} +#endif //QT_NO_XMLSTREAMREADER.xml + + +#endif // QXMLSTREAM_P_H + diff --git a/xml/qxmlutils.cpp b/xml/qxmlutils.cpp new file mode 100644 index 0000000000..45b7757573 --- /dev/null +++ b/xml/qxmlutils.cpp @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qxmlutils.h" + +/* TODO: + * - isNameChar() doesn't have to be public, it's only needed in + * qdom.cpp -- refactor fixedXmlName() to use isNCName() + * - A lot of functions can be inlined. + */ + +class QXmlCharRange +{ +public: + ushort min; + ushort max; +}; +typedef const QXmlCharRange *RangeIter; + +/*! + Performs a binary search between \a begin and \a end inclusive, to check whether \a + c is contained. Remember that the QXmlCharRange instances must be in numeric order. + */ +bool XmlUtils::rangeContains(RangeIter begin, RangeIter end, const QChar c) +{ + const ushort cp(c.unicode()); + + // check the first two ranges "manually" as characters in that + // range are checked very often and we avoid the binary search below. + + if (cp <= begin->max) + return cp >= begin->min; + + ++begin; + + if (begin == end) + return false; + + if (cp <= begin->max) + return cp >= begin->min; + + while (begin != end) { + int delta = (end - begin) / 2; + RangeIter mid = begin + delta; + + if (mid->min > cp) + end = mid; + else if (mid->max < cp) + begin = mid; + else + return true; + + if (delta == 0) + break; + } + + return false; +} + +// [85] BaseChar ::= ... + +static const QXmlCharRange g_base_begin[] = +{ + {0x0041, 0x005A}, {0x0061, 0x007A}, {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x00FF}, + {0x0100, 0x0131}, {0x0134, 0x013E}, {0x0141, 0x0148}, {0x014A, 0x017E}, {0x0180, 0x01C3}, + {0x01CD, 0x01F0}, {0x01F4, 0x01F5}, {0x01FA, 0x0217}, {0x0250, 0x02A8}, {0x02BB, 0x02C1}, + {0x0386, 0x0386}, {0x0388, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x03A1}, {0x03A3, 0x03CE}, + {0x03D0, 0x03D6}, {0x03DA, 0x03DA}, {0x03DC, 0x03DC}, {0x03DE, 0x03DE}, {0x03E0, 0x03E0}, + {0x03E2, 0x03F3}, {0x0401, 0x040C}, {0x040E, 0x044F}, {0x0451, 0x045C}, {0x045E, 0x0481}, + {0x0490, 0x04C4}, {0x04C7, 0x04C8}, {0x04CB, 0x04CC}, {0x04D0, 0x04EB}, {0x04EE, 0x04F5}, + {0x04F8, 0x04F9}, {0x0531, 0x0556}, {0x0559, 0x0559}, {0x0561, 0x0586}, {0x05D0, 0x05EA}, + {0x05F0, 0x05F2}, {0x0621, 0x063A}, {0x0641, 0x064A}, {0x0671, 0x06B7}, {0x06BA, 0x06BE}, + {0x06C0, 0x06CE}, {0x06D0, 0x06D3}, {0x06D5, 0x06D5}, {0x06E5, 0x06E6}, {0x0905, 0x0939}, + {0x093D, 0x093D}, {0x0958, 0x0961}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, + {0x09F0, 0x09F1}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, + {0x0A72, 0x0A74}, {0x0A85, 0x0A8B}, {0x0A8D, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, + {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABD, 0x0ABD}, {0x0AE0, 0x0AE0}, + {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B36, 0x0B39}, {0x0B3D, 0x0B3D}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB5}, {0x0BB7, 0x0BB9}, {0x0C05, 0x0C0C}, + {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C33}, {0x0C35, 0x0C39}, {0x0C60, 0x0C61}, + {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D28}, + {0x0D2A, 0x0D39}, {0x0D60, 0x0D61}, {0x0E01, 0x0E2E}, {0x0E30, 0x0E30}, {0x0E32, 0x0E33}, + {0x0E40, 0x0E45}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, + {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, + {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EAE}, {0x0EB0, 0x0EB0}, {0x0EB2, 0x0EB3}, + {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0F40, 0x0F47}, {0x0F49, 0x0F69}, {0x10A0, 0x10C5}, + {0x10D0, 0x10F6}, {0x1100, 0x1100}, {0x1102, 0x1103}, {0x1105, 0x1107}, {0x1109, 0x1109}, + {0x110B, 0x110C}, {0x110E, 0x1112}, {0x113C, 0x113C}, {0x113E, 0x113E}, {0x1140, 0x1140}, + {0x114C, 0x114C}, {0x114E, 0x114E}, {0x1150, 0x1150}, {0x1154, 0x1155}, {0x1159, 0x1159}, + {0x115F, 0x1161}, {0x1163, 0x1163}, {0x1165, 0x1165}, {0x1167, 0x1167}, {0x1169, 0x1169}, + {0x116D, 0x116E}, {0x1172, 0x1173}, {0x1175, 0x1175}, {0x119E, 0x119E}, {0x11A8, 0x11A8}, + {0x11AB, 0x11AB}, {0x11AE, 0x11AF}, {0x11B7, 0x11B8}, {0x11BA, 0x11BA}, {0x11BC, 0x11C2}, + {0x11EB, 0x11EB}, {0x11F0, 0x11F0}, {0x11F9, 0x11F9}, {0x1E00, 0x1E9B}, {0x1EA0, 0x1EF9}, + {0x1F00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, + {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, + {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x2126, 0x2126}, + {0x212A, 0x212B}, {0x212E, 0x212E}, {0x2180, 0x2182}, {0x3041, 0x3094}, {0x30A1, 0x30FA}, + {0x3105, 0x312C}, {0xAC00, 0xD7A3} +}; +static const RangeIter g_base_end = g_base_begin + sizeof(g_base_begin) / sizeof(QXmlCharRange); + +static const QXmlCharRange g_ideographic_begin[] = +{ + {0x3007, 0x3007}, {0x3021, 0x3029}, {0x4E00, 0x9FA5} +}; +static const RangeIter g_ideographic_end = g_ideographic_begin + sizeof(g_ideographic_begin) / sizeof(QXmlCharRange); + +bool XmlUtils::isIdeographic(const QChar c) +{ + return rangeContains(g_ideographic_begin, g_ideographic_end, c); +} + +static const QXmlCharRange g_combining_begin[] = +{ + {0x0300, 0x0345}, {0x0360, 0x0361}, {0x0483, 0x0486}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C4}, {0x064B, 0x0652}, + {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DD, 0x06DF}, {0x06E0, 0x06E4}, {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, {0x0901, 0x0903}, {0x093C, 0x093C}, {0x093E, 0x094C}, {0x094D, 0x094D}, + {0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0983}, {0x09BC, 0x09BC}, {0x09BE, 0x09BE}, + {0x09BF, 0x09BF}, {0x09C0, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, + {0x09E2, 0x09E3}, {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A3E}, {0x0A3F, 0x0A3F}, + {0x0A40, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, {0x0A81, 0x0A83}, + {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0B01, 0x0B03}, + {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B43}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, + {0x0B82, 0x0B83}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, + {0x0C01, 0x0C03}, {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0C82, 0x0C83}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, + {0x0D02, 0x0D03}, {0x0D3E, 0x0D43}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F3E, 0x0F3E}, {0x0F3F, 0x0F3F}, {0x0F71, 0x0F84}, {0x0F86, 0x0F8B}, + {0x0F90, 0x0F95}, {0x0F97, 0x0F97}, {0x0F99, 0x0FAD}, {0x0FB1, 0x0FB7}, {0x0FB9, 0x0FB9}, + {0x20D0, 0x20DC}, {0x20E1, 0x20E1}, {0x302A, 0x302F}, {0x3099, 0x3099}, {0x309A, 0x309A} +}; +static const RangeIter g_combining_end = g_combining_begin + sizeof(g_combining_begin) / sizeof(QXmlCharRange); + +bool XmlUtils::isCombiningChar(const QChar c) +{ + return rangeContains(g_combining_begin, g_combining_end, c); +} + +// [88] Digit ::= ... +static const QXmlCharRange g_digit_begin[] = +{ + {0x0030, 0x0039}, {0x0660, 0x0669}, {0x06F0, 0x06F9}, {0x0966, 0x096F}, {0x09E6, 0x09EF}, + {0x0A66, 0x0A6F}, {0x0AE6, 0x0AEF}, {0x0B66, 0x0B6F}, {0x0BE7, 0x0BEF}, {0x0C66, 0x0C6F}, + {0x0CE6, 0x0CEF}, {0x0D66, 0x0D6F}, {0x0E50, 0x0E59}, {0x0ED0, 0x0ED9}, {0x0F20, 0x0F29} +}; +static const RangeIter g_digit_end = g_digit_begin + sizeof(g_digit_begin) / sizeof(QXmlCharRange); + +bool XmlUtils::isDigit(const QChar c) +{ + return rangeContains(g_digit_begin, g_digit_end, c); +} + +// [89] Extender ::= ... +static const QXmlCharRange g_extender_begin[] = +{ + {0x00B7, 0x00B7}, {0x02D0, 0x02D0}, {0x02D1, 0x02D1}, {0x0387, 0x0387}, {0x0640, 0x0640}, + {0x0E46, 0x0E46}, {0x0EC6, 0x0EC6}, {0x3005, 0x3005}, {0x3031, 0x3035}, {0x309D, 0x309E}, + {0x30FC, 0x30FE} +}; +static const RangeIter g_extender_end = g_extender_begin + sizeof(g_extender_begin) / sizeof(QXmlCharRange); + +bool XmlUtils::isExtender(const QChar c) +{ + return rangeContains(g_extender_begin, g_extender_end, c); +} + +bool XmlUtils::isBaseChar(const QChar c) +{ + return rangeContains(g_base_begin, g_base_end, c); +} + +/*! + \internal + + Determines whether \a encName is a valid instance of production [81]EncName in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa \l {http://www.w3.org/TR/REC-xml/#NT-EncName} + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [81] EncName} + */ +bool XmlUtils::isEncName(const QString &encName) +{ + /* Right, we here have a dependency on QRegExp. Writing a manual parser to + * replace that regexp is probably a 70 lines so I prioritize this to when + * the dependency is considered alarming, or when the rest of the bugs + * are fixed. */ + const QRegExp encNameRegExp(QLatin1String("[A-Za-z][A-Za-z0-9._\\-]*")); + Q_ASSERT(encNameRegExp.isValid()); + + return encNameRegExp.exactMatch(encName); +} + +/*! + \internal + + Determines whether \a c is a valid instance of production [84]Letter in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa \l {http://www.w3.org/TR/REC-xml/#NT-Letter} + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [84] Letter} + */ +bool XmlUtils::isLetter(const QChar c) +{ + return isBaseChar(c) || isIdeographic(c); +} + +/*! + \internal + + Determines whether \a c is a valid instance of production [2]Char in the XML 1.0 + specification. If it is, true is returned, otherwise false. + + \sa \l {http://www.w3.org/TR/REC-xml/#NT-Char} + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [2] Char} + */ +bool XmlUtils::isChar(const QChar c) +{ + return (c.unicode() >= 0x0020 && c.unicode() <= 0xD7FF) + || c.unicode() == 0x0009 + || c.unicode() == 0x000A + || c.unicode() == 0x000D + || c.unicode() == 0x0003 // ws: ETX + || (c.unicode() >= 0xE000 && c.unicode() <= 0xFFFD); +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [4]NameChar in the XML 1.0 specification. If it + is, true is returned, otherwise false. + + \sa \l {http://www.w3.org/TR/REC-xml/#NT-NameChar} + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [4] NameChar} + */ +bool XmlUtils::isNameChar(const QChar c) +{ + return isBaseChar(c) + || isDigit(c) + || c.unicode() == '.' + || c.unicode() == '-' + || c.unicode() == '_' + || c.unicode() == ':' + || isCombiningChar(c) + || isIdeographic(c) + || isExtender(c); +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [12] PubidLiteral in the XML 1.0 specification. If it + is, true is returned, otherwise false. + + \sa \l {http://www.w3.org/TR/REC-xml/#NT-PubidLiteral} + {Extensible Markup Language (XML) 1.0 (Fourth Edition), [12] PubidLiteral} + */ +bool XmlUtils::isPublicID(const QString &candidate) +{ + const int len = candidate.length(); + + for(int i = 0; i < len; ++i) + { + const ushort cp = candidate.at(i).unicode(); + + if ((cp >= 'a' && cp <= 'z') + || (cp >= 'A' && cp <= 'Z') + || (cp >= '0' && cp <= '9')) + { + continue; + } + + switch (cp) + { + /* Fallthrough all these. */ + case 0x20: + case 0x0D: + case 0x0A: + case '-': + case '\'': + case '(': + case ')': + case '+': + case ',': + case '.': + case '/': + case ':': + case '=': + case '?': + case ';': + case '!': + case '*': + case '#': + case '@': + case '$': + case '_': + case '%': + continue; + default: + return false; + } + } + + return true; +} + +/*! + \internal + + Determines whether \a c is a valid instance of + production [4]NCName in the XML 1.0 Namespaces specification. If it + is, true is returned, otherwise false. + + \sa \l {http://www.w3.org/TR/REC-xml-names/#NT-NCName} + {W3CNamespaces in XML 1.0 (Second Edition), [4] NCName} + */ +bool XmlUtils::isNCName(const QStringRef &ncName) +{ + if(ncName.isEmpty()) + return false; + + const QChar first(ncName.at(0)); + + if(!XmlUtils::isLetter(first) && first.unicode() != '_' && first.unicode() != ':') + return false; + + const int len = ncName.size(); + for(int i = 0; i < len; ++i) + { + const QChar &at = ncName.at(i); + if(!XmlUtils::isNameChar(at) || at == QLatin1Char(':')) + return false; + } + + return true; +} + diff --git a/xml/qxmlutils.h b/xml/qxmlutils.h new file mode 100644 index 0000000000..a284ef6829 --- /dev/null +++ b/xml/qxmlutils.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef __XMLUTILS_H__ +#define __XMLUTILS_H__ + +#include + +class QString; +class QChar; +class QXmlCharRange; + +class XmlUtils + { + typedef const QXmlCharRange *RangeIter; + static bool rangeContains(RangeIter begin, RangeIter end, const QChar c); + static bool isBaseChar(const QChar c); + static bool isDigit(const QChar c); + static bool isExtender(const QChar c); + static bool isIdeographic(const QChar c); + static bool isCombiningChar(const QChar c); + + public: + static bool isEncName(const QString &encName); + static bool isChar(const QChar c); + static bool isNameChar(const QChar c); + static bool isLetter(const QChar c); + static bool isNCName(const QStringRef &ncName); + static inline bool isNCName(const QString &ncName) { return isNCName(&ncName); } + static bool isPublicID(const QString &candidate); + }; + +#endif