From a6b697ca13945a174cff9f3e9b1af1cf61c0bea5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 12 Dec 2017 18:32:19 -0800 Subject: Create corelib/serialization and move existing file formats into it This is in preparation to adding CBOR support. We don't need yet another dir for CBOR and placing it in src/corelib/json is just wrong. Change-Id: I9741f017961b410c910dfffd14ffb9d870340fa6 Reviewed-by: Oswald Buddenhagen Reviewed-by: Lars Knoll --- src/corelib/serialization/qxmlstream.cpp | 4014 ++++++++++++++++++++++++++++++ 1 file changed, 4014 insertions(+) create mode 100644 src/corelib/serialization/qxmlstream.cpp (limited to 'src/corelib/serialization/qxmlstream.cpp') diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp new file mode 100644 index 0000000000..9b5295a17e --- /dev/null +++ b/src/corelib/serialization/qxmlstream.cpp @@ -0,0 +1,4014 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtCore/qxmlstream.h" + +#ifndef QT_NO_XMLSTREAM + +#include "qxmlutils_p.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 { StreamEOF = ~0U }; + +/*! + \enum QXmlStreamReader::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 \c true; otherwise it + returns \c 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 \c true. If + the characters stem from a CDATA section, isCDATA() returns \c 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 QXmlStreamReader::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 QXmlStreamReader::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 QXmlStreamEntityResolver + \inmodule QtCore + \reentrant + \since 4.4 + + \brief The QXmlStreamEntityResolver class provides an entity + resolver for a QXmlStreamReader. + + \ingroup xml-tools + */ + +/*! + Destroys the entity resolver. + */ +QXmlStreamEntityResolver::~QXmlStreamEntityResolver() +{ +} + +/*! + \internal + +This function is a stub for later functionality. +*/ +QString QXmlStreamEntityResolver::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 QXmlStreamEntityResolver::resolveUndeclaredEntity(const QString &/*name*/) +{ + return QString(); +} + +#ifndef QT_NO_XMLSTREAMREADER + +QString QXmlStreamReaderPrivate::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 QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver) +{ + Q_D(QXmlStreamReader); + d->entityResolver = resolver; +} + +/*! + \since 4.4 + + Returns the entity resolver, or 0 if there is no entity resolver. + + \sa setEntityResolver() + */ +QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const +{ + Q_D(const QXmlStreamReader); + return d->entityResolver; +} + + + +/*! + \class QXmlStreamReader + \inmodule QtCore + \reentrant + \since 4.3 + + \brief The QXmlStreamReader class provides a fast parser for reading + well-formed XML via a simple streaming API. + + + \ingroup xml-tools + + QXmlStreamReader 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). + QXmlStreamReader reads data either from a QIODevice (see + setDevice()), or from a raw QByteArray (see addData()). + + Qt provides QXmlStreamWriter 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 + QXmlStreamReader 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 QXmlStreamReader, 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 QXmlStreamReader looks like this: + + \snippet code/src_corelib_xml_qxmlstream.cpp 0 + + + QXmlStreamReader 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, QXmlStreamReader contains a raiseError() + mechanism that lets you raise custom errors that trigger the same + error handling described. + + The \l{QXmlStream Bookmarks Example} illustrates how to use the + recursive descent technique to read an XML bookmark file (XBEL) with + a stream reader. + + \section1 Namespaces + + QXmlStream 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 + + QXmlStreamReader 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{QIODevice::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{QIODevice::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 + + QXmlStreamReader 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, QXmlStreamReader + 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() + */ +QXmlStreamReader::QXmlStreamReader() + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ +} + +/*! Creates a new stream reader that reads from \a device. + +\sa setDevice(), clear() + */ +QXmlStreamReader::QXmlStreamReader(QIODevice *device) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + setDevice(device); +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const QByteArray &data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); + d->dataBuffer = data; +} + +/*! + Creates a new stream reader that reads from \a data. + + \sa addData(), clear(), setDevice() + */ +QXmlStreamReader::QXmlStreamReader(const QString &data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); +#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() + */ +QXmlStreamReader::QXmlStreamReader(const char *data) + : d_ptr(new QXmlStreamReaderPrivate(this)) +{ + Q_D(QXmlStreamReader); + d->dataBuffer = QByteArray(data); +} + +/*! + Destructs the reader. + */ +QXmlStreamReader::~QXmlStreamReader() +{ + Q_D(QXmlStreamReader); + if (d->deleteDevice) + delete d->device; +} + +/*! \fn bool QXmlStreamReader::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 QXmlStreamReader::setDevice(QIODevice *device) +{ + Q_D(QXmlStreamReader); + if (d->deleteDevice) { + delete d->device; + d->deleteDevice = false; + } + d->device = device; + d->init(); + +} + +/*! + Returns the current device associated with the QXmlStreamReader, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QXmlStreamReader::device() const +{ + Q_D(const QXmlStreamReader); + 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 QXmlStreamReader::addData(const QByteArray &data) +{ + Q_D(QXmlStreamReader); + if (d->device) { + qWarning("QXmlStreamReader: 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 QXmlStreamReader::addData(const QString &data) +{ + Q_D(QXmlStreamReader); + 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 QXmlStreamReader::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 QXmlStreamReader::clear() +{ + Q_D(QXmlStreamReader); + d->init(); + if (d->device) { + if (d->deleteDevice) + delete d->device; + d->device = 0; + } +} + +/*! + Returns \c 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 \c 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 QXmlStreamReader::atEnd() const +{ + Q_D(const QXmlStreamReader); + if (d->atEnd + && ((d->type == QXmlStreamReader::Invalid && d->error == PrematureEndOfDocumentError) + || (d->type == QXmlStreamReader::EndDocument))) { + if (d->device) + return d->device->atEnd(); + else + return !d->dataBuffer.size(); + } + return (d->atEnd || d->type == QXmlStreamReader::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 \c true, hasError() returns \c true, and this function returns + QXmlStreamReader::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() + */ +QXmlStreamReader::TokenType QXmlStreamReader::readNext() +{ + Q_D(QXmlStreamReader); + 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(QXmlStream::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() + */ +QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const +{ + Q_D(const QXmlStreamReader); + return d->type; +} + +/*! + Reads until the next start element within the current element. Returns \c 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. + + This is a convenience function for when you're only concerned with parsing + XML elements. The \l{QXmlStream Bookmarks Example} makes extensive use of + this function. + + \since 4.6 + \sa readNext() + */ +bool QXmlStreamReader::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 QXmlStreamReader::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 QXmlStreamReader_tokenTypeString_string[] =\n"; +$counter = 0; +$i = 0; +while () { + chomp; + print " \"$_\\0\"\n"; + $sizes[$i++] = $counter; + $counter += length 1 + $_; +} +print " \"\\0\";\n\nstatic const short QXmlStreamReader_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 QXmlStreamReader_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 QXmlStreamReader_tokenTypeString_indices[] = { + 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 +}; + + +/*! + \property QXmlStreamReader::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 QXmlStreamReader::setNamespaceProcessing(bool enable) +{ + Q_D(QXmlStreamReader); + d->namespaceProcessing = enable; +} + +bool QXmlStreamReader::namespaceProcessing() const +{ + Q_D(const QXmlStreamReader); + return d->namespaceProcessing; +} + +/*! Returns the reader's current token as string. + +\sa tokenType() +*/ +QString QXmlStreamReader::tokenString() const +{ + Q_D(const QXmlStreamReader); + return QLatin1String(QXmlStreamReader_tokenTypeString_string + + QXmlStreamReader_tokenTypeString_indices[d->type]); +} + +#endif // QT_NO_XMLSTREAMREADER + +QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() +{ + tagStack.reserve(16); + tagStackStringStorage.reserve(32); + tagStackStringStorageSize = 0; + NamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.push(); + namespaceDeclaration.prefix = addToStringStorage(QStringViewLiteral("xml")); + namespaceDeclaration.namespaceUri = addToStringStorage(QStringViewLiteral("http://www.w3.org/XML/1998/namespace")); + initialTagStackStringStorageSize = tagStackStringStorageSize; +} + +#ifndef QT_NO_XMLSTREAMREADER + +QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *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(); +#define ADD_PREDEFINED(n, v) \ + do { \ + Entity e = Entity::createLiteral(QLatin1String(n), QLatin1String(v)); \ + entityHash.insert(qToStringViewIgnoringNull(e.name), std::move(e)); \ + } while (false) + ADD_PREDEFINED("lt", "<"); + ADD_PREDEFINED("gt", ">"); + ADD_PREDEFINED("amp", "&"); + ADD_PREDEFINED("apos", "'"); + ADD_PREDEFINED("quot", "\""); +#undef ADD_PREDEFINED +} + +void QXmlStreamReaderPrivate::init() +{ + 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(); + tagStackStringStorageSize = initialTagStackStringStorageSize; + + type = QXmlStreamReader::NoToken; + error = QXmlStreamReader::NoError; +} + +/* + Well-formed requires that we verify entity values. We do this with a + standard parser. + */ +void QXmlStreamReaderPrivate::parseEntity(const QString &value) +{ + Q_Q(QXmlStreamReader); + + if (value.isEmpty()) + return; + + + if (!entityParser) + entityParser = new QXmlStreamReaderPrivate(q); + else + entityParser->init(); + entityParser->inParseEntity = true; + entityParser->readBuffer = value; + entityParser->injectToken(PARSE_ENTITY); + while (!entityParser->atEnd && entityParser->type != QXmlStreamReader::Invalid) + entityParser->parse(); + if (entityParser->type == QXmlStreamReader::Invalid || entityParser->tagStack.size()) + raiseWellFormedError(QXmlStream::tr("Invalid entity value.")); + +} + +inline void QXmlStreamReaderPrivate::reallocateStack() +{ + stack_size <<= 1; + sym_stack = reinterpret_cast (realloc(sym_stack, stack_size * sizeof(Value))); + Q_CHECK_PTR(sym_stack); + state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); + Q_CHECK_PTR(state_stack); +} + + +QXmlStreamReaderPrivate::~QXmlStreamReaderPrivate() +{ +#ifndef QT_NO_TEXTCODEC + delete decoder; +#endif + free(sym_stack); + free(state_stack); + delete entityParser; +} + + +inline uint QXmlStreamReaderPrivate::filterCarriageReturn() +{ + uint peekc = peekChar(); + if (peekc == '\n') { + if (putStack.size()) + putStack.pop(); + else + ++readBufferPos; + return peekc; + } + if (peekc == StreamEOF) { + putChar('\r'); + return 0; + } + return '\n'; +} + +/*! + \internal + If the end of the file is encountered, ~0 is returned. + */ +inline uint QXmlStreamReaderPrivate::getChar() +{ + uint c; + if (putStack.size()) { + c = atEnd ? StreamEOF : putStack.pop(); + } else { + if (readBufferPos < readBuffer.size()) + c = readBuffer.at(readBufferPos++).unicode(); + else + c = getChar_helper(); + } + + return c; +} + +inline uint QXmlStreamReaderPrivate::peekChar() +{ + uint c; + if (putStack.size()) { + c = putStack.top(); + } else if (readBufferPos < readBuffer.size()) { + c = readBuffer.at(readBufferPos).unicode(); + } else { + if ((c = getChar_helper()) != StreamEOF) + --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 QXmlStreamReaderPrivate::scanUntil(const char *str, short tokenToInject) +{ + int pos = textBuffer.size(); + int oldLineNumber = lineNumber; + + uint c; + while ((c = getChar()) != StreamEOF) { + /* First, we do the validation & normalization. */ + switch (c) { + case '\r': + if ((c = filterCarriageReturn()) == 0) + break; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case '\t': + textBuffer += QChar(c); + continue; + default: + if (c < 0x20 || (c > 0xFFFD && c < 0x10000) || c > QChar::LastValidCodePoint ) { + raiseWellFormedError(QXmlStream::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 QXmlStreamReaderPrivate::scanString(const char *str, short tokenToInject, bool requireSpace) +{ + int n = 0; + while (str[n]) { + uint c = getChar(); + if (c != ushort(str[n])) { + if (c != StreamEOF) + 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 QXmlStreamReaderPrivate::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 QXmlStreamReaderPrivate::scanPublicOrSystem() +{ + switch (peekChar()) { + case 'S': + return scanString(spell[SYSTEM], SYSTEM); + case 'P': + return scanString(spell[PUBLIC], PUBLIC); + default: + ; + } + return false; +} + +bool QXmlStreamReaderPrivate::scanNData() +{ + if (fastScanSpace()) { + if (scanString(spell[NDATA], NDATA)) + return true; + putChar(' '); + } + return false; +} + +bool QXmlStreamReaderPrivate::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 QXmlStreamReaderPrivate::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 QXmlStreamReaderPrivate::fastScanLiteralContent() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + 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; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + if (normalizeLiterals) + textBuffer += QLatin1Char(' '); + else + textBuffer += QChar(c); + ++n; + break; + case '&': + case '<': + case '\"': + case '\'': + if (!(c & 0xff0000)) { + putChar(c); + return n; + } + Q_FALLTHROUGH(); + default: + if (c < 0x20) { + putChar(c); + return n; + } + textBuffer += QChar(c); + ++n; + } + } + return n; +} + +inline int QXmlStreamReaderPrivate::fastScanSpace() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + switch (c) { + case '\r': + if ((c = filterCarriageReturn()) == 0) + return n; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + 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 QXmlStreamReaderPrivate::fastScanContentCharList() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + 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(QXmlStream::tr("Sequence ']]>' not allowed in content.")); + } else { + putChar(c); + break; + } + return n; + } break; + case '\r': + if ((c = filterCarriageReturn()) == 0) + return n; + Q_FALLTHROUGH(); + case '\n': + ++lineNumber; + lastLineStart = characterOffset + readBufferPos; + Q_FALLTHROUGH(); + case ' ': + case '\t': + textBuffer += QChar(ushort(c)); + ++n; + break; + case '&': + case '<': + if (!(c & 0xff0000)) { + putChar(c); + return n; + } + Q_FALLTHROUGH(); + default: + if (c < 0x20) { + putChar(c); + return n; + } + isWhitespace = false; + textBuffer += QChar(ushort(c)); + ++n; + } + } + return n; +} + +inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + 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; + } + Q_FALLTHROUGH(); + 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 QXmlStreamReaderPrivate::fastScanNMTOKEN() +{ + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { + 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 QXmlStreamReaderPrivate::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 QXmlStreamReaderPrivate::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 QXmlStreamReaderPrivate::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 QXmlStreamReaderPrivate::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); + } +} + +uint QXmlStreamReaderPrivate::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 StreamEOF; + } + +#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 StreamEOF; + } + 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(QXmlStream::tr("Encountered incorrectly encoded content.")); + readBuffer.clear(); + return StreamEOF; + } +#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 StreamEOF; +} + +QStringRef QXmlStreamReaderPrivate::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(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix)); +#endif + + return QStringRef(); +} + +/* + uses namespaceForPrefix and builds the attribute vector + */ +void QXmlStreamReaderPrivate::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(QXmlStream::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(QXmlStream::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) { + QXmlStreamAttribute &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 = QXmlStreamStringRef(name); + attribute.m_qualifiedName = QXmlStreamStringRef(qualifiedName); + attribute.m_value = QXmlStreamStringRef(value); + + if (!prefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(prefix); + attribute.m_namespaceUri = QXmlStreamStringRef(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(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName())); + } + } + + 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; + + + + QXmlStreamAttribute attribute; + attribute.m_name = QXmlStreamStringRef(dtdAttribute.attributeName); + attribute.m_qualifiedName = QXmlStreamStringRef(dtdAttribute.attributeQualifiedName); + attribute.m_value = QXmlStreamStringRef(dtdAttribute.defaultValue); + + if (!dtdAttribute.attributePrefix.isEmpty()) { + QStringRef attributeNamespaceUri = namespaceForPrefix(dtdAttribute.attributePrefix); + attribute.m_namespaceUri = QXmlStreamStringRef(attributeNamespaceUri); + } + attribute.m_isDefault = true; + attributes.append(attribute); + } + + attributeStack.clear(); +} + +void QXmlStreamReaderPrivate::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); + QXmlStreamNamespaceDeclaration &publicNamespaceDeclaration = publicNamespaceDeclarations[i]; + publicNamespaceDeclaration.m_prefix = QXmlStreamStringRef(namespaceDeclaration.prefix); + publicNamespaceDeclaration.m_namespaceUri = QXmlStreamStringRef(namespaceDeclaration.namespaceUri); + } +} + +void QXmlStreamReaderPrivate::resolveDtd() +{ + publicNotationDeclarations.resize(notationDeclarations.size()); + for (int i = 0; i < notationDeclarations.size(); ++i) { + const QXmlStreamReaderPrivate::NotationDeclaration ¬ationDeclaration = notationDeclarations.at(i); + QXmlStreamNotationDeclaration &publicNotationDeclaration = publicNotationDeclarations[i]; + publicNotationDeclaration.m_name = QXmlStreamStringRef(notationDeclaration.name); + publicNotationDeclaration.m_systemId = QXmlStreamStringRef(notationDeclaration.systemId); + publicNotationDeclaration.m_publicId = QXmlStreamStringRef(notationDeclaration.publicId); + + } + notationDeclarations.clear(); + publicEntityDeclarations.resize(entityDeclarations.size()); + for (int i = 0; i < entityDeclarations.size(); ++i) { + const QXmlStreamReaderPrivate::EntityDeclaration &entityDeclaration = entityDeclarations.at(i); + QXmlStreamEntityDeclaration &publicEntityDeclaration = publicEntityDeclarations[i]; + publicEntityDeclaration.m_name = QXmlStreamStringRef(entityDeclaration.name); + publicEntityDeclaration.m_notationName = QXmlStreamStringRef(entityDeclaration.notationName); + publicEntityDeclaration.m_systemId = QXmlStreamStringRef(entityDeclaration.systemId); + publicEntityDeclaration.m_publicId = QXmlStreamStringRef(entityDeclaration.publicId); + publicEntityDeclaration.m_value = QXmlStreamStringRef(entityDeclaration.value); + } + entityDeclarations.clear(); + parameterEntityHash.clear(); +} + +uint QXmlStreamReaderPrivate::resolveCharRef(int symbolIndex) +{ + bool ok = true; + uint s; + // ### add toXShort to QStringRef? + if (sym(symbolIndex).c == 'x') + s = symString(symbolIndex, 1).toUInt(&ok, 16); + else + s = symString(symbolIndex).toUInt(&ok, 10); + + ok &= (s == 0x9 || s == 0xa || s == 0xd || (s >= 0x20 && s <= 0xd7ff) + || (s >= 0xe000 && s <= 0xfffd) || (s >= 0x10000 && s <= QChar::LastValidCodePoint)); + + return ok ? s : 0; +} + + +void QXmlStreamReaderPrivate::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(QXmlStream::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 \c true; otherwise it sets up everything + for a synthetic start document event and returns \c false. + */ +bool QXmlStreamReaderPrivate::checkStartDocument() +{ + hasCheckedStartDocument = true; + + if (scanString(spell[XML], XML)) + return true; + + type = QXmlStreamReader::StartDocument; + if (atEnd) { + hasCheckedStartDocument = false; + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); + } + return false; +} + +void QXmlStreamReaderPrivate::startDocument() +{ + QString err; + if (documentVersion != QLatin1String("1.0")) { + if (documentVersion.contains(QLatin1Char(' '))) + err = QXmlStream::tr("Invalid XML version string."); + else + err = QXmlStream::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")) { + documentEncoding = value; + + if(hasStandalone) + err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding."); + if (!QXmlUtils::isEncName(value)) + err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value); + else { +#ifdef QT_NO_TEXTCODEC + readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); +#else + QTextCodec *const newCodec = QTextCodec::codecForName(value.toLatin1()); + if (!newCodec) + err = QXmlStream::tr("Encoding %1 is unsupported").arg(value); + 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 = QXmlStream::tr("Standalone accepts only yes or no."); + } else { + err = QXmlStream::tr("Invalid attribute in XML declaration."); + } + } + + if (!err.isNull()) + raiseWellFormedError(err); + attributeStack.clear(); +} + + +void QXmlStreamReaderPrivate::raiseError(QXmlStreamReader::Error error, const QString& message) +{ + this->error = error; + errorString = message; + if (errorString.isNull()) { + if (error == QXmlStreamReader::PrematureEndOfDocumentError) + errorString = QXmlStream::tr("Premature end of document."); + else if (error == QXmlStreamReader::CustomError) + errorString = QXmlStream::tr("Invalid document."); + } + + type = QXmlStreamReader::Invalid; +} + +void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message) +{ + raiseError(QXmlStreamReader::NotWellFormedError, message); +} + +void QXmlStreamReaderPrivate::parseError() +{ + + if (token == EOF_SYMBOL) { + raiseError(QXmlStreamReader::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; + } + } + + if (nexpected && nexpected < nmax) { + //: '' + QString exp_str = QXmlStream::tr("'%1'", "expected").arg(QLatin1String(spell[expected[0]])); + if (nexpected == 2) { + //: , '' + exp_str = QXmlStream::tr("%1 or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[1]])); + } else if (nexpected > 2) { + int s = 1; + for (; s < nexpected - 1; ++s) { + //: , '' + exp_str = QXmlStream::tr("%1, '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); + } + //: , or '' + exp_str = QXmlStream::tr("%1, or '%2'", "expected").arg(exp_str, QLatin1String(spell[expected[s]])); + } + error_message = QXmlStream::tr("Expected %1, but got '%2'.").arg(exp_str, QLatin1String(spell[token])); + } else { + error_message = QXmlStream::tr("Unexpected '%1'.").arg(QLatin1String(spell[token])); + } + + raiseWellFormedError(error_message); +} + +void QXmlStreamReaderPrivate::resume(int rule) { + resumeReduction = rule; + if (error == QXmlStreamReader::NoError) + raiseError(QXmlStreamReader::PrematureEndOfDocumentError); +} + +/*! Returns the current line number, starting with 1. + +\sa columnNumber(), characterOffset() + */ +qint64 QXmlStreamReader::lineNumber() const +{ + Q_D(const QXmlStreamReader); + return d->lineNumber + 1; // in public we start with 1 +} + +/*! Returns the current column number, starting with 0. + +\sa lineNumber(), characterOffset() + */ +qint64 QXmlStreamReader::columnNumber() const +{ + Q_D(const QXmlStreamReader); + return d->characterOffset - d->lastLineStart + d->readBufferPos; +} + +/*! Returns the current character offset, starting with 0. + +\sa lineNumber(), columnNumber() +*/ +qint64 QXmlStreamReader::characterOffset() const +{ + Q_D(const QXmlStreamReader); + return d->characterOffset + d->readBufferPos; +} + + +/*! Returns the text of \l Characters, \l Comment, \l DTD, or + EntityReference. + */ +QStringRef QXmlStreamReader::text() const +{ + Q_D(const QXmlStreamReader); + return d->text; +} + + +/*! If the tokenType() is \l DTD, this function returns the DTD's + notation declarations. Otherwise an empty vector is returned. + + The QXmlStreamNotationDeclarations class is defined to be a QVector + of QXmlStreamNotationDeclaration. + */ +QXmlStreamNotationDeclarations QXmlStreamReader::notationDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->notationDeclarations.size()) + const_cast(d)->resolveDtd(); + return d->publicNotationDeclarations; +} + + +/*! If the tokenType() is \l DTD, this function returns the DTD's + unparsed (external) entity declarations. Otherwise an empty vector is returned. + + The QXmlStreamEntityDeclarations class is defined to be a QVector + of QXmlStreamEntityDeclaration. + */ +QXmlStreamEntityDeclarations QXmlStreamReader::entityDeclarations() const +{ + Q_D(const QXmlStreamReader); + if (d->entityDeclarations.size()) + const_cast(d)->resolveDtd(); + return d->publicEntityDeclarations; +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + name. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdName() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdName; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + public identifier. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdPublicId() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdPublicId; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l DTD, this function returns the DTD's + system identifier. Otherwise an empty string is returned. + + */ +QStringRef QXmlStreamReader::dtdSystemId() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::DTD) + return d->dtdSystemId; + return QStringRef(); +} + +/*! If the tokenType() is \l StartElement, this function returns the + element's namespace declarations. Otherwise an empty vector is + returned. + + The QXmlStreamNamespaceDeclarations class is defined to be a QVector + of QXmlStreamNamespaceDeclaration. + + \sa addExtraNamespaceDeclaration(), addExtraNamespaceDeclarations() + */ +QXmlStreamNamespaceDeclarations QXmlStreamReader::namespaceDeclarations() const +{ + Q_D(const QXmlStreamReader); + 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 QXmlStreamReader::addExtraNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &extraNamespaceDeclaration) +{ + Q_D(QXmlStreamReader); + QXmlStreamReaderPrivate::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 QXmlStreamReader::addExtraNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &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 (default). + + \since 4.6 + */ +QString QXmlStreamReader::readElementText(ReadElementTextBehaviour behaviour) +{ + Q_D(QXmlStreamReader); + 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; + } + Q_FALLTHROUGH(); + default: + if (d->error || behaviour == ErrorOnUnexpectedElement) { + if (!d->error) + d->raiseError(UnexpectedElementError, QXmlStream::tr("Expected character data.")); + return result; + } + } + } + } + return QString(); +} + +/*! Raises a custom error with an optional error \a message. + + \sa error(), errorString() + */ +void QXmlStreamReader::raiseError(const QString& message) +{ + Q_D(QXmlStreamReader); + d->raiseError(CustomError, message); +} + +/*! + Returns the error message that was set with raiseError(). + + \sa error(), lineNumber(), columnNumber(), characterOffset() + */ +QString QXmlStreamReader::errorString() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::Invalid) + return d->errorString; + return QString(); +} + +/*! Returns the type of the current error, or NoError if no error occurred. + + \sa errorString(), raiseError() + */ +QXmlStreamReader::Error QXmlStreamReader::error() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::Invalid) + return d->error; + return NoError; +} + +/*! + Returns the target of a ProcessingInstruction. + */ +QStringRef QXmlStreamReader::processingInstructionTarget() const +{ + Q_D(const QXmlStreamReader); + return d->processingInstructionTarget; +} + +/*! + Returns the data of a ProcessingInstruction. + */ +QStringRef QXmlStreamReader::processingInstructionData() const +{ + Q_D(const QXmlStreamReader); + return d->processingInstructionData; +} + + + +/*! + Returns the local name of a StartElement, EndElement, or an EntityReference. + + \sa namespaceUri(), qualifiedName() + */ +QStringRef QXmlStreamReader::name() const +{ + Q_D(const QXmlStreamReader); + return d->name; +} + +/*! + Returns the namespaceUri of a StartElement or EndElement. + + \sa name(), qualifiedName() + */ +QStringRef QXmlStreamReader::namespaceUri() const +{ + Q_D(const QXmlStreamReader); + 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 QXmlStreamReader::qualifiedName() const +{ + Q_D(const QXmlStreamReader); + return d->qualifiedName; +} + + + +/*! + \since 4.4 + + Returns the prefix of a StartElement or EndElement. + + \sa name(), qualifiedName() +*/ +QStringRef QXmlStreamReader::prefix() const +{ + Q_D(const QXmlStreamReader); + return d->prefix; +} + +/*! + Returns the attributes of a StartElement. + */ +QXmlStreamAttributes QXmlStreamReader::attributes() const +{ + Q_D(const QXmlStreamReader); + return d->attributes; +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class QXmlStreamAttribute + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamAttribute 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. + */ +QXmlStreamAttribute::QXmlStreamAttribute() +{ + m_isDefault = false; +} + +/*! + Destructs an attribute. + */ +QXmlStreamAttribute::~QXmlStreamAttribute() +{ +} + +/*! Constructs an attribute in the namespace described with \a + namespaceUri with \a name and value \a value. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); + m_name = m_qualifiedName = QXmlStreamStringRef(QStringRef(&name)); + m_value = QXmlStreamStringRef(QStringRef(&value)); + m_namespaceUri = QXmlStreamStringRef(QStringRef(&namespaceUri)); +} + +/*! + Constructs an attribute with qualified name \a qualifiedName and value \a value. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value) +{ + int colon = qualifiedName.indexOf(QLatin1Char(':')); + m_name = QXmlStreamStringRef(QStringRef(&qualifiedName, + colon + 1, + qualifiedName.size() - (colon + 1))); + m_qualifiedName = QXmlStreamStringRef(QStringRef(&qualifiedName)); + m_value = QXmlStreamStringRef(QStringRef(&value)); +} + +/*! \fn QStringRef QXmlStreamAttribute::namespaceUri() const + + Returns the attribute's resolved namespaceUri, or an empty string + reference if the attribute does not have a defined namespace. + */ +/*! \fn QStringRef QXmlStreamAttribute::name() const + Returns the attribute's local name. + */ +/*! \fn QStringRef QXmlStreamAttribute::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 QXmlStreamAttribute::prefix() const + \since 4.4 + Returns the attribute's namespace prefix. + + \sa name(), qualifiedName() + +*/ + +/*! \fn QStringRef QXmlStreamAttribute::value() const + Returns the attribute's value. + */ + +/*! \fn bool QXmlStreamAttribute::isDefault() const + + Returns \c true if the parser added this attribute with a default + value following an ATTLIST declaration in the DTD; otherwise + returns \c false. +*/ +/*! \fn bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &other) const + + Compares this attribute with \a other and returns \c true if they are + equal; otherwise returns \c false. + */ +/*! \fn bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other) const + + Compares this attribute with \a other and returns \c true if they are + not equal; otherwise returns \c false. + */ + + +/*! + Creates a copy of \a other. + */ +QXmlStreamAttribute::QXmlStreamAttribute(const QXmlStreamAttribute &other) +{ + *this = other; +} + +/*! + Assigns \a other to this attribute. + */ +QXmlStreamAttribute& QXmlStreamAttribute::operator=(const QXmlStreamAttribute &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 QXmlStreamAttributes + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamAttributes class represents a vector of QXmlStreamAttribute. + + Attributes are returned by a QXmlStreamReader in + \l{QXmlStreamReader::attributes()} {attributes()} when the reader + reports a \l {QXmlStreamReader::StartElement}{start element}. The + class can also be used with a QXmlStreamWriter as an argument to + \l {QXmlStreamWriter::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 QXmlStreamAttributes::QXmlStreamAttributes() + + A constructor for QXmlStreamAttributes. +*/ + +/*! + \typedef QXmlStreamNotationDeclarations + \relates QXmlStreamNotationDeclaration + + Synonym for QVector. +*/ + + +/*! + \class QXmlStreamNotationDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamNotationDeclaration 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. +*/ +QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration() +{ +} +/*! + Creates a copy of \a other. + */ +QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration(const QXmlStreamNotationDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this notation declaration. + */ +QXmlStreamNotationDeclaration& QXmlStreamNotationDeclaration::operator=(const QXmlStreamNotationDeclaration &other) +{ + m_name = other.m_name; + m_systemId = other.m_systemId; + m_publicId = other.m_publicId; + return *this; +} + +/*! +Destructs this notation declaration. +*/ +QXmlStreamNotationDeclaration::~QXmlStreamNotationDeclaration() +{ +} + +/*! \fn QStringRef QXmlStreamNotationDeclaration::name() const + +Returns the notation name. +*/ +/*! \fn QStringRef QXmlStreamNotationDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef QXmlStreamNotationDeclaration::publicId() const + +Returns the public identifier. +*/ + +/*! \fn inline bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns \c true + if they are equal; otherwise returns \c false. + */ +/*! \fn inline bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other) const + + Compares this notation declaration with \a other and returns \c true + if they are not equal; otherwise returns \c false. + */ + +/*! + \typedef QXmlStreamNamespaceDeclarations + \relates QXmlStreamNamespaceDeclaration + + Synonym for QVector. +*/ + +/*! + \class QXmlStreamNamespaceDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamNamespaceDeclaration class represents a namespace declaration. + + \ingroup xml-tools + + An namespace declaration consists of a prefix() and a namespaceUri(). +*/ +/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns \c true + if they are equal; otherwise returns \c false. + */ +/*! \fn inline bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other) const + + Compares this namespace declaration with \a other and returns \c true + if they are not equal; otherwise returns \c false. + */ + +/*! + Creates an empty namespace declaration. +*/ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration() +{ +} + +/*! + \since 4.4 + + Creates a namespace declaration with \a prefix and \a namespaceUri. +*/ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri) +{ + m_prefix = prefix; + m_namespaceUri = namespaceUri; +} + +/*! + Creates a copy of \a other. + */ +QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this namespace declaration. + */ +QXmlStreamNamespaceDeclaration& QXmlStreamNamespaceDeclaration::operator=(const QXmlStreamNamespaceDeclaration &other) +{ + m_prefix = other.m_prefix; + m_namespaceUri = other.m_namespaceUri; + return *this; +} +/*! +Destructs this namespace declaration. +*/ +QXmlStreamNamespaceDeclaration::~QXmlStreamNamespaceDeclaration() +{ +} + +/*! \fn QStringRef QXmlStreamNamespaceDeclaration::prefix() const + +Returns the prefix. +*/ +/*! \fn QStringRef QXmlStreamNamespaceDeclaration::namespaceUri() const + +Returns the namespaceUri. +*/ + + + + +/*! + \typedef QXmlStreamEntityDeclarations + \relates QXmlStreamEntityDeclaration + + Synonym for QVector. +*/ + +/*! + \class QXmlStreamStringRef + \inmodule QtCore + \since 4.3 + \internal +*/ + +/*! + \class QXmlStreamEntityDeclaration + \inmodule QtCore + \since 4.3 + \reentrant + \brief The QXmlStreamEntityDeclaration 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. +*/ +QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration() +{ +} + +/*! + Creates a copy of \a other. + */ +QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration(const QXmlStreamEntityDeclaration &other) +{ + *this = other; +} + +/*! + Assigns \a other to this entity declaration. + */ +QXmlStreamEntityDeclaration& QXmlStreamEntityDeclaration::operator=(const QXmlStreamEntityDeclaration &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. +*/ +QXmlStreamEntityDeclaration::~QXmlStreamEntityDeclaration() +{ +} + +/*! \fn QXmlStreamStringRef::swap(QXmlStreamStringRef &other) + \since 5.6 + + Swaps this string reference's contents with \a other. + This function is very fast and never fails. +*/ + +/*! \fn QStringRef QXmlStreamEntityDeclaration::name() const + +Returns the entity name. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::notationName() const + +Returns the notation name. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::systemId() const + +Returns the system identifier. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::publicId() const + +Returns the public identifier. +*/ +/*! \fn QStringRef QXmlStreamEntityDeclaration::value() const + +Returns the entity's value. +*/ + +/*! \fn bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns \c true if + they are equal; otherwise returns \c false. + */ +/*! \fn bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other) const + + Compares this entity declaration with \a other and returns \c true if + they are not equal; otherwise returns \c 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 QXmlStreamAttributes::value(const QString &namespaceUri, const QString &name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &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 QXmlStreamAttributes::value(const QString &namespaceUri, QLatin1String name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &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 QXmlStreamAttributes::value(QLatin1String namespaceUri, QLatin1String name) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &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 QXmlStreamAttributes::value(const QString &qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &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 QXmlStreamAttributes::value(QLatin1String qualifiedName) const +{ + for (int i = 0; i < size(); ++i) { + const QXmlStreamAttribute &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 QXmlStreamAttributes::append(const QString &namespaceUri, const QString &name, const QString &value) +{ + append(QXmlStreamAttribute(namespaceUri, name, value)); +} + +/*!\overload + Appends a new attribute with qualified name \a qualifiedName and + value \a value. + */ +void QXmlStreamAttributes::append(const QString &qualifiedName, const QString &value) +{ + append(QXmlStreamAttribute(qualifiedName, value)); +} + +#ifndef QT_NO_XMLSTREAMREADER + +/*! \fn bool QXmlStreamReader::isStartDocument() const + Returns \c true if tokenType() equals \l StartDocument; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isEndDocument() const + Returns \c true if tokenType() equals \l EndDocument; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isStartElement() const + Returns \c true if tokenType() equals \l StartElement; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isEndElement() const + Returns \c true if tokenType() equals \l EndElement; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isCharacters() const + Returns \c true if tokenType() equals \l Characters; otherwise returns \c false. + + \sa isWhitespace(), isCDATA() +*/ +/*! \fn bool QXmlStreamReader::isComment() const + Returns \c true if tokenType() equals \l Comment; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isDTD() const + Returns \c true if tokenType() equals \l DTD; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isEntityReference() const + Returns \c true if tokenType() equals \l EntityReference; otherwise returns \c false. +*/ +/*! \fn bool QXmlStreamReader::isProcessingInstruction() const + Returns \c true if tokenType() equals \l ProcessingInstruction; otherwise returns \c false. +*/ + +/*! Returns \c true if the reader reports characters that only consist + of white-space; otherwise returns \c false. + + \sa isCharacters(), text() +*/ +bool QXmlStreamReader::isWhitespace() const +{ + Q_D(const QXmlStreamReader); + return d->type == QXmlStreamReader::Characters && d->isWhitespace; +} + +/*! Returns \c true if the reader reports characters that stem from a + CDATA section; otherwise returns \c false. + + \sa isCharacters(), text() +*/ +bool QXmlStreamReader::isCDATA() const +{ + Q_D(const QXmlStreamReader); + return d->type == QXmlStreamReader::Characters && d->isCDATA; +} + + + +/*! + Returns \c true if this document has been declared standalone in the + XML declaration; otherwise returns \c false. + + If no XML declaration has been parsed, this function returns \c false. + */ +bool QXmlStreamReader::isStandaloneDocument() const +{ + Q_D(const QXmlStreamReader); + return d->standalone; +} + + +/*! + \since 4.4 + + If the tokenType() is \l StartDocument, this function returns the + version string as specified in the XML declaration. + Otherwise an empty string is returned. + */ +QStringRef QXmlStreamReader::documentVersion() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::StartDocument) + return d->documentVersion; + return QStringRef(); +} + +/*! + \since 4.4 + + If the tokenType() is \l StartDocument, this function returns the + encoding string as specified in the XML declaration. + Otherwise an empty string is returned. + */ +QStringRef QXmlStreamReader::documentEncoding() const +{ + Q_D(const QXmlStreamReader); + if (d->type == QXmlStreamReader::StartDocument) + return d->documentEncoding; + return QStringRef(); +} + +#endif // QT_NO_XMLSTREAMREADER + +/*! + \class QXmlStreamWriter + \inmodule QtCore + \since 4.3 + \reentrant + + \brief The QXmlStreamWriter class provides an XML writer with a + simple streaming API. + + \ingroup xml-tools + + QXmlStreamWriter is the counterpart to QXmlStreamReader 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 qxmlstreamwriter/main.cpp start stream + \dots + \snippet qxmlstreamwriter/main.cpp write element + \dots + \snippet qxmlstreamwriter/main.cpp finish stream + + QXmlStreamWriter 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, QXmlStreamWriter 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{QXmlStream Bookmarks Example} illustrates how to use a + stream writer to write an XML bookmark file (XBEL) that + was previously read in by a QXmlStreamReader. + +*/ + +#ifndef QT_NO_XMLSTREAMWRITER + +class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack { + QXmlStreamWriter *q_ptr; + Q_DECLARE_PUBLIC(QXmlStreamWriter) +public: + QXmlStreamWriterPrivate(QXmlStreamWriter *q); + ~QXmlStreamWriterPrivate() { + 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 hasIoError :1; + uint hasEncodingError :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); +}; + + +QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *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; + hasIoError = false; + hasEncodingError = false; + lastWasStartElement = false; + lastNamespaceDeclaration = 1; + autoFormatting = false; + namespacePrefixCount = 0; +} + +void QXmlStreamWriterPrivate::checkIfASCIICompatibleCodec() +{ +#ifndef QT_NO_TEXTCODEC + Q_ASSERT(encoder); + // test ASCII-compatibility using the letter 'a' + QChar letterA = QLatin1Char('a'); + const QByteArray bytesA = encoder->fromUnicode(&letterA, 1); + const bool isCodecASCIICompatibleA = (bytesA.count() == 1) && (bytesA[0] == 0x61) ; + QChar letterLess = QLatin1Char('<'); + const QByteArray bytesLess = encoder->fromUnicode(&letterLess, 1); + const bool isCodecASCIICompatibleLess = (bytesLess.count() == 1) && (bytesLess[0] == 0x3C) ; + isCodecASCIICompatible = isCodecASCIICompatibleA && isCodecASCIICompatibleLess ; +#else + isCodecASCIICompatible = true; +#endif +} + +void QXmlStreamWriterPrivate::write(const QStringRef &s) +{ + if (device) { + if (hasIoError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); + if (encoder->hasFailure()) { + hasEncodingError = true; + return; + } +#endif + if (device->write(bytes) != bytes.size()) + hasIoError = true; + } + else if (stringDevice) + s.appendTo(stringDevice); + else + qWarning("QXmlStreamWriter: No device"); +} + +void QXmlStreamWriterPrivate::write(const QString &s) +{ + if (device) { + if (hasIoError) + return; +#ifdef QT_NO_TEXTCODEC + QByteArray bytes = s.toLatin1(); +#else + QByteArray bytes = encoder->fromUnicode(s); + if (encoder->hasFailure()) { + hasEncodingError = true; + return; + } +#endif + if (device->write(bytes) != bytes.size()) + hasIoError = true; + } + else if (stringDevice) + stringDevice->append(s); + else + qWarning("QXmlStreamWriter: No device"); +} + +void QXmlStreamWriterPrivate::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); + switch (c.unicode()) { + case '<': + escaped.append(QLatin1String("<")); + break; + case '>': + escaped.append(QLatin1String(">")); + break; + case '&': + escaped.append(QLatin1String("&")); + break; + case '\"': + escaped.append(QLatin1String(""")); + break; + case '\t': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + case '\n': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + case '\v': + case '\f': + hasEncodingError = true; + break; + case '\r': + if (escapeWhitespace) + escaped.append(QLatin1String(" ")); + else + escaped += c; + break; + default: + if (c.unicode() > 0x1f && c.unicode() < 0xfffe) + escaped += c; + else + hasEncodingError = true; + break; + } + } + write(escaped); +} + +// Converts from ASCII to output encoding +void QXmlStreamWriterPrivate::write(const char *s, int len) +{ + if (device) { + if (hasIoError) + return; + if (isCodecASCIICompatible) { + if (device->write(s, len) != len) + hasIoError = true; + return; + } + } + + write(QString::fromLatin1(s, len)); +} + +void QXmlStreamWriterPrivate::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 QXmlStreamWriterPrivate::finishStartElement(bool contents) +{ + bool hadSomethingWritten = wroteSomething; + wroteSomething = contents; + if (!inStartElement) + return hadSomethingWritten; + + if (inEmptyElement) { + write("/>"); + QXmlStreamWriterPrivate::Tag &tag = tagStack_pop(); + lastNamespaceDeclaration = tag.namespaceDeclarationsSize; + lastWasStartElement = false; + } else { + write(">"); + } + inStartElement = inEmptyElement = false; + lastNamespaceDeclaration = namespaceDeclarations.size(); + return hadSomethingWritten; +} + +QXmlStreamPrivateTagStack::NamespaceDeclaration &QXmlStreamWriterPrivate::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 QXmlStreamWriterPrivate::indent(int level) +{ + write("\n"); + for (int i = level; i > 0; --i) + write(autoFormattingIndent.constData(), autoFormattingIndent.length()); +} + + +/*! + Constructs a stream writer. + + \sa setDevice() + */ +QXmlStreamWriter::QXmlStreamWriter() + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ +} + +/*! + Constructs a stream writer that writes into \a device; + */ +QXmlStreamWriter::QXmlStreamWriter(QIODevice *device) + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ + Q_D(QXmlStreamWriter); + 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. + */ +QXmlStreamWriter::QXmlStreamWriter(QByteArray *array) + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ + Q_D(QXmlStreamWriter); + d->device = new QBuffer(array); + d->device->open(QIODevice::WriteOnly); + d->deleteDevice = true; +} + + +/*! Constructs a stream writer that writes into \a string. + */ +QXmlStreamWriter::QXmlStreamWriter(QString *string) + : d_ptr(new QXmlStreamWriterPrivate(this)) +{ + Q_D(QXmlStreamWriter); + d->stringDevice = string; +} + +/*! + Destructor. +*/ +QXmlStreamWriter::~QXmlStreamWriter() +{ +} + + +/*! + 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 QXmlStreamWriter::setDevice(QIODevice *device) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter, + or 0 if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QXmlStreamWriter::device() const +{ + Q_D(const QXmlStreamWriter); + 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, QXmlStreamWriter + 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 QXmlStreamWriter::setCodec(QTextCodec *codec) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::setCodec(const char *codecName) +{ + setCodec(QTextCodec::codecForName(codecName)); +} + +/*! + Returns the codec that is currently assigned to the stream. + + \sa setCodec() +*/ +QTextCodec *QXmlStreamWriter::codec() const +{ + Q_D(const QXmlStreamWriter); + return d->codec; +} +#endif // QT_NO_TEXTCODEC + +/*! + \property QXmlStreamWriter::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 QXmlStreamWriter::setAutoFormatting(bool enable) +{ + Q_D(QXmlStreamWriter); + d->autoFormatting = enable; +} + +/*! + \since 4.4 + + Returns \c true if auto formattting is enabled, otherwise \c false. + */ +bool QXmlStreamWriter::autoFormatting() const +{ + Q_D(const QXmlStreamWriter); + return d->autoFormatting; +} + +/*! + \property QXmlStreamWriter::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 QXmlStreamWriter::setAutoFormattingIndent(int spacesOrTabs) +{ + Q_D(QXmlStreamWriter); + d->autoFormattingIndent = QByteArray(qAbs(spacesOrTabs), spacesOrTabs >= 0 ? ' ' : '\t'); +} + +int QXmlStreamWriter::autoFormattingIndent() const +{ + Q_D(const QXmlStreamWriter); + return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); +} + +/*! + Returns \c true if writing failed. + + This can happen if the stream failed to write to the underlying + device or if the data to be written contained invalid characters. + + The error status is never reset. Writes happening after the error + occurred may be ignored, even if the error condition is cleared. + */ +bool QXmlStreamWriter::hasError() const +{ + Q_D(const QXmlStreamWriter); + return d->hasIoError || d->hasEncodingError; +} + +/*! + \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 QXmlStreamWriter::writeAttribute(const QString &qualifiedName, const QString &value) +{ + Q_D(QXmlStreamWriter); + 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, QXmlStreamWriter will generate a namespace declaration + for it. + + This function can only be called after writeStartElement() before + any content is written, or after writeEmptyElement(). + */ +void QXmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString &name, const QString &value) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(d->inStartElement); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + QXmlStreamWriterPrivate::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 QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute& 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, QXmlStreamWriter + 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 QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes& attributes) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::writeCDATA(const QString &text) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::writeCharacters(const QString &text) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::writeComment(const QString &text) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::writeDTD(const QString &dtd) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::writeEmptyElement(const QString &qualifiedName) +{ + Q_D(QXmlStreamWriter); + 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, + QXmlStreamWriter will generate a namespace declaration for it. + Subsequent calls to writeAttribute() will add attributes to this element. + + \sa writeNamespace() + */ +void QXmlStreamWriter::writeEmptyElement(const QString &namespaceUri, const QString &name) +{ + Q_D(QXmlStreamWriter); + 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 code/src_corelib_xml_qxmlstream.cpp 1 + +*/ +void QXmlStreamWriter::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, QXmlStreamWriter will generate a namespace declaration + for it. + + + This is a convenience function equivalent to: + \snippet code/src_corelib_xml_qxmlstream.cpp 2 + +*/ +void QXmlStreamWriter::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 QXmlStreamWriter::writeEndDocument() +{ + Q_D(QXmlStreamWriter); + while (d->tagStack.size()) + writeEndElement(); + d->write("\n"); +} + +/*! + Closes the previous start element. + + \sa writeStartElement() + */ +void QXmlStreamWriter::writeEndElement() +{ + Q_D(QXmlStreamWriter); + 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; + QXmlStreamWriterPrivate::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; + QXmlStreamWriterPrivate::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 QXmlStreamWriter::writeEntityReference(const QString &name) +{ + Q_D(QXmlStreamWriter); + d->finishStartElement(); + d->write("&"); + d->write(name); + d->write(";"); +} + + +/*! Writes a namespace declaration for \a namespaceUri with \a + prefix. If \a prefix is empty, QXmlStreamWriter 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 QXmlStreamWriter::writeNamespace(const QString &namespaceUri, const QString &prefix) +{ + Q_D(QXmlStreamWriter); + 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/")); + QXmlStreamWriterPrivate::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 QXmlStreamWriter::writeDefaultNamespace(const QString &namespaceUri) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/XML/1998/namespace")); + Q_ASSERT(namespaceUri != QLatin1String("http://www.w3.org/2000/xmlns/")); + QXmlStreamWriterPrivate::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 QXmlStreamWriter::writeProcessingInstruction(const QString &target, const QString &data) +{ + Q_D(QXmlStreamWriter); + 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 QXmlStreamWriter::writeStartDocument() +{ + writeStartDocument(QLatin1String("1.0")); +} + + +/*! + Writes a document start with the XML version number \a version. + + \sa writeEndDocument() + */ +void QXmlStreamWriter::writeStartDocument(const QString &version) +{ + Q_D(QXmlStreamWriter); + 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 + const QByteArray name = d->codec->name(); + d->write(name.constData(), 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 QXmlStreamWriter::writeStartDocument(const QString &version, bool standalone) +{ + Q_D(QXmlStreamWriter); + 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 + const QByteArray name = d->codec->name(); + d->write(name.constData(), 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 QXmlStreamWriter::writeStartElement(const QString &qualifiedName) +{ + Q_D(QXmlStreamWriter); + 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, + QXmlStreamWriter will generate a namespace declaration for + it. Subsequent calls to writeAttribute() will add attributes to this + element. + + \sa writeNamespace(), writeEndElement(), writeEmptyElement() + */ +void QXmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name) +{ + Q_D(QXmlStreamWriter); + Q_ASSERT(!name.contains(QLatin1Char(':'))); + d->writeStartElement(namespaceUri, name); +} + +void QXmlStreamWriterPrivate::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 QXmlStreamReader::tokenType() + */ +void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader) +{ + switch (reader.tokenType()) { + case QXmlStreamReader::NoToken: + break; + case QXmlStreamReader::StartDocument: + writeStartDocument(); + break; + case QXmlStreamReader::EndDocument: + writeEndDocument(); + break; + case QXmlStreamReader::StartElement: { + QXmlStreamNamespaceDeclarations namespaceDeclarations = reader.namespaceDeclarations(); + for (int i = 0; i < namespaceDeclarations.size(); ++i) { + const QXmlStreamNamespaceDeclaration &namespaceDeclaration = namespaceDeclarations.at(i); + writeNamespace(namespaceDeclaration.namespaceUri().toString(), + namespaceDeclaration.prefix().toString()); + } + writeStartElement(reader.namespaceUri().toString(), reader.name().toString()); + writeAttributes(reader.attributes()); + } break; + case QXmlStreamReader::EndElement: + writeEndElement(); + break; + case QXmlStreamReader::Characters: + if (reader.isCDATA()) + writeCDATA(reader.text().toString()); + else + writeCharacters(reader.text().toString()); + break; + case QXmlStreamReader::Comment: + writeComment(reader.text().toString()); + break; + case QXmlStreamReader::DTD: + writeDTD(reader.text().toString()); + break; + case QXmlStreamReader::EntityReference: + writeEntityReference(reader.name().toString()); + break; + case QXmlStreamReader::ProcessingInstruction: + writeProcessingInstruction(reader.processingInstructionTarget().toString(), + reader.processingInstructionData().toString()); + break; + default: + Q_ASSERT(reader.tokenType() != QXmlStreamReader::Invalid); + qWarning("QXmlStreamWriter: writeCurrentToken() with invalid state."); + break; + } +} + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const + \since 4.5 + + Returns \c true if this QXmlStreamAttributes has an attribute whose + qualified name is \a qualifiedName; otherwise returns \c false. + + Note that this is not namespace aware. For instance, if this + QXmlStreamAttributes 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 QXmlStreamAttributes::hasAttribute(QLatin1String qualifiedName) const + \overload + \since 4.5 +*/ + +/*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &namespaceUri, + const QString &name) const + \overload + \since 4.5 + + Returns \c true if this QXmlStreamAttributes has an attribute whose + namespace URI and name correspond to \a namespaceUri and \a name; + otherwise returns \c false. +*/ + +#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMWRITER + +QT_END_NAMESPACE + +#endif // QT_NO_XMLSTREAM -- cgit v1.2.3