src/xmlpatterns/api/qabstractxmlreceiver.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/xmlpatterns/api/qabstractxmlreceiver.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QString>
+
+#include "qitem_p.h"
+
+#include "qabstractxmlreceiver_p.h"
+#include "qabstractxmlreceiver.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+  \class QAbstractXmlReceiver
+  \brief The QAbstractXmlReceiver class provides a callback interface
+         for transforming the output of a QXmlQuery.
+  \reentrant
+  \since 4.4
+  \ingroup xml-tools
+
+  QAbstractXmlReceiver is an abstract base class that provides
+  a callback interface for receiving an \l {XQuery Sequence}
+  {XQuery sequence}, usually the output of an QXmlQuery, and
+  transforming that sequence into a structure of your choosing,
+  usually XML. Consider the example:
+
+  \snippet doc/src/snippets/code/src_xmlpatterns_api_qabstractxmlreceiver.cpp 0
+
+  First it constructs a \l {QXmlQuery} {query} that gets the
+  first paragraph from document \c index.html. Then it constructs
+  an \l {QXmlSerializer} {XML serializer} with the \l {QXmlQuery}
+  {query} and \l {QIODevice} {myOutputDevice} (Note the
+  \l {QXmlSerializer} {serializer} is an \e {XML receiver},
+  ie a subclass of QAbstractXmlReceiver). Finally, it
+  \l {QXmlQuery::evaluateTo()} {evaluates} the
+  \l {QXmlQuery} {query}, producing an ordered sequence of calls
+  to the \l {QXmlSerializer} {serializer's} callback functions.
+  The sequence of callbacks transforms the query output to XML
+  and writes it to \l {QIODevice} {myOutputDevice}.
+
+  Although the example uses \l {QXmlQuery} to produce the sequence
+  of callbacks to functions in QAbstractXmlReceiver, you can call
+  the callback functions directly as long as your sequence of
+  calls represents a valid \l {XQuery Sequence} {XQuery sequence}.
+
+  \target XQuery Sequence
+  \section1 XQuery Sequences
+
+  An XQuery \a sequence is an ordered collection of zero, one,
+  or many \e items. Each \e item is either an \e {atomic value}
+  or a \e {node}. An \e {atomic value} is a simple data value.
+
+  There are six kinds of \e nodes.
+
+  \list
+
+  \o An \e {Element Node} represents an XML element.
+
+  \o An \e {Attribute Node} represents an XML attribute.
+
+  \o A \e {Document Node} represents an entire XML document.
+
+  \o A \e {Text Node} represents character data (element content).
+
+  \o A \e {Processing Instruction Node} represents an XML
+  processing instruction, which is used in an XML document
+  to tell the application reading the document to perform
+  some action. A typical example is to use a processing
+  instruction to tell the application to use a particular
+  XSLT stylesheet to display the document.
+
+  \o And a \e {Comment node} represents an XML comment.
+
+  \endlist
+
+  The \e sequence of \e nodes and \e {atomic values} obeys
+  the following rules. Note that \e {Namespace Node} refers
+  to a special \e {Attribute Node} with name \e {xmlns}.
+
+  \list
+
+  \o Each \e node appears in the \e sequence before its children
+  and their descendants appear.
+
+  \o A \e node's descendants appear in the \e sequence before
+  any of its siblings appear.
+
+  \o A \e {Document Node} represents an entire document. Zero or
+  more \e {Document Nodes} can appear in a \e sequence, but they
+  can only be top level items (i.e., a \e {Document Node} can't
+  be a child of another \e node.
+
+  \o \e {Namespace Nodes} immediately follow the \e {Element Node}
+   with which they are associated.
+
+  \o \e {Attribute Nodes} immediately follow the \e {Namespace Nodes}
+   of the element with which they are associated, or...
+
+   \o If there are no \e {Namespace Nodes} following an element, then
+   the \e {Attribute Nodes} immediately follow the element.
+
+   \o An \e {atomic value} can only appear as a top level \e item,
+   i.e., it can't appear as a child of a \e node.
+
+   \o \e {Processing Instruction Nodes} do not have children, and
+   their parent is either a \e {Document Node} or an \e {Element
+   Node}.
+
+   \o \e {Comment Nodes} do not have children, and
+   their parent is either a \e {Document Node} or an \e {Element
+   Node}.
+
+  \endlist
+
+  The \e sequence of \e nodes and \e {atomic values} is sent to
+  an QAbstractXmlReceiver (QXmlSerializer in
+  the example above) as a sequence of calls to the receiver's
+  callback functions. The mapping of callback functions to
+  sequence items is as follows.
+
+  \list
+
+  \o startDocument() and endDocument() are called for each
+  \e {Document Node} in the \e sequence. endDocument() is not
+  called until all the \e {Document Node's} children have
+  appeared in the \e sequence.
+
+  \o startElement() and endElement() are called for each
+  \e {Element Node}. endElement() is not called until all the
+  \e {Element Node's} children have appeared in the \e sequence.
+
+  \o attribute() is called for each \e {Attribute Node}.
+
+  \o comment() is called for each \e {Comment Node}.
+
+  \o characters() is called for each \e {Text Node}.
+
+  \o processingInstruction() is called for each \e {Processing
+  Instruction Node}.
+
+  \o namespaceBinding() is called for each \e {Namespace Node}.
+
+  \o atomicValue() is called for each \e {atomic value}.
+
+  \endlist
+
+  For a complete explanation of XQuery sequences, visit
+  \l {http://www.w3.org/TR/xpath-datamodel/}{XQuery Data Model}.
+
+  \sa {http://www.w3.org/TR/xpath-datamodel/}{W3C XQuery 1.0 and XPath 2.0 Data Model (XDM)}
+  \sa QXmlSerializer
+  \sa QXmlResultItems
+ */
+
+template<const QXmlNodeModelIndex::Axis axis>
+void QAbstractXmlReceiver::sendFromAxis(const QXmlNodeModelIndex &node)
+{
+    Q_ASSERT(!node.isNull());
+    const QXmlNodeModelIndex::Iterator::Ptr it(node.iterate(axis));
+    QXmlNodeModelIndex next(it->next());
+
+    while(!next.isNull())
+    {
+        sendAsNode(next);
+        next = it->next();
+    }
+}
+
+/*!
+ \internal
+ */
+QAbstractXmlReceiver::QAbstractXmlReceiver(QAbstractXmlReceiverPrivate *d)
+  : d_ptr(d)
+{
+}
+
+/*!
+  Constructs an abstract xml receiver.
+ */
+QAbstractXmlReceiver::QAbstractXmlReceiver() : d_ptr(0)
+{
+}
+
+/*!
+  Destroys the xml receiver.
+ */
+QAbstractXmlReceiver::~QAbstractXmlReceiver()
+{
+}
+
+/*!
+  \fn void QAbstractXmlReceiver::startElement(const QXmlName &name)
+
+  This callback is called when a new element node appears
+  in the \l {XQuery Sequence} {sequence}. \a name is the
+  valid \l {QXmlName} {name} of the node element.
+ */
+
+/*
+### Qt 5:
+
+Consider how source locations should be communicated. Maybe every signature
+should be extended by adding "qint64 line = -1, qint64 column = -1".
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::endElement()
+
+  This callback is called when the end of an element node
+  appears in the \l {XQuery Sequence} {sequence}.
+*/
+
+/*!
+  \fn void QAbstractXmlReceiver::attribute(const QXmlName &name,
+                                           const QStringRef &value)
+  This callback is called when an attribute node
+  appears in the \l {XQuery Sequence} {sequence}.
+  \a name is the \l {QXmlName} {attribute name} and
+  the \a value string contains the attribute value.
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::comment(const QString &value)
+
+  This callback is called when a comment node appears
+  in the \l {XQuery Sequence} {sequence}. The \a value
+  is the comment text, which must not contain the string
+  "--".
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::characters(const QStringRef &value)
+
+  This callback is called when a text node appears in the
+  \l {XQuery Sequence} {sequence}. The \a value contains
+  the text. Adjacent text nodes may not occur in the
+  \l {XQuery Sequence} {sequence}, i.e., this callback must not
+  be called twice in a row.
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::startDocument()
+
+  This callback is called when a document node appears
+  in the \l {XQuery Sequence} {sequence}.
+ */
+
+/*
+### Qt 5:
+
+Change
+    virtual void startDocument() = 0;
+
+To:
+    virtual void startDocument(const QUrl &uri) = 0;
+
+Such that it allows the document URI to be communicated. The contract would
+allow null QUrls.
+*/
+
+/*!
+  \fn void QAbstractXmlReceiver::endDocument()
+
+  This callback is called when the end of a document node
+  appears in the \l {XQuery Sequence} {sequence}.
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::processingInstruction(const QXmlName &target,
+                                                       const QString &value)
+
+  This callback is called when a processing instruction
+  appears in the \l {XQuery Sequence} {sequence}.
+  A processing instruction is used in an XML document
+  to tell the application reading the document to
+  perform some action. A typical example is to use a
+  processing instruction to tell the application to use a
+  particular XSLT stylesheet to process the document.
+
+  \quotefile doc/src/snippets/patternist/xmlStylesheet.xq
+
+  \a target is the \l {QXmlName} {name} of the processing
+  instruction. Its \e prefix and \e {namespace URI} must both
+  be empty. Its \e {local name} is the target. In the above
+  example, the name is \e {xml-stylesheet}.
+
+  The \a value specifies the action to be taken. Note that
+  the \a value must not contain the string "?>". In the above
+  example, the \a value is \e{type="test/xsl" href="formatter.xsl}.
+
+  Generally, use of processing instructions should be avoided,
+  because they are not namespace aware and in many contexts
+  are stripped out anyway. Processing instructions can often
+  be replaced with elements from a custom namespace.
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::atomicValue(const QVariant &value)
+
+  This callback is called when an atomic value appears in the \l
+  {XQuery Sequence} {sequence}. The \a value is a simple \l {QVariant}
+  {data value}. It is guaranteed to be \l {QVariant::isValid()}
+  {valid}.
+ */
+
+/*!
+  \fn virtual void QAbstractXmlReceiver::namespaceBinding(const QXmlName &name)
+
+  This callback is called when a namespace binding is in scope of an
+  element. A namespace is defined by a URI. In the \l {QXmlName}
+  \a name, the value of \l {QXmlName::namespaceUri()} is that URI. The
+  value of \l {QXmlName::prefix()} is the prefix that the URI is bound
+  to. The local name is insignificant and can be an arbitrary value.
+ */
+
+/*!
+  \internal
+
+   Treats \a outputItem as a node and calls the appropriate function,
+   e.g., attribute() or comment(), depending on its
+   QXmlNodeModelIndex::NodeKind.
+
+   This is a helper function that subclasses can use to multiplex
+   Nodes received via item().
+ */
+void QAbstractXmlReceiver::sendAsNode(const QPatternist::Item &outputItem)
+{
+    Q_ASSERT(outputItem);
+    Q_ASSERT(outputItem.isNode());
+    const QXmlNodeModelIndex asNode = outputItem.asNode();
+
+    switch(asNode.kind())
+    {
+        case QXmlNodeModelIndex::Attribute:
+        {
+            const QString &v = outputItem.stringValue();
+            attribute(asNode.name(), QStringRef(&v));
+            return;
+        }
+        case QXmlNodeModelIndex::Element:
+        {
+            startElement(asNode.name());
+
+            /* First the namespaces, then attributes, then the children. */
+            asNode.sendNamespaces(this);
+            sendFromAxis<QXmlNodeModelIndex::AxisAttribute>(asNode);
+            sendFromAxis<QXmlNodeModelIndex::AxisChild>(asNode);
+
+            endElement();
+
+            return;
+        }
+        case QXmlNodeModelIndex::Text:
+        {
+            const QString &v = asNode.stringValue();
+            characters(QStringRef(&v));
+            return;
+        }
+        case QXmlNodeModelIndex::ProcessingInstruction:
+        {
+            processingInstruction(asNode.name(), outputItem.stringValue());
+            return;
+        }
+        case QXmlNodeModelIndex::Comment:
+        {
+            comment(outputItem.stringValue());
+            return;
+        }
+        case QXmlNodeModelIndex::Document:
+        {
+            startDocument();
+            sendFromAxis<QXmlNodeModelIndex::AxisChild>(asNode);
+            endDocument();
+            return;
+        }
+        case QXmlNodeModelIndex::Namespace:
+            Q_ASSERT_X(false, Q_FUNC_INFO, "Not implemented");
+    }
+
+    Q_ASSERT_X(false, Q_FUNC_INFO,
+               QString::fromLatin1("Unknown node type: %1").arg(asNode.kind()).toUtf8().constData());
+}
+
+/*!
+  \internal
+
+   This function may be called instead of characters() if, and only if,
+   \a value consists only of whitespace.
+
+   The caller gurantees that \a value is not empty.
+
+   \e Whitespace refers to a sequence of characters that are either
+   spaces, tabs, or newlines, in any order. In other words, not all
+   the Unicode whitespace category is considered whitespace here.
+
+   However, there is no guarantee or requirement that whitespaceOnly()
+   is called for text nodes containing whitespace only. characters()
+   may be called just as well. This is why the default implementation
+   for whitespaceOnly() calls characters().
+
+   \sa characters()
+ */
+void QAbstractXmlReceiver::whitespaceOnly(const QStringRef &value)
+{
+    Q_ASSERT_X(value.toString().trimmed().isEmpty(), Q_FUNC_INFO,
+               "The caller must guarantee only whitespace is passed. Use characters() in other cases.");
+    const QString &v = value.toString();
+    characters(QStringRef(&v));
+}
+
+/*!
+  \internal
+ */
+void QAbstractXmlReceiver::item(const QPatternist::Item &item)
+{
+    if(item.isNode())
+        return sendAsNode(item);
+    else
+        atomicValue(QPatternist::AtomicValue::toQt(item.asAtomicValue()));
+}
+
+/*!
+ \fn void QAbstractXmlReceiver::startOfSequence()
+
+ This callback is called once only, right before the
+ \l {XQuery Sequence} {sequence} begins.
+ */
+
+/*!
+  \fn void QAbstractXmlReceiver::endOfSequence()
+
+ This callback is called once only, right after the
+ \l {XQuery Sequence} {sequence} ends.
+ */
+
+QT_END_NAMESPACE
+