summaryrefslogtreecommitdiffstats
path: root/src/xmlpatterns/acceltree
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/xmlpatterns/acceltree
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'src/xmlpatterns/acceltree')
-rw-r--r--src/xmlpatterns/acceltree/acceltree.pri10
-rw-r--r--src/xmlpatterns/acceltree/qacceliterators.cpp181
-rw-r--r--src/xmlpatterns/acceltree/qacceliterators_p.h413
-rw-r--r--src/xmlpatterns/acceltree/qacceltree.cpp706
-rw-r--r--src/xmlpatterns/acceltree/qacceltree_p.h404
-rw-r--r--src/xmlpatterns/acceltree/qacceltreebuilder.cpp429
-rw-r--r--src/xmlpatterns/acceltree/qacceltreebuilder_p.h187
-rw-r--r--src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp411
-rw-r--r--src/xmlpatterns/acceltree/qacceltreeresourceloader_p.h195
-rw-r--r--src/xmlpatterns/acceltree/qcompressedwhitespace.cpp197
-rw-r--r--src/xmlpatterns/acceltree/qcompressedwhitespace_p.h186
11 files changed, 3319 insertions, 0 deletions
diff --git a/src/xmlpatterns/acceltree/acceltree.pri b/src/xmlpatterns/acceltree/acceltree.pri
new file mode 100644
index 0000000..284371a
--- /dev/null
+++ b/src/xmlpatterns/acceltree/acceltree.pri
@@ -0,0 +1,10 @@
+SOURCES += $$PWD/qacceltree.cpp \
+ $$PWD/qacceltreeresourceloader.cpp \
+ $$PWD/qacceliterators.cpp \
+ $$PWD/qcompressedwhitespace.cpp
+
+HEADERS += $$PWD/qacceltreebuilder_p.h \
+ $$PWD/qacceltree_p.h \
+ $$PWD/qacceltreeresourceloader_p.h \
+ $$PWD/qacceliterators_p.h \
+ $$PWD/qcompressedwhitespace_p.h
diff --git a/src/xmlpatterns/acceltree/qacceliterators.cpp b/src/xmlpatterns/acceltree/qacceliterators.cpp
new file mode 100644
index 0000000..152c5d8
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceliterators.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtDebug>
+
+#include "qacceliterators_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+xsInteger AccelIterator::position() const
+{
+ return m_position;
+}
+
+QXmlNodeModelIndex AccelIterator::current() const
+{
+ return m_current;
+}
+
+QXmlNodeModelIndex FollowingIterator::next()
+{
+ /* "the following axis contains all nodes that are descendants
+ * of the root of the tree in which the context node is found,
+ * are not descendants of the context node, and occur after
+ * the context node in document order." */
+
+ if(m_position == 0)
+ {
+ /* Skip the descendants. */
+ m_currentPre += m_document->size(m_preNumber) + 1;
+ }
+
+ if(m_currentPre > m_document->maximumPreNumber())
+ return closedExit();
+
+ while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
+ {
+ ++m_currentPre;
+ if(m_currentPre > m_document->maximumPreNumber())
+ return closedExit();
+ }
+
+ m_current = m_document->createIndex(m_currentPre);
+ ++m_position;
+ ++m_currentPre;
+ return m_current;
+}
+
+QXmlNodeModelIndex PrecedingIterator::next()
+{
+ if(m_currentPre == -1)
+ return closedExit();
+
+ /* We skip ancestors and attributes and take into account that they can be intermixed. If one
+ * skips them in two separate loops, one can end up with skipping all the attributes to then
+ * be positioned at an ancestor(which will be accepted because the ancestor loop was before the
+ * attributes loop). */
+ while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute ||
+ m_document->postNumber(m_currentPre) > m_postNumber)
+ {
+ --m_currentPre;
+ if(m_currentPre == -1)
+ return closedExit();
+ }
+
+ if(m_currentPre == -1)
+ {
+ m_currentPre = -1;
+ return closedExit();
+ }
+
+ /* Phew, m_currentPre is now 1) not an ancestor; and
+ * 2) not an attribute; and 3) preceds the context node. */
+
+ m_current = m_document->createIndex(m_currentPre);
+ ++m_position;
+ --m_currentPre;
+
+ return m_current;
+}
+
+QXmlNodeModelIndex::Iterator::Ptr PrecedingIterator::copy() const
+{
+ return QXmlNodeModelIndex::Iterator::Ptr(new PrecedingIterator(m_document, m_preNumber));
+}
+
+QXmlNodeModelIndex::Iterator::Ptr FollowingIterator::copy() const
+{
+ return QXmlNodeModelIndex::Iterator::Ptr(new FollowingIterator(m_document, m_preNumber));
+}
+
+QXmlNodeModelIndex ChildIterator::next()
+{
+ if(m_currentPre == -1)
+ return closedExit();
+
+ ++m_position;
+ m_current = m_document->createIndex(m_currentPre);
+
+ /* We get the count of the descendants, and increment m_currentPre. After
+ * this, m_currentPre is the node after the descendants. */
+ m_currentPre += m_document->size(m_currentPre);
+ ++m_currentPre;
+
+ if(m_currentPre > m_document->maximumPreNumber() || m_document->depth(m_currentPre) != m_depth)
+ m_currentPre = -1;
+
+ return m_current;
+}
+
+QXmlNodeModelIndex::Iterator::Ptr ChildIterator::copy() const
+{
+ return QXmlNodeModelIndex::Iterator::Ptr(new ChildIterator(m_document, m_preNumber));
+}
+
+QXmlNodeModelIndex AttributeIterator::next()
+{
+ if(m_currentPre == -1)
+ return closedExit();
+ else
+ {
+ m_current = m_document->createIndex(m_currentPre);
+ ++m_position;
+
+ ++m_currentPre;
+
+ if(m_currentPre > m_document->maximumPreNumber() ||
+ m_document->kind(m_currentPre) != QXmlNodeModelIndex::Attribute)
+ m_currentPre = -1;
+
+ return m_current;
+ }
+}
+
+QXmlNodeModelIndex::Iterator::Ptr AttributeIterator::copy() const
+{
+ return QXmlNodeModelIndex::Iterator::Ptr(new AttributeIterator(m_document, m_preNumber));
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/xmlpatterns/acceltree/qacceliterators_p.h b/src/xmlpatterns/acceltree/qacceliterators_p.h
new file mode 100644
index 0000000..545ff73
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceliterators_p.h
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_AccelIterators_H
+#define Patternist_AccelIterators_H
+
+#include "qacceltree_p.h"
+#include "qitem_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+namespace QPatternist
+{
+ /**
+ * @short Abstract base class for Iterators for the AccelTree, that
+ * contains common functions and members.
+ *
+ * @author Frans Englich<fenglich@trolltech.com>
+ */
+ class AccelIterator : public QXmlNodeModelIndex::Iterator
+ {
+ public:
+ virtual xsInteger position() const;
+ virtual QXmlNodeModelIndex current() const;
+
+ protected:
+ inline AccelIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre,
+ const AccelTree::PreNumber currentPre) : m_document(doc)
+ , m_preNumber(pre)
+ , m_currentPre(currentPre)
+ , m_position(0)
+
+ {
+ Q_ASSERT(m_document);
+ Q_ASSERT(m_preNumber >= 0);
+ }
+
+ inline QXmlNodeModelIndex closedExit()
+ {
+ m_position = -1;
+ m_current.reset();
+ return QXmlNodeModelIndex();
+ }
+
+ /**
+ * We do not own it.
+ */
+ const AccelTree *const m_document;
+
+ /**
+ * The pre number of the node that should be navigated from.
+ */
+ const AccelTree::PreNumber m_preNumber;
+ AccelTree::PreNumber m_currentPre;
+ xsInteger m_position;
+ QXmlNodeModelIndex m_current;
+ };
+
+ /**
+ * @short Iterates along the @c ancestor or @c ancestor-or-self axis in an AccelTree.
+ *
+ * @author Frans Englich<fenglich@trolltech.com>
+ */
+ template<const bool IncludeSelf>
+ class AncestorIterator : public AccelIterator
+ {
+ public:
+ /**
+ * @p pre is the node from which iteration starts
+ * from. In the @c ancestor axis it is excluded,
+ * while in @c ancestor-or-self it is included. @p pre
+ * must have at least one ancestor.
+ */
+ inline AncestorIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre) : AccelIterator(doc, pre, IncludeSelf ? pre : doc->basicData.at(pre).parent())
+ {
+ Q_ASSERT(IncludeSelf || m_document->hasParent(pre));
+ }
+
+ virtual QXmlNodeModelIndex next()
+ {
+ if(m_currentPre == -1)
+ return closedExit();
+ else
+ {
+ ++m_position;
+ m_current = m_document->createIndex(m_currentPre);
+ m_currentPre = m_document->basicData.at(m_currentPre).parent();
+
+ return m_current;
+ }
+ }
+
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const
+ {
+ return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<IncludeSelf>(m_document, m_preNumber));
+ }
+ };
+
+ /**
+ * @short Iterates along the @c child axis in an AccelTree.
+ *
+ * @author Frans Englich<fenglich@trolltech.com>
+ */
+ class ChildIterator : public AccelIterator
+ {
+ public:
+ /**
+ * @p pre must have at least one child.
+ */
+ inline ChildIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + 1),
+ m_depth(m_document->depth(m_currentPre))
+ {
+ Q_ASSERT(m_document->hasChildren(pre));
+
+ /* Skip the attributes, that are children in the pre/post plane, of
+ * the node we're applying the child axis to. */
+ while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
+ {
+ ++m_currentPre;
+ /* We check the depth here because we would otherwise include
+ * following siblings. */
+ if(m_currentPre > m_document->maximumPreNumber() || m_document->depth(m_currentPre) != m_depth)
+ {
+ m_currentPre = -1;
+ break;
+ }
+ }
+ }
+
+ virtual QXmlNodeModelIndex next();
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
+
+ private:
+ const AccelTree::Depth m_depth;
+ };
+
+ /**
+ * @short Iterates along the sibling axes in an AccelTree.
+ *
+ * @author Frans Englich<fenglich@trolltech.com>
+ */
+ template<const bool IsFollowing>
+ class SiblingIterator : public AccelIterator
+ {
+ public:
+ inline SiblingIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + (IsFollowing ? 0 : -1)),
+ m_depth(doc->depth(pre))
+ {
+ Q_ASSERT_X(IsFollowing || pre != 0, "",
+ "When being preceding-sibling, the context node cannot be the first node in the document.");
+ Q_ASSERT_X(!IsFollowing || pre != m_document->maximumPreNumber(), "",
+ "When being following-sibling, the context node cannot be the last node in the document.");
+ }
+
+ virtual QXmlNodeModelIndex next()
+ {
+ if(m_currentPre == -1)
+ return QXmlNodeModelIndex();
+
+ if(IsFollowing)
+ {
+ /* Skip the descendants, and jump to the next node. */
+ m_currentPre += m_document->size(m_currentPre) + 1;
+
+ if(m_currentPre > m_document->maximumPreNumber() || m_document->depth(m_currentPre) != m_depth)
+ return closedExit();
+ else
+ {
+ ++m_position;
+ m_current = m_document->createIndex(m_currentPre);
+ return m_current;
+ }
+ }
+ else
+ {
+ while(m_document->depth(m_currentPre) > m_depth)
+ --m_currentPre;
+
+ while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
+ --m_currentPre;
+
+ if(m_document->depth(m_currentPre) == m_depth &&
+ m_document->kind(m_currentPre) != QXmlNodeModelIndex::Attribute)
+ {
+ m_current = m_document->createIndex(m_currentPre);
+ ++m_position;
+ --m_currentPre;
+ return m_current;
+ }
+ else
+ {
+ m_currentPre = -1;
+ return closedExit();
+ }
+ }
+ }
+
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const
+ {
+ return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<IsFollowing>(m_document, m_preNumber));
+ }
+
+ private:
+ const AccelTree::Depth m_depth;
+ };
+
+ /**
+ * @short Implements axis @c descendant and @c descendant-or-self for the
+ * AccelTree.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ template<const bool IncludeSelf>
+ class DescendantIterator : public AccelIterator
+ {
+ public:
+ /**
+ * @p pre must have at least one child.
+ */
+ inline DescendantIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + (IncludeSelf ? 0 : 1)),
+ m_postNumber(doc->postNumber(pre))
+ {
+ Q_ASSERT(IncludeSelf || m_document->hasChildren(pre));
+
+ /* Make sure that m_currentPre is the first node part of this axis.
+ * Since we're not including ourself, advance to the node after our
+ * attributes, if any. */
+ if(!IncludeSelf)
+ {
+ while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
+ {
+ ++m_currentPre;
+ /* We check the depth here because we would otherwise include
+ * following siblings. */
+ if(m_currentPre > m_document->maximumPreNumber() || m_document->postNumber(m_currentPre) > m_postNumber)
+ {
+ m_currentPre = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ virtual QXmlNodeModelIndex next()
+ {
+ if(m_currentPre == -1)
+ return closedExit();
+
+ ++m_position;
+ m_current = m_document->createIndex(m_currentPre);
+
+ ++m_currentPre;
+
+ if(m_currentPre > m_document->maximumPreNumber())
+ {
+ m_currentPre = -1;
+ return m_current;
+ }
+
+ if(m_document->postNumber(m_currentPre) < m_postNumber)
+ {
+ while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
+ {
+ ++m_currentPre;
+ if(m_currentPre > m_document->maximumPreNumber())
+ {
+ m_currentPre = -1;
+ break;
+ }
+ }
+ }
+ else
+ m_currentPre = -1;
+
+ return m_current;
+ }
+
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const
+ {
+ return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<IncludeSelf>(m_document, m_preNumber));
+ }
+
+ private:
+ const AccelTree::PreNumber m_postNumber;
+ };
+
+ /**
+ * @short Implements axis @c following for the AccelTree.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ class FollowingIterator : public AccelIterator
+ {
+ public:
+ /**
+ * @ pre must have at least one child.
+ */
+ inline FollowingIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre)
+ {
+ }
+
+ virtual QXmlNodeModelIndex next();
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
+ };
+
+ /**
+ * @short Implements axis @c preceding for the AccelTree.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ class PrecedingIterator : public AccelIterator
+ {
+ public:
+ /**
+ * @ pre must have at least one child.
+ */
+ inline PrecedingIterator(const AccelTree *const doc,
+ const AccelTree::PreNumber pre) : AccelIterator(doc, pre,
+ pre - 1 /* currentPre */)
+ , m_postNumber(m_document->postNumber(m_preNumber))
+ {
+ }
+
+ virtual QXmlNodeModelIndex next();
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
+
+ private:
+ const AccelTree::PreNumber m_postNumber;
+ };
+
+ /**
+ * @short Implements axis @c attribute for the AccelTree.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ class AttributeIterator : public AccelIterator
+ {
+ public:
+ /**
+ * @p pre must have at least one child.
+ */
+ inline AttributeIterator(const AccelTree *const doc, const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + 1)
+ {
+ Q_ASSERT(m_document->hasChildren(pre));
+ Q_ASSERT(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute);
+ }
+
+ virtual QXmlNodeModelIndex next();
+ virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
+ };
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/xmlpatterns/acceltree/qacceltree.cpp b/src/xmlpatterns/acceltree/qacceltree.cpp
new file mode 100644
index 0000000..60e6e27
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceltree.cpp
@@ -0,0 +1,706 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QStack>
+
+#include "qabstractxmlreceiver.h"
+#include "qacceliterators_p.h"
+#include "qacceltree_p.h"
+#include "qatomicstring_p.h"
+#include "qcommonvalues_p.h"
+#include "qcompressedwhitespace_p.h"
+#include "qdebug_p.h"
+#include "quntypedatomic_p.h"
+#include "qxpathhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+void AccelTree::printStats(const NamePool::Ptr &np) const
+{
+ Q_ASSERT(np);
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(np); /* Needed when compiling in release mode. */
+#else
+ const int len = basicData.count();
+
+ pDebug() << "AccelTree stats for" << (m_documentURI.isEmpty() ? QString::fromLatin1("<empty URI>") : m_documentURI.toString());
+ pDebug() << "Maximum pre number:" << maximumPreNumber();
+ pDebug() << "+---------------+-------+-------+---------------+-------+--------------+-------+";
+ pDebug() << "| Pre number | Depth | Size | Post Number | Kind | Name | Value |";
+ pDebug() << "+---------------+-------+-------+---------------+-------+--------------+-------+";
+ for(int i = 0; i < len; ++i)
+ {
+ const BasicNodeData &v = basicData.at(i);
+ pDebug() << "|" << i
+ << "\t\t|" << v.depth()
+ << "\t|" << v.size()
+ << "\t|" << postNumber(i)
+ << "\t|" << v.kind()
+ << "\t\t|" << (v.name().isNull() ? QString::fromLatin1("(none)") : np->displayName(v.name()))
+ << "\t\t|" << ((v.kind() == QXmlNodeModelIndex::Text && isCompressed(i)) ? CompressedWhitespace::decompress(data.value(i))
+ : data.value(i))
+ << "\t|";
+ /*
+ pDebug() << "|" << QString().arg(i, 14)
+ << "|" << QString().arg(v.depth(), 6)
+ << "|" << QString().arg(v.size(), 6)
+ << "|" << QString().arg(postNumber(i), 14)
+ << "|" << QString().arg(v.kind(), 6)
+ << "|";
+ */
+ }
+ pDebug() << "+---------------+-------+-------+---------------+-------+--------------+";
+ pDebug() << "Namespaces(" << namespaces.count() << "):";
+
+ QHashIterator<PreNumber, QVector<QXmlName> > it(namespaces);
+ while(it.hasNext())
+ {
+ it.next();
+
+ pDebug() << "PreNumber: " << QString::number(it.key());
+ for(int i = 0; i < it.value().count(); ++i)
+ pDebug() << "\t\t" << np->stringForPrefix(it.value().at(i).prefix()) << " = " << np->stringForNamespace(it.value().at(i).namespaceURI());
+ }
+
+#endif
+}
+
+QUrl AccelTree::baseUri(const QXmlNodeModelIndex &ni) const
+{
+ switch(kind(toPreNumber(ni)))
+ {
+ case QXmlNodeModelIndex::Document:
+ return baseUri();
+ case QXmlNodeModelIndex::Element:
+ {
+ const QXmlNodeModelIndex::Iterator::Ptr it(iterate(ni, QXmlNodeModelIndex::AxisAttribute));
+ QXmlNodeModelIndex next(it->next());
+
+ while(!next.isNull())
+ {
+ if(next.name() == QXmlName(StandardNamespaces::xml, StandardLocalNames::base))
+ {
+ const QUrl candidate(next.stringValue());
+ // TODO. The xml:base spec says to do URI escaping here.
+
+ if(!candidate.isValid())
+ return QUrl();
+ else if(candidate.isRelative())
+ {
+ const QXmlNodeModelIndex par(parent(ni));
+
+ if(par.isNull())
+ return baseUri().resolved(candidate);
+ else
+ return par.baseUri().resolved(candidate);
+ }
+ else
+ return candidate;
+ }
+
+ next = it->next();
+ }
+
+ /* We have no xml:base-attribute. Can any parent supply us a base URI? */
+ const QXmlNodeModelIndex par(parent(ni));
+
+ if(par.isNull())
+ return baseUri();
+ else
+ return par.baseUri();
+ }
+ case QXmlNodeModelIndex::ProcessingInstruction:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Comment:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Attribute:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Text:
+ {
+ const QXmlNodeModelIndex par(ni.iterate(QXmlNodeModelIndex::AxisParent)->next());
+ if(par.isNull())
+ return QUrl();
+ else
+ return par.baseUri();
+ }
+ case QXmlNodeModelIndex::Namespace:
+ return QUrl();
+ }
+
+ Q_ASSERT_X(false, Q_FUNC_INFO, "This line is never supposed to be reached.");
+ return QUrl();
+}
+
+QUrl AccelTree::documentUri(const QXmlNodeModelIndex &ni) const
+{
+ if(kind(toPreNumber(ni)) == QXmlNodeModelIndex::Document)
+ return documentUri();
+ else
+ return QUrl();
+}
+
+QXmlNodeModelIndex::NodeKind AccelTree::kind(const QXmlNodeModelIndex &ni) const
+{
+ return kind(toPreNumber(ni));
+}
+
+QXmlNodeModelIndex::DocumentOrder AccelTree::compareOrder(const QXmlNodeModelIndex &ni1,
+ const QXmlNodeModelIndex &ni2) const
+{
+ Q_ASSERT_X(ni1.model() == ni2.model(), Q_FUNC_INFO,
+ "The API docs guarantees the two nodes are from the same model");
+
+ const PreNumber p1 = ni1.data();
+ const PreNumber p2 = ni2.data();
+
+ if(p1 == p2)
+ return QXmlNodeModelIndex::Is;
+ else if(p1 < p2)
+ return QXmlNodeModelIndex::Precedes;
+ else
+ return QXmlNodeModelIndex::Follows;
+}
+
+QXmlNodeModelIndex AccelTree::root(const QXmlNodeModelIndex &) const
+{
+ return createIndex(qint64(0));
+}
+
+QXmlNodeModelIndex AccelTree::parent(const QXmlNodeModelIndex &ni) const
+{
+ const AccelTree::PreNumber p = basicData.at(toPreNumber(ni)).parent();
+
+ if(p == -1)
+ return QXmlNodeModelIndex();
+ else
+ return createIndex(p);
+}
+
+QXmlNodeModelIndex::Iterator::Ptr AccelTree::iterate(const QXmlNodeModelIndex &ni,
+ QXmlNodeModelIndex::Axis axis) const
+{
+ const PreNumber preNumber = toPreNumber(ni);
+
+ switch(axis)
+ {
+ case QXmlNodeModelIndex::AxisChildOrTop:
+ {
+ if(!hasParent(preNumber))
+ {
+ switch(kind(preNumber))
+ {
+ case QXmlNodeModelIndex::Comment:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::ProcessingInstruction:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Element:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Text:
+ return makeSingletonIterator(ni);
+ case QXmlNodeModelIndex::Attribute:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Document:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Namespace:
+ /* Do nothing. */;
+ }
+ }
+ /* Else, fallthrough to AxisChild. */
+ }
+ case QXmlNodeModelIndex::AxisChild:
+ {
+ if(hasChildren(preNumber))
+ return QXmlNodeModelIndex::Iterator::Ptr(new ChildIterator(this, preNumber));
+ else
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ }
+ case QXmlNodeModelIndex::AxisAncestor:
+ {
+ if(hasParent(preNumber))
+ return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<false>(this, preNumber));
+ else
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ }
+ case QXmlNodeModelIndex::AxisAncestorOrSelf:
+ return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<true>(this, preNumber));
+ case QXmlNodeModelIndex::AxisParent:
+ {
+ if(hasParent(preNumber))
+ return makeSingletonIterator(createIndex(parent(preNumber)));
+ else
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ }
+ case QXmlNodeModelIndex::AxisDescendant:
+ {
+ if(hasChildren(preNumber))
+ return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<false>(this, preNumber));
+ else
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ }
+ case QXmlNodeModelIndex::AxisDescendantOrSelf:
+ return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<true>(this, preNumber));
+ case QXmlNodeModelIndex::AxisFollowing:
+ {
+ if(preNumber == maximumPreNumber())
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ else
+ return QXmlNodeModelIndex::Iterator::Ptr(new FollowingIterator(this, preNumber));
+ }
+ case QXmlNodeModelIndex::AxisAttributeOrTop:
+ {
+ if(!hasParent(preNumber) && kind(preNumber) == QXmlNodeModelIndex::Attribute)
+ return makeSingletonIterator(ni);
+ /* Else, falthrough to AxisAttribute. */
+ }
+ case QXmlNodeModelIndex::AxisAttribute:
+ {
+ if(hasChildren(preNumber) && kind(preNumber + 1) == QXmlNodeModelIndex::Attribute)
+ return QXmlNodeModelIndex::Iterator::Ptr(new AttributeIterator(this, preNumber));
+ else
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ }
+ case QXmlNodeModelIndex::AxisPreceding:
+ {
+ if(preNumber == 0)
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ else
+ return QXmlNodeModelIndex::Iterator::Ptr(new PrecedingIterator(this, preNumber));
+ }
+ case QXmlNodeModelIndex::AxisSelf:
+ return makeSingletonIterator(createIndex(toPreNumber(ni)));
+ case QXmlNodeModelIndex::AxisFollowingSibling:
+ {
+ if(preNumber == maximumPreNumber())
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ else
+ return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<true>(this, preNumber));
+ }
+ case QXmlNodeModelIndex::AxisPrecedingSibling:
+ {
+ if(preNumber == 0)
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ else
+ return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<false>(this, preNumber));
+ }
+ case QXmlNodeModelIndex::AxisNamespace:
+ return makeEmptyIterator<QXmlNodeModelIndex>();
+ }
+
+ Q_ASSERT(false);
+ return QXmlNodeModelIndex::Iterator::Ptr();
+}
+
+QXmlNodeModelIndex AccelTree::nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis,
+ const QXmlNodeModelIndex&) const
+{
+ Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
+ return QXmlNodeModelIndex();
+}
+
+QVector<QXmlNodeModelIndex> AccelTree::attributes(const QXmlNodeModelIndex &element) const
+{
+ Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
+ Q_UNUSED(element);
+ return QVector<QXmlNodeModelIndex>();
+}
+
+QXmlName AccelTree::name(const QXmlNodeModelIndex &ni) const
+{
+ /* If this node type does not have a name(for instance, it's a comment)
+ * we will return the default constructed value, which is conformant with
+ * this function's contract. */
+ return name(toPreNumber(ni));
+}
+
+QVector<QXmlName> AccelTree::namespaceBindings(const QXmlNodeModelIndex &ni) const
+{
+ /* We get a hold of the ancestor, and loop them in reverse document
+ * order(first the parent, then the parent's parent, etc). As soon
+ * we find a binding that hasn't already been added, we add it to the
+ * result list. In that way, declarations appearing further down override
+ * those further up. */
+
+ const PreNumber preNumber = toPreNumber(ni);
+
+ const QXmlNodeModelIndex::Iterator::Ptr it(new AncestorIterator<true>(this, preNumber));
+ QVector<QXmlName> result;
+ QXmlNodeModelIndex n(it->next());
+
+ /* Whether xmlns="" has been encountered. */
+ bool hasUndeclaration = false;
+
+ while(!n.isNull())
+ {
+ const QVector<QXmlName> &forNode = namespaces.value(toPreNumber(n));
+ const int len = forNode.size();
+ bool stopInheritance = false;
+
+ for(int i = 0; i < len; ++i)
+ {
+ const QXmlName &nsb = forNode.at(i);
+
+ if(nsb.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
+ {
+ stopInheritance = true;
+ continue;
+ }
+
+ if(nsb.prefix() == StandardPrefixes::empty &&
+ nsb.namespaceURI() == StandardNamespaces::empty)
+ {
+ hasUndeclaration = true;
+ continue;
+ }
+
+ if(!hasPrefix(result, nsb.prefix()))
+ {
+ /* We've already encountered an undeclaration, so we're supposed to skip
+ * them. */
+ if(hasUndeclaration && nsb.prefix() == StandardPrefixes::empty)
+ continue;
+ else
+ result.append(nsb);
+ }
+ }
+
+ if(stopInheritance)
+ break;
+ else
+ n = it->next();
+ }
+
+ result.append(QXmlName(StandardNamespaces::xml, StandardLocalNames::empty, StandardPrefixes::xml));
+
+ return result;
+}
+
+void AccelTree::sendNamespaces(const QXmlNodeModelIndex &n,
+ QAbstractXmlReceiver *const receiver) const
+{
+ Q_ASSERT(n.kind() == QXmlNodeModelIndex::Element);
+
+ const QXmlNodeModelIndex::Iterator::Ptr it(iterate(n, QXmlNodeModelIndex::AxisAncestorOrSelf));
+ QXmlNodeModelIndex next(it->next());
+ QVector<QXmlName::PrefixCode> alreadySent;
+
+ while(!next.isNull())
+ {
+ const PreNumber preNumber = toPreNumber(next);
+
+ const QVector<QXmlName> &nss = namespaces.value(preNumber);
+
+ /* This is by far the most common case. */
+ if(nss.isEmpty())
+ {
+ next = it->next();
+ continue;
+ }
+
+ const int len = nss.count();
+ bool stopInheritance = false;
+
+ for(int i = 0; i < len; ++i)
+ {
+ const QXmlName &name = nss.at(i);
+
+ if(name.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
+ {
+ stopInheritance = true;
+ continue;
+ }
+
+ if(!alreadySent.contains(name.prefix()))
+ {
+ alreadySent.append(name.prefix());
+ receiver->namespaceBinding(name);
+ }
+ }
+
+ if(stopInheritance)
+ break;
+ else
+ next = it->next();
+ }
+}
+
+QString AccelTree::stringValue(const QXmlNodeModelIndex &ni) const
+{
+ const PreNumber preNumber = toPreNumber(ni);
+
+ switch(kind(preNumber))
+ {
+ case QXmlNodeModelIndex::Element:
+ {
+ /* Concatenate all text nodes that are descendants of this node. */
+ if(!hasChildren(preNumber))
+ return QString();
+
+ const AccelTree::PreNumber stop = preNumber + size(preNumber);
+ AccelTree::PreNumber pn = preNumber + 1; /* Jump over ourselves. */
+ QString result;
+
+ for(; pn <= stop; ++pn)
+ {
+ if(kind(pn) == QXmlNodeModelIndex::Text)
+ {
+ if(isCompressed(pn))
+ result += CompressedWhitespace::decompress(data.value(pn));
+ else
+ result += data.value(pn);
+ }
+ }
+
+ return result;
+ }
+ case QXmlNodeModelIndex::Text:
+ {
+ if(isCompressed(preNumber))
+ return CompressedWhitespace::decompress(data.value(preNumber));
+ /* Else, fallthrough. It's not compressed so use it as it is. */
+ }
+ case QXmlNodeModelIndex::Attribute:
+ /* Fallthrough */
+ case QXmlNodeModelIndex::ProcessingInstruction:
+ /* Fallthrough */
+ case QXmlNodeModelIndex::Comment:
+ return data.value(preNumber);
+ case QXmlNodeModelIndex::Document:
+ {
+ /* Concatenate all text nodes in the whole document. */
+
+ QString result;
+ // Perhaps we can QString::reserve() the result based on the size?
+ const AccelTree::PreNumber max = maximumPreNumber();
+
+ for(AccelTree::PreNumber i = 0; i <= max; ++i)
+ {
+ if(kind(i) == QXmlNodeModelIndex::Text)
+ {
+ if(isCompressed(i))
+ result += CompressedWhitespace::decompress(data.value(i));
+ else
+ result += data.value(i);
+ }
+ }
+
+ return result;
+ }
+ default:
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO,
+ "A node type that doesn't exist in the XPath Data Model was encountered.");
+ return QString(); /* Dummy, silence compiler warning. */
+ }
+ }
+}
+
+QVariant AccelTree::typedValue(const QXmlNodeModelIndex &n) const
+{
+ return stringValue(n);
+}
+
+bool AccelTree::hasPrefix(const QVector<QXmlName> &nbs, const QXmlName::PrefixCode prefix)
+{
+ const int size = nbs.size();
+
+ for(int i = 0; i < size; ++i)
+ {
+ if(nbs.at(i).prefix() == prefix)
+ return true;
+ }
+
+ return false;
+}
+
+ItemType::Ptr AccelTree::type(const QXmlNodeModelIndex &ni) const
+{
+ /* kind() is manually inlined here to avoid a virtual call. */
+ return XPathHelper::typeFromKind(basicData.at(toPreNumber(ni)).kind());
+}
+
+Item::Iterator::Ptr AccelTree::sequencedTypedValue(const QXmlNodeModelIndex &n) const
+{
+ const PreNumber preNumber = toPreNumber(n);
+
+ switch(kind(preNumber))
+ {
+ case QXmlNodeModelIndex::Element:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Document:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Attribute:
+ return makeSingletonIterator(Item(UntypedAtomic::fromValue(stringValue(n))));
+
+ case QXmlNodeModelIndex::Text:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::ProcessingInstruction:
+ /* Fallthrough. */
+ case QXmlNodeModelIndex::Comment:
+ return makeSingletonIterator(Item(AtomicString::fromValue(stringValue(n))));
+ default:
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO,
+ qPrintable(QString::fromLatin1("A node type that doesn't exist "
+ "in the XPath Data Model was encountered.").arg(kind(preNumber))));
+ return Item::Iterator::Ptr(); /* Dummy, silence compiler warning. */
+ }
+ }
+}
+
+void AccelTree::copyNodeTo(const QXmlNodeModelIndex &node,
+ QAbstractXmlReceiver *const receiver,
+ const NodeCopySettings &settings) const
+{
+ /* This code piece can be seen as a customized version of
+ * QAbstractXmlReceiver::item/sendAsNode(). */
+ Q_ASSERT(receiver);
+ Q_ASSERT(!node.isNull());
+
+ typedef QHash<QXmlName::PrefixCode, QXmlName::NamespaceCode> Binding;
+ QStack<Binding> outputted;
+
+ switch(node.kind())
+ {
+ case QXmlNodeModelIndex::Element:
+ {
+ outputted.push(Binding());
+
+ /* Add the namespace for our element name. */
+ const QXmlName elementName(node.name());
+
+ receiver->startElement(elementName);
+
+ if(!settings.testFlag(InheritNamespaces))
+ receiver->namespaceBinding(QXmlName(StandardNamespaces::StopNamespaceInheritance, 0,
+ StandardPrefixes::StopNamespaceInheritance));
+
+ if(settings.testFlag(PreserveNamespaces))
+ node.sendNamespaces(receiver);
+ else
+ {
+ /* Find the namespaces that we actually use and add them to outputted. These are drawn
+ * from the element name, and the node's attributes. */
+ outputted.top().insert(elementName.prefix(), elementName.namespaceURI());
+
+ const QXmlNodeModelIndex::Iterator::Ptr attributes(iterate(node, QXmlNodeModelIndex::AxisAttribute));
+ QXmlNodeModelIndex attr(attributes->next());
+
+ while(!attr.isNull())
+ {
+ const QXmlName &attrName = attr.name();
+ outputted.top().insert(attrName.prefix(), attrName.namespaceURI());
+ attr = attributes->next();
+ }
+
+ Binding::const_iterator it(outputted.top().constBegin());
+ const Binding::const_iterator end(outputted.top().constEnd());
+
+ for(; it != end; ++it)
+ receiver->namespaceBinding(QXmlName(it.value(), 0, it.key()));
+ }
+
+ /* Send the attributes of the element. */
+ {
+ QXmlNodeModelIndex::Iterator::Ptr attributes(node.iterate(QXmlNodeModelIndex::AxisAttribute));
+ QXmlNodeModelIndex attribute(attributes->next());
+
+ while(!attribute.isNull())
+ {
+ const QString &v = attribute.stringValue();
+ receiver->attribute(attribute.name(), QStringRef(&v));
+ attribute = attributes->next();
+ }
+ }
+
+ /* Send the children of the element. */
+ copyChildren(node, receiver, settings);
+
+ receiver->endElement();
+ outputted.pop();
+ break;
+ }
+ case QXmlNodeModelIndex::Document:
+ {
+ /* We need to intercept and grab the elements of the document node, such
+ * that we preserve/inherit preference applies to them. */
+ receiver->startDocument();
+ copyChildren(node, receiver, settings);
+ receiver->endDocument();
+ break;
+ }
+ default:
+ receiver->item(node);
+ }
+
+}
+
+void AccelTree::copyChildren(const QXmlNodeModelIndex &node,
+ QAbstractXmlReceiver *const receiver,
+ const NodeCopySettings &settings) const
+{
+ QXmlNodeModelIndex::Iterator::Ptr children(node.iterate(QXmlNodeModelIndex::AxisChild));
+ QXmlNodeModelIndex child(children->next());
+
+ while(!child.isNull())
+ {
+ copyNodeTo(child, receiver, settings);
+ child = children->next();
+ }
+}
+
+QXmlNodeModelIndex AccelTree::elementById(const QXmlName &id) const
+{
+ const PreNumber pre = m_IDs.value(id.localName(), -1);
+ if(pre == -1)
+ return QXmlNodeModelIndex();
+ else
+ return createIndex(pre);
+}
+
+QVector<QXmlNodeModelIndex> AccelTree::nodesByIdref(const QXmlName &) const
+{
+ return QVector<QXmlNodeModelIndex>();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/xmlpatterns/acceltree/qacceltree_p.h b/src/xmlpatterns/acceltree/qacceltree_p.h
new file mode 100644
index 0000000..10320ba
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceltree_p.h
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_AccelTree_H
+#define Patternist_AccelTree_H
+
+#include <QHash>
+#include <QUrl>
+#include <QVector>
+#include <QXmlName>
+
+#include "qitem_p.h"
+#include "qnamepool_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+namespace QPatternist
+{
+ template<bool> class AccelTreeBuilder;
+
+ /**
+ * @short Stores an XML document using the XPath Accelerator scheme, also
+ * known as pre/post numbering.
+ *
+ * Working on this code will be destructive without a proper understanding of
+ * the Accelerator scheme, so do check out the links. We don't implement any form
+ * of staircase join, although that is only due to time constraints.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ * @see <a href="http://www.pathfinder-xquery.org/?q=research/xpath-accel">XPath
+ * Accelerator</a>
+ * @see <a href="http://www.pathfinder-xquery.org/files/xpath-accel.pdf">Accelerating
+ * XPath Location Steps, Torsten Grust</a>
+ * @see <a href="http://citeseer.ist.psu.edu/cache/papers/cs/29367/http:zSzzSzwww.informatik.uni-konstanz.dezSz~grustzSzfileszSzstaircase-join.pdf/grust03staircase.pdf">Staircase Join:
+ * Teach a Relational DBMS to Watch its (Axis) Steps</a>
+ * @see <a href="http://ftp.cwi.nl/CWIreports/INS/INS-E0510.pdf">Loop-lifted
+ * staircase join: from XPath to XQuery, Torsten Grust</a>
+ * @see <a href="http://englich.wordpress.com/2007/01/09/xmlstat/">xmlstat, Frans Englich</a>
+ * @see <a href"http://www.inf.uni-konstanz.de/dbis/publications/download/accelerating-locsteps.pdf">Accelerating
+ * XPath Evaluation in Any RDBMS, Torsten Grust</a>
+ */
+ class AccelTree : public QAbstractXmlNodeModel
+ {
+ public:
+ using QAbstractXmlNodeModel::createIndex;
+
+ typedef QExplicitlySharedDataPointer<AccelTree> Ptr;
+ typedef qint32 PreNumber;
+ typedef PreNumber PostNumber;
+ typedef qint8 Depth;
+
+ inline AccelTree(const QUrl &docURI,
+ const QUrl &bURI) : m_documentURI(docURI),
+ m_baseURI(bURI)
+ {
+ /* Pre-allocate at least a little bit. */
+ // TODO. Do it according to what an average 4 KB doc contains.
+ basicData.reserve(100);
+ data.reserve(30);
+ }
+
+ /**
+ * @short Houses data for a node, and that all node kinds have.
+ *
+ * BasicNodeData is internal to the Accel tree implementation, and is
+ * only used by those classes.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ * @todo Can't m_kind be coded somewhere else? If m_name is invalid,
+ * its bits can be used to distinguish the node types that doesn't have
+ * names, and for elements, attributes and processing instructions, we need
+ * two bits, somewhere. Attributes and processing instructions can't have a
+ * size, is that of help? There's also certain rules for the names. For instance,
+ * a processing instruction will never have a prefix nor namespace. Neither
+ * will an attribute node have a default, non-empty namespace, right?
+ * @todo Compress text nodes, add general support for it in Patternist.
+ */
+ class BasicNodeData
+ {
+ public:
+ inline BasicNodeData()
+ {
+ }
+
+ inline BasicNodeData(const PreNumber aDepth,
+ const PreNumber aParent,
+ const QXmlNodeModelIndex::NodeKind k,
+ const PreNumber s,
+ const QXmlName n = QXmlName()) : m_parent(aParent)
+ , m_size(s)
+ , m_name(n)
+ , m_depth(aDepth)
+ , m_kind(k)
+ {
+ }
+
+ inline Depth depth() const
+ {
+ return m_depth;
+ }
+
+ inline PreNumber parent() const
+ {
+ return m_parent;
+ }
+
+ /**
+ * @see AccelTree::size()
+ */
+ inline PreNumber size() const
+ {
+ /* Remember that we use the m_size to signal compression if
+ * we're a text node. */
+ if(m_kind == QXmlNodeModelIndex::Text)
+ return 0;
+ else
+ return m_size;
+ }
+
+ inline void setSize(const PreNumber aSize)
+ {
+ m_size = aSize;
+ }
+
+ inline QXmlNodeModelIndex::NodeKind kind() const
+ {
+ return m_kind;
+ }
+
+ inline QXmlName name() const
+ {
+ return m_name;
+ }
+
+ inline bool isCompressed() const
+ {
+ Q_ASSERT_X(m_kind == QXmlNodeModelIndex::Text, Q_FUNC_INFO,
+ "Currently, only text nodes are compressed.");
+ /* Note, we don't call size() here, since it has logic for text
+ * nodes. */
+ return m_size == IsCompressed;
+ }
+
+ private:
+ /**
+ * This is the pre number of the parent.
+ */
+ PreNumber m_parent;
+
+ /**
+ * This is the count of children this node has.
+ *
+ * In the case of a text node, which cannot have children,
+ * it is set to IsCompressed, if the content has been the result
+ * of CompressedWhitespace::compress(). If it's not compressed,
+ * it is zero.
+ */
+ PreNumber m_size;
+
+ /**
+ * For text nodes, and less importantly, comments,
+ * this variable is not used.
+ */
+ QXmlName m_name;
+
+ Depth m_depth;
+
+ /**
+ * Technically it is sufficient with 8 bits. However, at least MSVC
+ * 2005 miscompiles it such that QXmlNodeModelIndex::Text becomes
+ * -64 instead of 64 with hilarious crashes as result.
+ *
+ * Fortunately this extra bit would be padded anyway.
+ */
+ QXmlNodeModelIndex::NodeKind m_kind : 8;
+ };
+
+ virtual QUrl baseUri(const QXmlNodeModelIndex &ni) const;
+ virtual QUrl documentUri(const QXmlNodeModelIndex &ni) const;
+ virtual QXmlNodeModelIndex::NodeKind kind(const QXmlNodeModelIndex &ni) const;
+ virtual QXmlNodeModelIndex::DocumentOrder compareOrder(const QXmlNodeModelIndex &ni1,
+ const QXmlNodeModelIndex &ni2) const;
+
+ /**
+ * @short Returns the root node.
+ *
+ * This function does not use @p n, so a default constructed
+ * QXmlNodeModelIndex may be passed.
+ */
+ virtual QXmlNodeModelIndex root(const QXmlNodeModelIndex &n) const;
+
+ virtual QXmlNodeModelIndex parent(const QXmlNodeModelIndex &ni) const;
+ virtual QXmlNodeModelIndex::Iterator::Ptr iterate(const QXmlNodeModelIndex &ni,
+ QXmlNodeModelIndex::Axis axis) const;
+ virtual QXmlName name(const QXmlNodeModelIndex &ni) const;
+ virtual QVector<QXmlName> namespaceBindings(const QXmlNodeModelIndex &n) const;
+ virtual void sendNamespaces(const QXmlNodeModelIndex &n,
+ QAbstractXmlReceiver *const receiver) const;
+ virtual QString stringValue(const QXmlNodeModelIndex &n) const;
+ virtual QVariant typedValue(const QXmlNodeModelIndex &n) const;
+ virtual Item::Iterator::Ptr sequencedTypedValue(const QXmlNodeModelIndex &n) const;
+ virtual ItemType::Ptr type(const QXmlNodeModelIndex &ni) const;
+ virtual QXmlNodeModelIndex elementById(const QXmlName &id) const;
+ virtual QVector<QXmlNodeModelIndex> nodesByIdref(const QXmlName &idref) const;
+ virtual void copyNodeTo(const QXmlNodeModelIndex &node,
+ QAbstractXmlReceiver *const receiver,
+ const NodeCopySettings &settings) const;
+
+ friend class AccelTreeBuilder<false>;
+ friend class AccelTreeBuilder<true>;
+
+ enum Constants
+ {
+ IsCompressed = 1
+ };
+
+ /**
+ * The key is the pre number of an element, and the value is a vector
+ * containing the namespace declarations being declared on that
+ * element. Therefore, it does not reflect the namespaces being in
+ * scope for that element. For that, a walk along axis ancestor is
+ * necessary.
+ */
+ QHash<PreNumber, QVector<QXmlName> > namespaces;
+
+ /**
+ * Stores data for nodes. The QHash's value is the data of the processing instruction, and the
+ * content of a text node or comment.
+ */
+ QHash<PreNumber, QString> data;
+
+ QVector<BasicNodeData> basicData;
+
+ inline QUrl documentUri() const
+ {
+ return m_documentURI;
+ }
+
+ inline QUrl baseUri() const
+ {
+ return m_baseURI;
+ }
+
+ /**
+ * @short Returns @c true if the node identified by @p pre has child
+ * nodes(in the sense of the XDM), but also if it has namespace nodes,
+ * or attribute nodes.
+ */
+ inline bool hasChildren(const PreNumber pre) const
+ {
+ return basicData.at(pre).size() > 0;
+ }
+
+ /**
+ * @short Returns the parent node of @p pre.
+ *
+ * If @p pre parent doesn't have a parent node, the return value is
+ * undefined.
+ *
+ * @see hasParent()
+ */
+ inline PreNumber parent(const PreNumber pre) const
+ {
+ return basicData.at(pre).parent();
+ }
+
+ inline bool hasParent(const PreNumber pre) const
+ {
+ return basicData.at(pre).depth() > 0;
+ }
+
+ inline bool hasFollowingSibling(const PreNumber pre) const
+ {
+ return pre < maximumPreNumber();
+ }
+
+ inline PostNumber postNumber(const PreNumber pre) const
+ {
+ const BasicNodeData &b = basicData.at(pre);
+ return pre + b.size() - b.depth();
+ }
+
+ inline QXmlNodeModelIndex::NodeKind kind(const PreNumber pre) const
+ {
+ return basicData.at(pre).kind();
+ }
+
+ inline PreNumber maximumPreNumber() const
+ {
+ return basicData.count() - 1;
+ }
+
+ inline PreNumber toPreNumber(const QXmlNodeModelIndex n) const
+ {
+ return n.data();
+ }
+
+ inline PreNumber size(const PreNumber pre) const
+ {
+ Q_ASSERT_X(basicData.at(pre).size() != -1, Q_FUNC_INFO,
+ "The size cannot be -1. That means an uninitialized value is attempted to be used.");
+ return basicData.at(pre).size();
+ }
+
+ inline Depth depth(const PreNumber pre) const
+ {
+ return basicData.at(pre).depth();
+ }
+
+ void printStats(const NamePool::Ptr &np) const;
+
+ inline QXmlName name(const PreNumber pre) const
+ {
+ return basicData.at(pre).name();
+ }
+
+ inline bool isCompressed(const PreNumber pre) const
+ {
+ return basicData.at(pre).isCompressed();
+ }
+
+ static inline bool hasPrefix(const QVector<QXmlName> &nbs, const QXmlName::PrefixCode prefix);
+
+ QUrl m_documentURI;
+ QUrl m_baseURI;
+
+ protected:
+ virtual QXmlNodeModelIndex nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis,
+ const QXmlNodeModelIndex&) const;
+ virtual QVector<QXmlNodeModelIndex> attributes(const QXmlNodeModelIndex &element) const;
+
+ private:
+ /**
+ * Copies the children of @p node to @p receiver.
+ */
+ inline void copyChildren(const QXmlNodeModelIndex &node,
+ QAbstractXmlReceiver *const receiver,
+ const NodeCopySettings &settings) const;
+
+ /**
+ * The key is the xml:id value, and the value is the element
+ * with that value.
+ */
+ QHash<QXmlName::LocalNameCode, PreNumber> m_IDs;
+ };
+}
+
+Q_DECLARE_TYPEINFO(QPatternist::AccelTree::BasicNodeData, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/xmlpatterns/acceltree/qacceltreebuilder.cpp b/src/xmlpatterns/acceltree/qacceltreebuilder.cpp
new file mode 100644
index 0000000..5b16cc3
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceltreebuilder.cpp
@@ -0,0 +1,429 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/**
+ * @file
+ * @short This file is included by qacceltreebuilder_p.h.
+ * If you need includes in this file, put them in qacceltreebuilder_p.h, outside of the namespace.
+ */
+
+template <bool FromDocument>
+AccelTreeBuilder<FromDocument>::AccelTreeBuilder(const QUrl &docURI,
+ const QUrl &baseURI,
+ const NamePool::Ptr &np,
+ ReportContext *const context) : m_preNumber(-1)
+ , m_isPreviousAtomic(false)
+ , m_hasCharacters(false)
+ , m_isCharactersCompressed(false)
+ , m_namePool(np)
+ , m_document(new AccelTree(docURI, baseURI))
+ , m_skippedDocumentNodes(0)
+ , m_documentURI(docURI)
+ , m_context(context)
+{
+ Q_ASSERT(m_namePool);
+
+ /* TODO Perhaps we can merge m_ancestors and m_size
+ * into one, and store a struct for the two instead? */
+ m_ancestors.reserve(DefaultNodeStackSize);
+ m_ancestors.push(-1);
+
+ m_size.reserve(DefaultNodeStackSize);
+ m_size.push(0);
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::startStructure()
+{
+ if(m_hasCharacters)
+ {
+ /* We create a node even if m_characters is empty.
+ * Remember that `text {""}' creates one text node
+ * with string value "". */
+
+ ++m_preNumber;
+ m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(),
+ currentParent(),
+ QXmlNodeModelIndex::Text,
+ m_isCharactersCompressed ? AccelTree::IsCompressed : 0));
+ m_document->data.insert(m_preNumber, m_characters);
+ ++m_size.top();
+
+ m_characters.clear(); /* We don't want it added twice. */
+ m_hasCharacters = false;
+
+ if(m_isCharactersCompressed)
+ m_isCharactersCompressed = false;
+ }
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::item(const Item &it)
+{
+ Q_ASSERT(it);
+
+ if(it.isAtomicValue())
+ {
+ if(m_isPreviousAtomic)
+ {
+ m_characters += QLatin1Char(' ');
+ m_characters += it.stringValue();
+ }
+ else
+ {
+ m_isPreviousAtomic = true;
+ const QString sv(it.stringValue());
+
+ if(!sv.isEmpty())
+ {
+ m_characters += sv;
+ m_hasCharacters = true;
+ }
+ }
+ }
+ else
+ sendAsNode(it);
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::startElement(const QXmlName &name)
+{
+ startStructure();
+
+ m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(), currentParent(), QXmlNodeModelIndex::Element, -1, name));
+
+ ++m_preNumber;
+ m_ancestors.push(m_preNumber);
+
+ ++m_size.top();
+ m_size.push(0);
+
+ /* With node constructors, we can receive names for which we have no namespace
+ * constructors, such as in the query '<xs:space/>'. Since the 'xs' prefix has no
+ * NamespaceConstructor in this case, we synthesize the namespace.
+ *
+ * In case we're constructing from an XML document we avoid the call because
+ * although it's redundant, it's on extra virtual call for each element. */
+ if(!FromDocument)
+ namespaceBinding(QXmlName(name.namespaceURI(), 0, name.prefix()));
+
+ m_isPreviousAtomic = false;
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::endElement()
+{
+ startStructure();
+ const AccelTree::PreNumber index = m_ancestors.pop();
+ AccelTree::BasicNodeData &data = m_document->basicData[index];
+
+ /* Sub trees needs to be included in upper trees, so we add the count of this element
+ * to our parent. */
+ m_size[m_size.count() - 2] += m_size.top();
+
+ data.setSize(m_size.pop());
+ m_isPreviousAtomic = false;
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::attribute(const QXmlName &name, const QStringRef &value)
+{
+ /* Attributes adds a namespace binding, so lets synthesize one.
+ *
+ * We optimize by checking whether we have a namespace for which a binding would
+ * be generated. Happens relatively rarely. */
+ if(name.hasPrefix())
+ namespaceBinding(QXmlName(name.namespaceURI(), 0, name.prefix()));
+
+ m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(), currentParent(), QXmlNodeModelIndex::Attribute, 0, name));
+ ++m_preNumber;
+ ++m_size.top();
+
+ m_isPreviousAtomic = false;
+
+ if(name.namespaceURI() == StandardNamespaces::xml && name.localName() == StandardLocalNames::id)
+ {
+ const QString normalized(value.toString().simplified());
+
+ if(QXmlUtils::isNCName(normalized))
+ {
+ const QXmlName::LocalNameCode id = m_namePool->allocateLocalName(normalized);
+
+ const int oldSize = m_document->m_IDs.count();
+ m_document->m_IDs.insert(id, currentParent());
+ /* We don't run the value through m_attributeCompress here, because
+ * the likelyhood of it deing identical to another attribute is
+ * very small. */
+ m_document->data.insert(m_preNumber, normalized);
+
+ /**
+ * In the case that we're called for doc-available(), m_context is
+ * null, and we need to flag somehow that we failed to load this
+ * document.
+ */
+ if(oldSize == m_document->m_IDs.count() && m_context) // TODO
+ {
+ Q_ASSERT(m_context);
+ m_context->error(QtXmlPatterns::tr("An %1-attribute with value %2 has already been declared.")
+ .arg(formatKeyword("xml:id"),
+ formatData(normalized)),
+ FromDocument ? ReportContext::FODC0002 : ReportContext::XQDY0091,
+ this);
+ }
+ }
+ else if(m_context) // TODO
+ {
+ Q_ASSERT(m_context);
+
+ /* If we're building from an XML Document(e.g, we're fed from QXmlStreamReader, we raise FODC0002,
+ * otherwise XQDY0091. */
+ m_context->error(QtXmlPatterns::tr("An %1-attribute must have a "
+ "valid %2 as value, which %3 isn't.").arg(formatKeyword("xml:id"),
+ formatType(m_namePool, BuiltinTypes::xsNCName),
+ formatData(value.toString())),
+ FromDocument ? ReportContext::FODC0002 : ReportContext::XQDY0091,
+ this);
+ }
+ }
+ else
+ m_document->data.insert(m_preNumber, *m_attributeCompress.insert(value.toString()));
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::characters(const QStringRef &ch)
+{
+
+ /* If a text node constructor appears by itself, a node needs to
+ * be created. Therefore, we set m_hasCharacters
+ * if we're the only node.
+ * However, if the text node appears as a child of a document or element
+ * node it is discarded if it's empty.
+ */
+ if(m_hasCharacters && m_isCharactersCompressed)
+ {
+ m_characters = CompressedWhitespace::decompress(m_characters);
+ m_isCharactersCompressed = false;
+ }
+
+ m_characters += ch;
+
+ m_isPreviousAtomic = false;
+ m_hasCharacters = !m_characters.isEmpty() || m_preNumber == -1; /* -1 is our start value. */
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::whitespaceOnly(const QStringRef &ch)
+{
+ Q_ASSERT(!ch.isEmpty());
+ Q_ASSERT(ch.toString().trimmed().isEmpty());
+
+ /* This gets problematic due to how QXmlStreamReader works(which
+ * is the only one we get whitespaceOnly() events from). Namely, text intermingled
+ * with CDATA gets reported as individual Characters events, and
+ * QXmlStreamReader::isWhitespace() can return differently for each of those. However,
+ * it will occur very rarely, so this workaround of 1) mistakenly compressing 2) decompressing 3)
+ * appending, will happen infrequently.
+ */
+ if(m_hasCharacters)
+ {
+ if(m_isCharactersCompressed)
+ {
+ m_characters = CompressedWhitespace::decompress(m_characters);
+ m_isCharactersCompressed = false;
+ }
+
+ m_characters.append(ch.toString());
+ }
+ else
+ {
+ /* We haven't received a text node previously. */
+ m_characters = CompressedWhitespace::compress(ch);
+ m_isCharactersCompressed = true;
+ m_isPreviousAtomic = false;
+ m_hasCharacters = true;
+ }
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::processingInstruction(const QXmlName &target,
+ const QString &data)
+{
+ startStructure();
+ ++m_preNumber;
+ m_document->data.insert(m_preNumber, data);
+
+ m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(),
+ currentParent(),
+ QXmlNodeModelIndex::ProcessingInstruction,
+ 0,
+ target));
+ ++m_size.top();
+ m_isPreviousAtomic = false;
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::comment(const QString &content)
+{
+ startStructure();
+ m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(), currentParent(), QXmlNodeModelIndex::Comment, 0));
+ ++m_preNumber;
+ m_document->data.insert(m_preNumber, content);
+ ++m_size.top();
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::namespaceBinding(const QXmlName &nb)
+{
+ /* Note, because attribute() sometimes generate namespaceBinding() calls, this function
+ * can be called after attributes, in contrast to what the class documentation says. This is ok,
+ * as long as we're not dealing with public API. */
+
+ /* If we've received attributes, it means the element's size have changed and m_preNumber have advanced,
+ * so "reverse back" to the actual element. */
+ const AccelTree::PreNumber pn = m_preNumber - m_size.top();
+
+ QVector<QXmlName> &nss = m_document->namespaces[pn];
+
+ /* "xml" hasn't been declared for each node, AccelTree::namespaceBindings() adds it, so avoid it
+ * such that we don't get duplicates. */
+ if(nb.prefix() == StandardPrefixes::xml)
+ return;
+
+ /* If we already have the binding, skip it. */
+ const int len = nss.count();
+ for(int i = 0; i < len; ++i)
+ {
+ if(nss.at(i).prefix() == nb.prefix())
+ return;
+ }
+
+ nss.append(nb);
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::startDocument()
+{
+ /* If we have already received nodes, we can't add a document node. */
+ if(m_preNumber == -1) /* -1 is our start value. */
+ {
+ m_size.push(0);
+ m_document->basicData.append(AccelTree::BasicNodeData(0, -1, QXmlNodeModelIndex::Document, -1));
+ ++m_preNumber;
+ m_ancestors.push(m_preNumber);
+ }
+ else
+ ++m_skippedDocumentNodes;
+
+ m_isPreviousAtomic = false;
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::endDocument()
+{
+ if(m_skippedDocumentNodes == 0)
+ {
+ /* Create text nodes, if we've received any. We do this only if we're the
+ * top node because if we're getting this event as being a child of an element,
+ * text nodes or atomic values can appear after us, and which must get
+ * merged with the previous text.
+ *
+ * We call startStructure() before we pop the ancestor, such that the text node becomes
+ * a child of this document node. */
+ startStructure();
+
+ m_document->basicData.first().setSize(m_size.pop());
+ m_ancestors.pop();
+ }
+ else
+ --m_skippedDocumentNodes;
+
+ m_isPreviousAtomic = false;
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::atomicValue(const QVariant &value)
+{
+ Q_UNUSED(value);
+ // TODO
+}
+
+template <bool FromDocument>
+QAbstractXmlNodeModel::Ptr AccelTreeBuilder<FromDocument>::builtDocument()
+{
+ /* Create a text node, if we have received text in some way. */
+ startStructure();
+ m_document->printStats(m_namePool);
+
+ return m_document;
+}
+
+template <bool FromDocument>
+NodeBuilder::Ptr AccelTreeBuilder<FromDocument>::create(const QUrl &baseURI) const
+{
+ Q_UNUSED(baseURI);
+ return NodeBuilder::Ptr(new AccelTreeBuilder(QUrl(), baseURI, m_namePool, m_context));
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::startOfSequence()
+{
+}
+
+template <bool FromDocument>
+void AccelTreeBuilder<FromDocument>::endOfSequence()
+{
+}
+
+template <bool FromDocument>
+const SourceLocationReflection *AccelTreeBuilder<FromDocument>::actualReflection() const
+{
+ return this;
+}
+
+template <bool FromDocument>
+QSourceLocation AccelTreeBuilder<FromDocument>::sourceLocation() const
+{
+ if(m_documentURI.isEmpty())
+ return QSourceLocation(QUrl(QLatin1String("AnonymousNodeTree")));
+ else
+ return QSourceLocation(m_documentURI);
+}
+
diff --git a/src/xmlpatterns/acceltree/qacceltreebuilder_p.h b/src/xmlpatterns/acceltree/qacceltreebuilder_p.h
new file mode 100644
index 0000000..653eb85
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceltreebuilder_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_AccelTreeBuilder_H
+#define Patternist_AccelTreeBuilder_H
+
+#include <QSet>
+#include <QStack>
+
+#include "private/qxmlutils_p.h"
+#include "qacceltree_p.h"
+#include "qbuiltintypes_p.h"
+#include "qcompressedwhitespace_p.h"
+#include "qnamepool_p.h"
+#include "qnodebuilder_p.h"
+#include "qreportcontext_p.h"
+#include "qsourcelocationreflection_p.h"
+#include "qpatternistlocale_p.h"
+#include <QtDebug>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+namespace QPatternist
+{
+ /**
+ * @short Builds an AccelTree from a stream of XML/Item events
+ * received through the NodeBuilder interface.
+ *
+ * If FromDocument is @c true, it is assumed that AccelTreeBuilder is fed
+ * events from an XML document, otherwise it is assumed the events
+ * are from node constructor expressions.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ template<bool FromDocument>
+ class AccelTreeBuilder : public NodeBuilder
+ , public SourceLocationReflection
+ {
+ public:
+ typedef QExplicitlySharedDataPointer<AccelTreeBuilder> Ptr;
+
+ /**
+ * @param context may be @c null.
+ */
+ AccelTreeBuilder(const QUrl &docURI,
+ const QUrl &baseURI,
+ const NamePool::Ptr &np,
+ ReportContext *const context);
+ virtual void startDocument();
+ virtual void endDocument();
+ virtual void startElement(const QXmlName &name);
+ virtual void endElement();
+ virtual void attribute(const QXmlName &name, const QStringRef &value);
+ virtual void characters(const QStringRef &ch);
+ virtual void whitespaceOnly(const QStringRef &ch);
+ virtual void processingInstruction(const QXmlName &target,
+ const QString &data);
+ virtual void namespaceBinding(const QXmlName &nb);
+ virtual void comment(const QString &content);
+ virtual void item(const Item &it);
+
+ virtual QAbstractXmlNodeModel::Ptr builtDocument();
+ virtual NodeBuilder::Ptr create(const QUrl &baseURI) const;
+ virtual void startOfSequence();
+ virtual void endOfSequence();
+
+ inline AccelTree::Ptr builtDocument() const
+ {
+ return m_document;
+ }
+
+ virtual void atomicValue(const QVariant &value);
+
+ virtual const SourceLocationReflection *actualReflection() const;
+ virtual QSourceLocation sourceLocation() const;
+
+ private:
+ inline void startStructure();
+
+ inline AccelTree::PreNumber currentDepth() const
+ {
+ return m_ancestors.count() -1;
+ }
+
+ inline AccelTree::PreNumber currentParent() const
+ {
+ return m_ancestors.isEmpty() ? -1 : m_ancestors.top();
+ }
+
+ enum Constants
+ {
+ DefaultNodeStackSize = 10,
+ SizeIsEmpty = 0
+ };
+
+ AccelTree::PreNumber m_preNumber;
+ bool m_isPreviousAtomic;
+ bool m_hasCharacters;
+ /**
+ * Whether m_characters has been run through
+ * CompressedWhitespace::compress().
+ */
+ bool m_isCharactersCompressed;
+ QString m_characters;
+ NamePool::Ptr m_namePool;
+ AccelTree::Ptr m_document;
+ QStack<AccelTree::PreNumber> m_ancestors;
+ QStack<AccelTree::PreNumber> m_size;
+
+ /** If we have already commenced a document, we don't want to
+ * add more document nodes. We keep track of them with this
+ * counter, which ensures that startDocument() and endDocument()
+ * are skipped consistently. */
+ AccelTree::PreNumber m_skippedDocumentNodes;
+
+ /**
+ * All attribute values goes through this set such that we store only
+ * one QString for identical attribute values.
+ */
+ QSet<QString> m_attributeCompress;
+ const QUrl m_documentURI;
+ /**
+ * We don't store a reference pointer here because then we get a
+ * circular reference with GenericDynamicContext, when it stores us as
+ * a member.
+ */
+ ReportContext *const m_context;
+ };
+
+#include "qacceltreebuilder.cpp"
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp b/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp
new file mode 100644
index 0000000..4a5c219
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp
@@ -0,0 +1,411 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QFile>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTimer>
+#include <QtCore/QXmlStreamReader>
+
+#include <QtNetwork/QNetworkRequest>
+
+#include "qacceltreebuilder_p.h"
+#include "qatomicstring_p.h"
+#include "qautoptr_p.h"
+#include "qcommonsequencetypes_p.h"
+
+#include "qacceltreeresourceloader_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+static inline uint qHash(const QUrl &uri)
+{
+ return qHash(uri.toString());
+}
+
+AccelTreeResourceLoader::AccelTreeResourceLoader(const NamePool::Ptr &np,
+ const NetworkAccessDelegator::Ptr &manager) : m_namePool(np)
+ , m_networkAccessDelegator(manager)
+{
+ Q_ASSERT(m_namePool);
+ Q_ASSERT(m_networkAccessDelegator);
+}
+
+bool AccelTreeResourceLoader::retrieveDocument(const QUrl &uri,
+ const ReportContext::Ptr &context)
+{
+ Q_ASSERT(uri.isValid());
+ AccelTreeBuilder<true> builder(uri, uri, m_namePool, context.data());
+
+ const AutoPtr<QNetworkReply> reply(load(uri, m_networkAccessDelegator, context));
+
+ if(!reply)
+ return false;
+
+ bool success = false;
+ success = streamToReceiver(reply.data(), &builder, m_namePool, context, uri);
+
+ m_loadedDocuments.insert(uri, builder.builtDocument());
+ return success;
+}
+
+QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri,
+ const NetworkAccessDelegator::Ptr &networkDelegator,
+ const ReportContext::Ptr &context)
+{
+ return load(uri,
+ networkDelegator->managerFor(uri),
+ context);
+}
+
+QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri,
+ QNetworkAccessManager *const networkManager,
+ const ReportContext::Ptr &context)
+{
+ Q_ASSERT(networkManager);
+ Q_ASSERT(uri.isValid());
+
+ NetworkLoop networkLoop;
+
+ QNetworkRequest request(uri);
+ QNetworkReply *const reply = networkManager->get(request);
+ networkLoop.connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(error(QNetworkReply::NetworkError)));
+ networkLoop.connect(reply, SIGNAL(finished()), SLOT(finished()));
+
+ if(networkLoop.exec())
+ {
+ const QString errorMessage(escape(reply->errorString()));
+
+ /* Note, we delete reply before we exit this function with error(). */
+ delete reply;
+
+ const QSourceLocation location(uri);
+
+ if(context)
+ context->error(errorMessage, ReportContext::FODC0002, location);
+
+ return 0;
+ }
+ else
+ return reply;
+}
+
+bool AccelTreeResourceLoader::streamToReceiver(QIODevice *const dev,
+ QAbstractXmlReceiver *const receiver,
+ const NamePool::Ptr &np,
+ const ReportContext::Ptr &context,
+ const QUrl &uri)
+{
+ Q_ASSERT(dev);
+ Q_ASSERT(receiver);
+ Q_ASSERT(np);
+
+ QXmlStreamReader reader(dev);
+
+ /* Optimize: change NamePool to take QStringRef such that we don't have to call toString() below. That
+ * will save us a gazillion of temporary QStrings. */
+
+ while(!reader.atEnd())
+ {
+ reader.readNext();
+
+ switch(reader.tokenType())
+ {
+ case QXmlStreamReader::StartElement:
+ {
+ /* Send the name. */
+ receiver->startElement(np->allocateQName(reader.namespaceUri().toString(), reader.name().toString(),
+ reader.prefix().toString()));
+
+ /* Send namespace declarations. */
+ const QXmlStreamNamespaceDeclarations &nss = reader.namespaceDeclarations();
+
+ /* The far most common case, is for it to be empty. */
+ if(!nss.isEmpty())
+ {
+ const int len = nss.size();
+
+ for(int i = 0; i < len; ++i)
+ {
+ const QXmlStreamNamespaceDeclaration &ns = nss.at(i);
+ receiver->namespaceBinding(np->allocateBinding(ns.prefix().toString(), ns.namespaceUri().toString()));
+ }
+ }
+
+ /* Send attributes. */
+ const QXmlStreamAttributes &attrs = reader.attributes();
+ const int len = attrs.size();
+
+ for(int i = 0; i < len; ++i)
+ {
+ const QXmlStreamAttribute &attr = attrs.at(i);
+
+ receiver->attribute(np->allocateQName(attr.namespaceUri().toString(), attr.name().toString(),
+ attr.prefix().toString()),
+ attr.value());
+ }
+
+ continue;
+ }
+ case QXmlStreamReader::EndElement:
+ {
+ receiver->endElement();
+ continue;
+ }
+ case QXmlStreamReader::Characters:
+ {
+ if(reader.isWhitespace())
+ receiver->whitespaceOnly(reader.text());
+ else
+ receiver->characters(reader.text());
+
+ continue;
+ }
+ case QXmlStreamReader::Comment:
+ {
+ receiver->comment(reader.text().toString());
+ continue;
+ }
+ case QXmlStreamReader::ProcessingInstruction:
+ {
+ receiver->processingInstruction(np->allocateQName(QString(), reader.processingInstructionTarget().toString()),
+ reader.processingInstructionData().toString());
+ continue;
+ }
+ case QXmlStreamReader::StartDocument:
+ {
+ receiver->startDocument();
+ continue;
+ }
+ case QXmlStreamReader::EndDocument:
+ {
+ receiver->endDocument();
+ continue;
+ }
+ case QXmlStreamReader::EntityReference:
+ /* Fallthrough. */
+ case QXmlStreamReader::DTD:
+ {
+ /* We just ignore any DTD and entity references. */
+ continue;
+ }
+ case QXmlStreamReader::Invalid:
+ {
+ if(context)
+ context->error(escape(reader.errorString()), ReportContext::FODC0002, QSourceLocation(uri, reader.lineNumber(), reader.columnNumber()));
+
+ return false;
+ }
+ case QXmlStreamReader::NoToken:
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO,
+ "This token is never expected to be received.");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+Item AccelTreeResourceLoader::openDocument(const QUrl &uri,
+ const ReportContext::Ptr &context)
+{
+ const AccelTree::Ptr doc(m_loadedDocuments.value(uri));
+
+ if(doc)
+ return doc->root(QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
+ else
+ {
+ if(retrieveDocument(uri, context))
+ return m_loadedDocuments.value(uri)->root(QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
+ else
+ return Item();
+ }
+}
+
+SequenceType::Ptr AccelTreeResourceLoader::announceDocument(const QUrl &uri, const Usage)
+{
+ // TODO deal with the usage thingy
+ Q_ASSERT(uri.isValid());
+ Q_ASSERT(!uri.isRelative());
+ Q_UNUSED(uri); /* Needed when compiling in release mode. */
+
+ return CommonSequenceTypes::ZeroOrOneDocumentNode;
+}
+
+bool AccelTreeResourceLoader::isDocumentAvailable(const QUrl &uri)
+{
+ return retrieveDocument(uri, ReportContext::Ptr());
+}
+
+static inline uint qHash(const QPair<QUrl, QString> &desc)
+{
+ /* Probably a lousy hash. */
+ return qHash(desc.first) + qHash(desc.second);
+}
+
+bool AccelTreeResourceLoader::retrieveUnparsedText(const QUrl &uri,
+ const QString &encoding,
+ const ReportContext::Ptr &context,
+ const SourceLocationReflection *const where)
+{
+ const AutoPtr<QNetworkReply> reply(load(uri, m_networkAccessDelegator, context));
+
+ if(!reply)
+ return false;
+
+ const QTextCodec * codec;
+ if(encoding.isEmpty())
+ {
+ /* XSL Transformations (XSLT) Version 2.0 16.2 Reading Text Files:
+ *
+ * "if the media type of the resource is text/xml or application/xml
+ * (see [RFC2376]), or if it matches the conventions text/\*+xml or
+ * application/\*+xml (see [RFC3023] and/or its successors), then the
+ * encoding is recognized as specified in [XML 1.0]"
+ */
+ codec = QTextCodec::codecForMib(106);
+ }
+ else
+ {
+ codec = QTextCodec::codecForName(encoding.toLatin1());
+ if(codec && context)
+ {
+ context->error(QtXmlPatterns::tr("%1 is an unsupported encoding.").arg(formatURI(encoding)),
+ ReportContext::XTDE1190,
+ where);
+ }
+ else
+ return false;
+ }
+
+ QTextCodec::ConverterState converterState;
+ const QByteArray inData(reply->readAll());
+ const QString result(codec->toUnicode(inData.constData(), inData.length(), &converterState));
+
+ if(converterState.invalidChars)
+ {
+ if(context)
+ {
+ context->error(QtXmlPatterns::tr("%1 contains octets which are disallowed in "
+ "the requested encoding %2.").arg(formatURI(uri),
+ formatURI(encoding)),
+ ReportContext::XTDE1190,
+ where);
+ }
+ else
+ return false;
+ }
+
+ const int len = result.length();
+ /* This code is a candidate for threading. Divide and conqueror. */
+ for(int i = 0; i < len; ++i)
+ {
+ if(!QXmlUtils::isChar(result.at(i)))
+ {
+ if(context)
+ {
+ context->error(QtXmlPatterns::tr("The codepoint %1, occurring in %2 using encoding %3, "
+ "is an invalid XML character.").arg(formatData(result.at(i)),
+ formatURI(uri),
+ formatURI(encoding)),
+ ReportContext::XTDE1190,
+ where);
+ }
+ else
+ return false;
+ }
+ }
+
+ m_unparsedTexts.insert(qMakePair(uri, encoding), result);
+ return true;
+}
+
+bool AccelTreeResourceLoader::isUnparsedTextAvailable(const QUrl &uri,
+ const QString &encoding)
+{
+ return retrieveUnparsedText(uri, encoding, ReportContext::Ptr(), 0);
+}
+
+Item AccelTreeResourceLoader::openUnparsedText(const QUrl &uri,
+ const QString &encoding,
+ const ReportContext::Ptr &context,
+ const SourceLocationReflection *const where)
+{
+ const QString &text = m_unparsedTexts.value(qMakePair(uri, encoding));
+
+ if(text.isNull())
+ {
+ if(retrieveUnparsedText(uri, encoding, context, where))
+ return openUnparsedText(uri, encoding, context, where);
+ else
+ return Item();
+ }
+ else
+ return AtomicString::fromValue(text);
+}
+
+QSet<QUrl> AccelTreeResourceLoader::deviceURIs() const
+{
+ QHash<QUrl, AccelTree::Ptr>::const_iterator it(m_loadedDocuments.constBegin());
+ const QHash<QUrl, AccelTree::Ptr>::const_iterator end(m_loadedDocuments.constEnd());
+ QSet<QUrl> retval;
+
+ while (it != end)
+ {
+ if(it.key().toString().startsWith(QLatin1String("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:")))
+ retval.insert(it.key());
+
+ ++it;
+ }
+
+ return retval;
+}
+
+void AccelTreeResourceLoader::clear(const QUrl &uri)
+{
+ m_loadedDocuments.remove(uri);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/xmlpatterns/acceltree/qacceltreeresourceloader_p.h b/src/xmlpatterns/acceltree/qacceltreeresourceloader_p.h
new file mode 100644
index 0000000..863fd65
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qacceltreeresourceloader_p.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_AccelTreeResourceLoader_H
+#define Patternist_AccelTreeResourceLoader_H
+
+#include <QtCore/QHash>
+#include <QtCore/QEventLoop>
+#include <QtNetwork/QNetworkReply>
+
+#include "qabstractxmlreceiver.h"
+#include "qacceltree_p.h"
+#include "qdeviceresourceloader_p.h"
+#include "qnamepool_p.h"
+#include "qnetworkaccessdelegator_p.h"
+#include "qreportcontext_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+namespace QPatternist
+{
+ /**
+ * @short An helper class which enables QNetworkAccessManager
+ * to be used in a blocking manner.
+ *
+ * @see AccelTreeResourceLoader::load()
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ class NetworkLoop : public QEventLoop
+ {
+ Q_OBJECT
+ public:
+ NetworkLoop() : m_hasReceivedError(false)
+ {
+ }
+
+ public Q_SLOTS:
+ void error(QNetworkReply::NetworkError code)
+ {
+ Q_UNUSED(code);
+ m_hasReceivedError = true;
+ exit(1);
+ }
+
+ void finished()
+ {
+ if(m_hasReceivedError)
+ exit(1);
+ else
+ exit(0);
+ }
+ private:
+ bool m_hasReceivedError;
+ };
+
+ /**
+ * @short Handles requests for documents, and instantiates
+ * them as AccelTree instances.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ class Q_AUTOTEST_EXPORT AccelTreeResourceLoader : public DeviceResourceLoader
+ {
+ public:
+ /**
+ * AccelTreeResourceLoader does not own @p context.
+ */
+ AccelTreeResourceLoader(const NamePool::Ptr &np,
+ const NetworkAccessDelegator::Ptr &networkDelegator);
+
+ virtual Item openDocument(const QUrl &uri,
+ const ReportContext::Ptr &context);
+ virtual SequenceType::Ptr announceDocument(const QUrl &uri, const Usage usageHint);
+ virtual bool isDocumentAvailable(const QUrl &uri);
+
+ virtual bool isUnparsedTextAvailable(const QUrl &uri,
+ const QString &encoding);
+
+ virtual Item openUnparsedText(const QUrl &uri,
+ const QString &encoding,
+ const ReportContext::Ptr &context,
+ const SourceLocationReflection *const where);
+
+
+ /**
+ * @short Helper function that do NetworkAccessDelegator::get(), but
+ * does it blocked.
+ *
+ * The returned QNetworkReply has emitted QNetworkReply::finished().
+ *
+ * The caller owns the return QIODevice instance.
+ *
+ * @p context may be @c null or valid. If @c null, no error reporting
+ * is done and @c null is returned.
+ *
+ * @see NetworkAccessDelegator
+ */
+ static QNetworkReply *load(const QUrl &uri,
+ QNetworkAccessManager *const networkManager,
+ const ReportContext::Ptr &context);
+
+ /**
+ * @overload
+ */
+ static QNetworkReply *load(const QUrl &uri,
+ const NetworkAccessDelegator::Ptr &networkDelegator,
+ const ReportContext::Ptr &context);
+
+ /**
+ * @short Returns the URIs this AccelTreeResourceLoader has loaded
+ * which are for devices through variable bindings.
+ */
+ virtual QSet<QUrl> deviceURIs() const;
+
+ virtual void clear(const QUrl &uri);
+ private:
+ static bool streamToReceiver(QIODevice *const dev,
+ QAbstractXmlReceiver *const receiver,
+ const NamePool::Ptr &np,
+ const ReportContext::Ptr &context,
+ const QUrl &uri);
+ bool retrieveDocument(const QUrl &uri,
+ const ReportContext::Ptr &context);
+ /**
+ * If @p context is @c null, no error reporting should be done.
+ */
+ bool retrieveUnparsedText(const QUrl &uri,
+ const QString &encoding,
+ const ReportContext::Ptr &context,
+ const SourceLocationReflection *const where);
+
+ QHash<QUrl, AccelTree::Ptr> m_loadedDocuments;
+ const NamePool::Ptr m_namePool;
+ const NetworkAccessDelegator::Ptr m_networkAccessDelegator;
+ QHash<QPair<QUrl, QString>, QString> m_unparsedTexts;
+ };
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/xmlpatterns/acceltree/qcompressedwhitespace.cpp b/src/xmlpatterns/acceltree/qcompressedwhitespace.cpp
new file mode 100644
index 0000000..730d1d9
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qcompressedwhitespace.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QString>
+
+#include "qcompressedwhitespace_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+CompressedWhitespace::CharIdentifier CompressedWhitespace::toIdentifier(const QChar ch)
+{
+ switch(ch.unicode())
+ {
+ case ' ':
+ return Space;
+ case '\n':
+ return LF;
+ case '\r':
+ return CR;
+ case '\t':
+ return Tab;
+ default:
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO,
+ "The caller must guarantee only whitespace is passed.");
+ return Tab;
+ }
+ }
+}
+
+bool CompressedWhitespace::isEven(const int number)
+{
+ Q_ASSERT(number >= 0);
+ return number % 2 == 0;
+}
+
+quint8 CompressedWhitespace::toCompressedChar(const QChar ch, const int len)
+{
+ Q_ASSERT(len > 0);
+ Q_ASSERT(len <= MaxCharCount);
+
+ return len + toIdentifier(ch);
+}
+
+QChar CompressedWhitespace::toChar(const CharIdentifier id)
+{
+ switch(id)
+ {
+ case Space: return QLatin1Char(' ');
+ case CR: return QLatin1Char('\r');
+ case LF: return QLatin1Char('\n');
+ case Tab: return QLatin1Char('\t');
+ default:
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected input");
+ return QChar();
+ }
+ }
+}
+
+QString CompressedWhitespace::compress(const QStringRef &input)
+{
+ Q_ASSERT(!isEven(1) && isEven(0) && isEven(2));
+ Q_ASSERT(!input.isEmpty());
+
+ QString result;
+ const int len = input.length();
+
+ /* The amount of compressed characters. For instance, if input is
+ * four spaces followed by one tab, compressedChars will be 2, and the resulting
+ * QString will have a length of 1, two compressedChars stored in one QChar. */
+ int compressedChars = 0;
+
+ for(int i = 0; i < len; ++i)
+ {
+ const QChar c(input.at(i));
+
+ int start = i;
+
+ while(true)
+ {
+ if(i + 1 == input.length() || input.at(i + 1) != c)
+ break;
+ else
+ ++i;
+ }
+
+ /* The length of subsequent whitespace characters in the input. */
+ int wsLen = (i - start) + 1;
+
+ /* We might get a sequence of whitespace that is so long, that we can't
+ * store it in one unit/byte. In that case we chop it into as many subsequent
+ * ones that is needed. */
+ while(true)
+ {
+ const int unitLength = qMin(wsLen, int(MaxCharCount));
+ wsLen -= unitLength;
+
+ ushort resultCP = toCompressedChar(c, unitLength);
+
+ if(isEven(compressedChars))
+ result += QChar(resultCP);
+ else
+ {
+ resultCP = resultCP << 8;
+ resultCP |= result.at(result.size() - 1).unicode();
+ result[result.size() - 1] = resultCP;
+ }
+
+ ++compressedChars;
+
+ if(wsLen == 0)
+ break;
+ }
+ }
+
+ return result;
+}
+
+QString CompressedWhitespace::decompress(const QString &input)
+{
+ Q_ASSERT(!input.isEmpty());
+ const int len = input.length() * 2;
+ QString retval;
+
+ for(int i = 0; i < len; ++i)
+ {
+ ushort cp = input.at(i / 2).unicode();
+
+ if(isEven(i))
+ cp &= Lower8Bits;
+ else
+ {
+ cp = cp >> 8;
+
+ if(cp == 0)
+ return retval;
+ }
+
+ const quint8 wsLen = cp & Lower6Bits;
+ const quint8 id = cp & UpperTwoBits;
+
+ /* Resize retval, and fill in on the top. */
+ const int oldSize = retval.size();
+ const int newSize = retval.size() + wsLen;
+ retval.resize(newSize);
+ const QChar ch(toChar(CharIdentifier(id)));
+
+ for(int f = oldSize; f < newSize; ++f)
+ retval[f] = ch;
+ }
+
+ return retval;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/xmlpatterns/acceltree/qcompressedwhitespace_p.h b/src/xmlpatterns/acceltree/qcompressedwhitespace_p.h
new file mode 100644
index 0000000..92bc0a5
--- /dev/null
+++ b/src/xmlpatterns/acceltree/qcompressedwhitespace_p.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_CompressedWhitespace_H
+#define Patternist_CompressedWhitespace_H
+
+#include <QtGlobal>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QChar;
+class QString;
+class QStringRef;
+
+namespace QPatternist
+{
+ /**
+ * @short A compression facility for whitespace nodes.
+ *
+ * CompressedWhitespace compresses and decompresses strings that consists of
+ * whitespace only, and do so with a scheme that is designed to do this
+ * specialized task in an efficient way. The approach is simple: each
+ * sequence of equal whitespace in the input gets coded into one byte,
+ * where the first two bits signals the type, CharIdentifier, and the
+ * remininding six bits is the count.
+ *
+ * For instance, this scheme manages to compress a sequence of spaces
+ * followed by a new line into 16 bits(one QChar), and QString stores
+ * strings of one QChar quite efficiently, by avoiding a heap allocation.
+ *
+ * There is no way to tell whether a QString is compressed or not.
+ *
+ * The compression scheme originates from Saxon, by Michael Kay.
+ *
+ * @author Frans Englich <fenglich@trolltech.com>
+ */
+ class CompressedWhitespace
+ {
+ public:
+ /**
+ * @short Compresses @p input into a compressed format, returned
+ * as a QString.
+ *
+ * The caller guarantees that input is not empty
+ * and consists only of whitespace.
+ *
+ * The returned format is opaque. There is no way to find out
+ * whether a QString contains compressed data or not.
+ *
+ * @see decompress()
+ */
+ static QString compress(const QStringRef &input);
+
+ /**
+ * @short Decompresses @p input into a usual QString.
+ *
+ * @p input must be a QString as per returned from compress().
+ *
+ * @see compress()
+ */
+ static QString decompress(const QString &input);
+
+ private:
+ /**
+ * We use the two upper bits for communicating what space it is.
+ */
+ enum CharIdentifier
+ {
+ Space = 0x0,
+
+ /**
+ * 0xA, \\r
+ *
+ * Binary: 10000000
+ */
+ CR = 0x80,
+
+ /**
+ * 0xD, \\n
+ *
+ * Binary: 01000000
+ */
+ LF = 0x40,
+
+ /**
+ * Binary: 11000000
+ */
+ Tab = 0xC0
+ };
+
+ enum Constants
+ {
+ /* We can at maximum store this many consecutive characters
+ * of one type. We use 6 bits for the count. */
+ MaxCharCount = (1 << 6) - 1,
+
+ /**
+ * Binary: 11111111
+ */
+ Lower8Bits = (1 << 8) - 1,
+
+ /**
+ * Binary: 111111
+ */
+ Lower6Bits = (1 << 6) - 1,
+
+ /*
+ * Binary: 11000000
+ */
+ UpperTwoBits = 3 << 6
+ };
+
+ static inline CharIdentifier toIdentifier(const QChar ch);
+
+ static inline quint8 toCompressedChar(const QChar ch, const int len);
+ static inline QChar toChar(const CharIdentifier id);
+
+ /**
+ * @short Returns @c true if @p number is an even number, otherwise
+ * @c false.
+ */
+ static inline bool isEven(const int number);
+
+ /**
+ * @short This class can only be used via its static members.
+ */
+ inline CompressedWhitespace();
+ Q_DISABLE_COPY(CompressedWhitespace)
+ };
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif