From 393f5d5b2705c0ed7e6e1a3a69cc9cdf16cf334d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 17 Aug 2009 16:55:58 +0200 Subject: Added two convenience functions to QXmlStreamReader QXmlStreamReader::readNextStartElement reads until the next start element within the current element, or returns false when no such element is encountered before the end element is reached. It simplifies the common case of iterating over the elements in an XML document. QXmlStreamReader::skipCurrentElement reads until the end element of the current element, skipping any child elements. This functionality was requested in two tasks, and a similar function 'readUnknownElement' was present in Qt's stream reader example. Autotest is included, example and documentation have been updated. Task-number: 238793 Reviewed-by: mae --- doc/src/examples/qxmlstreambookmarks.qdoc | 13 ++- examples/network/googlesuggest/googlesuggest.cpp | 7 +- examples/xml/streambookmarks/xbelreader.cpp | 107 +++++++---------------- examples/xml/streambookmarks/xbelreader.h | 1 - src/corelib/xml/qxmlstream.cpp | 49 +++++++++++ src/corelib/xml/qxmlstream.h | 3 + tests/auto/qxmlstream/tst_qxmlstream.cpp | 19 ++++ 7 files changed, 114 insertions(+), 85 deletions(-) diff --git a/doc/src/examples/qxmlstreambookmarks.qdoc b/doc/src/examples/qxmlstreambookmarks.qdoc index def4c47..26964c4 100644 --- a/doc/src/examples/qxmlstreambookmarks.qdoc +++ b/doc/src/examples/qxmlstreambookmarks.qdoc @@ -106,19 +106,18 @@ of reading only takes place if the file is a valid XBEL 1.0 file. Note that the XML input needs to be well-formed to be accepted by QXmlStreamReader. Otherwise, the \l{QXmlStreamReader::raiseError()} - {raiseError()} function is used to display an error message. + {raiseError()} function is used to display an error message. Since the + XBEL reader is only concerned with reading XML elements, it makes + extensive use of the \l{QXmlStreamReader::readNextStartElement()} + convenience function. \snippet examples/xml/streambookmarks/xbelreader.cpp 1 - The \c readUnknownElement() function reads an unknown element. The - Q_ASSERT() macro is used to provide a pre-condition for the function. - - \snippet examples/xml/streambookmarks/xbelreader.cpp 2 - The \c readXBEL() function reads the name of a startElement and calls the appropriate function to read it, depending on whether if its a "folder", "bookmark" or "separator". Otherwise, it calls - \c readUnknownElement(). + \l{QXmlStreamReader::skipCurrentElement()}. The Q_ASSERT() macro is used + to provide a pre-condition for the function. \snippet examples/xml/streambookmarks/xbelreader.cpp 3 diff --git a/examples/network/googlesuggest/googlesuggest.cpp b/examples/network/googlesuggest/googlesuggest.cpp index ada0edf..4142511 100644 --- a/examples/network/googlesuggest/googlesuggest.cpp +++ b/examples/network/googlesuggest/googlesuggest.cpp @@ -134,7 +134,6 @@ bool GSuggestCompletion::eventFilter(QObject *obj, QEvent *ev) void GSuggestCompletion::showCompletion(const QStringList &choices, const QStringList &hits) { - if (choices.isEmpty() || choices.count() != hits.count()) return; @@ -204,16 +203,16 @@ void GSuggestCompletion::handleNetworkData(QNetworkReply *networkReply) QXmlStreamReader xml(response); while (!xml.atEnd()) { xml.readNext(); - if (xml.tokenType() == QXmlStreamReader::StartElement) + if (xml.isStartElement()) { if (xml.name() == "suggestion") { QStringRef str = xml.attributes().value("data"); choices << str.toString(); } - if (xml.tokenType() == QXmlStreamReader::StartElement) - if (xml.name() == "num_queries") { + else if (xml.name() == "num_queries") { QStringRef str = xml.attributes().value("int"); hits << str.toString(); } + } } showCompletion(choices, hits); diff --git a/examples/xml/streambookmarks/xbelreader.cpp b/examples/xml/streambookmarks/xbelreader.cpp index 47c8c3d..729fcf8 100644 --- a/examples/xml/streambookmarks/xbelreader.cpp +++ b/examples/xml/streambookmarks/xbelreader.cpp @@ -62,59 +62,31 @@ bool XbelReader::read(QIODevice *device) { setDevice(device); - while (!atEnd()) { - readNext(); - - if (isStartElement()) { - if (name() == "xbel" && attributes().value("version") == "1.0") - readXBEL(); - else - raiseError(QObject::tr("The file is not an XBEL version 1.0 file.")); - } + if (readNextStartElement()) { + if (name() == "xbel" && attributes().value("version") == "1.0") + readXBEL(); + else + raiseError(QObject::tr("The file is not an XBEL version 1.0 file.")); } return !error(); } //! [1] -//! [2] -void XbelReader::readUnknownElement() -{ - Q_ASSERT(isStartElement()); - - while (!atEnd()) { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) - readUnknownElement(); - } -} -//! [2] - //! [3] void XbelReader::readXBEL() { Q_ASSERT(isStartElement() && name() == "xbel"); - while (!atEnd()) { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) { - if (name() == "folder") - readFolder(0); - else if (name() == "bookmark") - readBookmark(0); - else if (name() == "separator") - readSeparator(0); - else - readUnknownElement(); - } + while (readNextStartElement()) { + if (name() == "folder") + readFolder(0); + else if (name() == "bookmark") + readBookmark(0); + else if (name() == "separator") + readSeparator(0); + else + readUnknownElement(); } } //! [3] @@ -132,10 +104,12 @@ void XbelReader::readTitle(QTreeWidgetItem *item) //! [5] void XbelReader::readSeparator(QTreeWidgetItem *item) { + Q_ASSERT(isStartElement() && name() == "separator"); + QTreeWidgetItem *separator = createChildItem(item); separator->setFlags(item->flags() & ~Qt::ItemIsSelectable); separator->setText(0, QString(30, 0xB7)); - readElementText(); + skipCurrentElement(); } //! [5] @@ -147,24 +121,17 @@ void XbelReader::readFolder(QTreeWidgetItem *item) bool folded = (attributes().value("folded") != "no"); treeWidget->setItemExpanded(folder, !folded); - while (!atEnd()) { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) { - if (name() == "title") - readTitle(folder); - else if (name() == "folder") - readFolder(folder); - else if (name() == "bookmark") - readBookmark(folder); - else if (name() == "separator") - readSeparator(folder); - else - readUnknownElement(); - } + while (readNextStartElement()) { + if (name() == "title") + readTitle(folder); + else if (name() == "folder") + readFolder(folder); + else if (name() == "bookmark") + readBookmark(folder); + else if (name() == "separator") + readSeparator(folder); + else + skipCurrentElement(); } } @@ -177,18 +144,12 @@ void XbelReader::readBookmark(QTreeWidgetItem *item) bookmark->setIcon(0, bookmarkIcon); bookmark->setText(0, QObject::tr("Unknown title")); bookmark->setText(1, attributes().value("href").toString()); - while (!atEnd()) { - readNext(); - - if (isEndElement()) - break; - - if (isStartElement()) { - if (name() == "title") - readTitle(bookmark); - else - readUnknownElement(); - } + + while (readNextStartElement()) { + if (name() == "title") + readTitle(bookmark); + else + skipCurrentElement(); } } diff --git a/examples/xml/streambookmarks/xbelreader.h b/examples/xml/streambookmarks/xbelreader.h index 80f0a28..2debadc 100644 --- a/examples/xml/streambookmarks/xbelreader.h +++ b/examples/xml/streambookmarks/xbelreader.h @@ -62,7 +62,6 @@ public: private: //! [2] - void readUnknownElement(); void readXBEL(); void readTitle(QTreeWidgetItem *item); void readSeparator(QTreeWidgetItem *item); diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index a08b167..8b2462e 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -622,6 +622,55 @@ QXmlStreamReader::TokenType QXmlStreamReader::tokenType() const return d->type; } +/*! + Reads until the next start element within the current element. Returns true + when a start element was reached. When the end element was reached, or when + an error occurred, false is returned. + + The current element is the element matching the most recently parsed start + element of which a matching end element has not yet been reached. When the + parser has reached the end element, the current element becomes the parent + element. + + 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 + */ +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 ==== diff --git a/src/corelib/xml/qxmlstream.h b/src/corelib/xml/qxmlstream.h index 420a66a..21dcb40 100644 --- a/src/corelib/xml/qxmlstream.h +++ b/src/corelib/xml/qxmlstream.h @@ -321,6 +321,9 @@ public: bool atEnd() const; TokenType readNext(); + bool readNextStartElement(); + void skipCurrentElement(); + TokenType tokenType() const; QString tokenString() const; diff --git a/tests/auto/qxmlstream/tst_qxmlstream.cpp b/tests/auto/qxmlstream/tst_qxmlstream.cpp index 375528c..f496dcf 100644 --- a/tests/auto/qxmlstream/tst_qxmlstream.cpp +++ b/tests/auto/qxmlstream/tst_qxmlstream.cpp @@ -550,6 +550,7 @@ private slots: void setEntityResolver(); void readFromQBuffer() const; void readFromQBufferInvalid() const; + void readNextStartElement() const; void crashInUTF16Codec() const; void hasAttributeSignature() const; void hasAttribute() const; @@ -1107,6 +1108,24 @@ void tst_QXmlStream::readFromQBufferInvalid() const QVERIFY(reader.hasError()); } +void tst_QXmlStream::readNextStartElement() const +{ + QLatin1String in("text"); + QXmlStreamReader reader(in); + + QVERIFY(reader.readNextStartElement()); + QVERIFY(reader.isStartElement() && reader.name() == "A"); + + int amountOfB = 0; + while (reader.readNextStartElement()) { + QVERIFY(reader.isStartElement() && reader.name() == "B"); + ++amountOfB; + reader.skipCurrentElement(); + } + + QCOMPARE(amountOfB, 2); +} + void tst_QXmlStream::crashInUTF16Codec() const { QEventLoop eventLoop; -- cgit v0.12