1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #include "qabstractxmlreceiver.h"
45 #include "qabstractxmlnodemodel_p.h"
46 #include "qacceliterators_p.h"
47 #include "qacceltree_p.h"
48 #include "qatomicstring_p.h"
49 #include "qcommonvalues_p.h"
50 #include "qcompressedwhitespace_p.h"
52 #include "quntypedatomic_p.h"
53 #include "qxpathhelper_p.h"
57 using namespace QPatternist;
59 namespace QPatternist {
61 class AccelTreePrivate : public QAbstractXmlNodeModelPrivate
64 AccelTreePrivate(AccelTree *accelTree)
65 : m_accelTree(accelTree)
69 virtual QSourceLocation sourceLocation(const QXmlNodeModelIndex &index) const
71 return m_accelTree->sourceLocation(index);
75 AccelTree *m_accelTree;
79 AccelTree::AccelTree(const QUrl &docURI, const QUrl &bURI)
80 : QAbstractXmlNodeModel(new AccelTreePrivate(this))
81 , m_documentURI(docURI)
84 /* Pre-allocate at least a little bit. */
85 // TODO. Do it according to what an average 4 KB doc contains.
86 basicData.reserve(100);
90 void AccelTree::printStats(const NamePool::Ptr &np) const
94 Q_UNUSED(np); /* Needed when compiling in release mode. */
96 const int len = basicData.count();
98 pDebug() << "AccelTree stats for" << (m_documentURI.isEmpty() ? QString::fromLatin1("<empty URI>") : m_documentURI.toString());
99 pDebug() << "Maximum pre number:" << maximumPreNumber();
100 pDebug() << "+---------------+-------+-------+---------------+-------+--------------+-------+";
101 pDebug() << "| Pre number | Depth | Size | Post Number | Kind | Name | Value |";
102 pDebug() << "+---------------+-------+-------+---------------+-------+--------------+-------+";
103 for(int i = 0; i < len; ++i)
105 const BasicNodeData &v = basicData.at(i);
107 << "\t\t|" << v.depth()
109 << "\t|" << postNumber(i)
111 << "\t\t|" << (v.name().isNull() ? QString::fromLatin1("(none)") : np->displayName(v.name()))
112 << "\t\t|" << ((v.kind() == QXmlNodeModelIndex::Text && isCompressed(i)) ? CompressedWhitespace::decompress(data.value(i))
116 pDebug() << '|' << QString().arg(i, 14)
117 << '|' << QString().arg(v.depth(), 6)
118 << '|' << QString().arg(v.size(), 6)
119 << '|' << QString().arg(postNumber(i), 14)
120 << '|' << QString().arg(v.kind(), 6)
124 pDebug() << "+---------------+-------+-------+---------------+-------+--------------+";
125 pDebug() << "Namespaces(" << namespaces.count() << "):";
127 QHashIterator<PreNumber, QVector<QXmlName> > it(namespaces);
132 pDebug() << "PreNumber: " << QString::number(it.key());
133 for(int i = 0; i < it.value().count(); ++i)
134 pDebug() << "\t\t" << np->stringForPrefix(it.value().at(i).prefix()) << " = " << np->stringForNamespace(it.value().at(i).namespaceURI());
140 QUrl AccelTree::baseUri(const QXmlNodeModelIndex &ni) const
142 switch(kind(toPreNumber(ni)))
144 case QXmlNodeModelIndex::Document:
146 case QXmlNodeModelIndex::Element:
148 const QXmlNodeModelIndex::Iterator::Ptr it(iterate(ni, QXmlNodeModelIndex::AxisAttribute));
149 QXmlNodeModelIndex next(it->next());
151 while(!next.isNull())
153 if(next.name() == QXmlName(StandardNamespaces::xml, StandardLocalNames::base))
155 const QUrl candidate(next.stringValue());
156 // TODO. The xml:base spec says to do URI escaping here.
158 if(!candidate.isValid())
160 else if(candidate.isRelative())
162 const QXmlNodeModelIndex par(parent(ni));
165 return baseUri().resolved(candidate);
167 return par.baseUri().resolved(candidate);
176 /* We have no xml:base-attribute. Can any parent supply us a base URI? */
177 const QXmlNodeModelIndex par(parent(ni));
182 return par.baseUri();
184 case QXmlNodeModelIndex::ProcessingInstruction:
186 case QXmlNodeModelIndex::Comment:
188 case QXmlNodeModelIndex::Attribute:
190 case QXmlNodeModelIndex::Text:
192 const QXmlNodeModelIndex par(ni.iterate(QXmlNodeModelIndex::AxisParent)->next());
196 return par.baseUri();
198 case QXmlNodeModelIndex::Namespace:
202 Q_ASSERT_X(false, Q_FUNC_INFO, "This line is never supposed to be reached.");
206 QUrl AccelTree::documentUri(const QXmlNodeModelIndex &ni) const
208 if(kind(toPreNumber(ni)) == QXmlNodeModelIndex::Document)
209 return documentUri();
214 QXmlNodeModelIndex::NodeKind AccelTree::kind(const QXmlNodeModelIndex &ni) const
216 return kind(toPreNumber(ni));
219 QXmlNodeModelIndex::DocumentOrder AccelTree::compareOrder(const QXmlNodeModelIndex &ni1,
220 const QXmlNodeModelIndex &ni2) const
222 Q_ASSERT_X(ni1.model() == ni2.model(), Q_FUNC_INFO,
223 "The API docs guarantees the two nodes are from the same model");
225 const PreNumber p1 = ni1.data();
226 const PreNumber p2 = ni2.data();
229 return QXmlNodeModelIndex::Is;
231 return QXmlNodeModelIndex::Precedes;
233 return QXmlNodeModelIndex::Follows;
236 QXmlNodeModelIndex AccelTree::root(const QXmlNodeModelIndex &) const
238 return createIndex(qint64(0));
241 QXmlNodeModelIndex AccelTree::parent(const QXmlNodeModelIndex &ni) const
243 const AccelTree::PreNumber p = basicData.at(toPreNumber(ni)).parent();
246 return QXmlNodeModelIndex();
248 return createIndex(p);
251 QXmlNodeModelIndex::Iterator::Ptr AccelTree::iterate(const QXmlNodeModelIndex &ni,
252 QXmlNodeModelIndex::Axis axis) const
254 const PreNumber preNumber = toPreNumber(ni);
258 case QXmlNodeModelIndex::AxisChildOrTop:
260 if(!hasParent(preNumber))
262 switch(kind(preNumber))
264 case QXmlNodeModelIndex::Comment:
266 case QXmlNodeModelIndex::ProcessingInstruction:
268 case QXmlNodeModelIndex::Element:
270 case QXmlNodeModelIndex::Text:
271 return makeSingletonIterator(ni);
272 case QXmlNodeModelIndex::Attribute:
274 case QXmlNodeModelIndex::Document:
276 case QXmlNodeModelIndex::Namespace:
280 /* Else, fallthrough to AxisChild. */
282 case QXmlNodeModelIndex::AxisChild:
284 if(hasChildren(preNumber))
285 return QXmlNodeModelIndex::Iterator::Ptr(new ChildIterator(this, preNumber));
287 return makeEmptyIterator<QXmlNodeModelIndex>();
289 case QXmlNodeModelIndex::AxisAncestor:
291 if(hasParent(preNumber))
292 return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<false>(this, preNumber));
294 return makeEmptyIterator<QXmlNodeModelIndex>();
296 case QXmlNodeModelIndex::AxisAncestorOrSelf:
297 return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<true>(this, preNumber));
298 case QXmlNodeModelIndex::AxisParent:
300 if(hasParent(preNumber))
301 return makeSingletonIterator(createIndex(parent(preNumber)));
303 return makeEmptyIterator<QXmlNodeModelIndex>();
305 case QXmlNodeModelIndex::AxisDescendant:
307 if(hasChildren(preNumber))
308 return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<false>(this, preNumber));
310 return makeEmptyIterator<QXmlNodeModelIndex>();
312 case QXmlNodeModelIndex::AxisDescendantOrSelf:
313 return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<true>(this, preNumber));
314 case QXmlNodeModelIndex::AxisFollowing:
316 if(preNumber == maximumPreNumber())
317 return makeEmptyIterator<QXmlNodeModelIndex>();
319 return QXmlNodeModelIndex::Iterator::Ptr(new FollowingIterator(this, preNumber));
321 case QXmlNodeModelIndex::AxisAttributeOrTop:
323 if(!hasParent(preNumber) && kind(preNumber) == QXmlNodeModelIndex::Attribute)
324 return makeSingletonIterator(ni);
325 /* Else, falthrough to AxisAttribute. */
327 case QXmlNodeModelIndex::AxisAttribute:
329 if(hasChildren(preNumber) && kind(preNumber + 1) == QXmlNodeModelIndex::Attribute)
330 return QXmlNodeModelIndex::Iterator::Ptr(new AttributeIterator(this, preNumber));
332 return makeEmptyIterator<QXmlNodeModelIndex>();
334 case QXmlNodeModelIndex::AxisPreceding:
337 return makeEmptyIterator<QXmlNodeModelIndex>();
339 return QXmlNodeModelIndex::Iterator::Ptr(new PrecedingIterator(this, preNumber));
341 case QXmlNodeModelIndex::AxisSelf:
342 return makeSingletonIterator(createIndex(toPreNumber(ni)));
343 case QXmlNodeModelIndex::AxisFollowingSibling:
345 if(preNumber == maximumPreNumber())
346 return makeEmptyIterator<QXmlNodeModelIndex>();
348 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<true>(this, preNumber));
350 case QXmlNodeModelIndex::AxisPrecedingSibling:
353 return makeEmptyIterator<QXmlNodeModelIndex>();
355 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<false>(this, preNumber));
357 case QXmlNodeModelIndex::AxisNamespace:
358 return makeEmptyIterator<QXmlNodeModelIndex>();
362 return QXmlNodeModelIndex::Iterator::Ptr();
365 QXmlNodeModelIndex AccelTree::nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis,
366 const QXmlNodeModelIndex&) const
368 Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
369 return QXmlNodeModelIndex();
372 QVector<QXmlNodeModelIndex> AccelTree::attributes(const QXmlNodeModelIndex &element) const
374 Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
376 return QVector<QXmlNodeModelIndex>();
379 QXmlName AccelTree::name(const QXmlNodeModelIndex &ni) const
381 /* If this node type does not have a name(for instance, it's a comment)
382 * we will return the default constructed value, which is conformant with
383 * this function's contract. */
384 return name(toPreNumber(ni));
387 QVector<QXmlName> AccelTree::namespaceBindings(const QXmlNodeModelIndex &ni) const
389 /* We get a hold of the ancestor, and loop them in reverse document
390 * order(first the parent, then the parent's parent, etc). As soon
391 * we find a binding that hasn't already been added, we add it to the
392 * result list. In that way, declarations appearing further down override
393 * those further up. */
395 const PreNumber preNumber = toPreNumber(ni);
397 const QXmlNodeModelIndex::Iterator::Ptr it(new AncestorIterator<true>(this, preNumber));
398 QVector<QXmlName> result;
399 QXmlNodeModelIndex n(it->next());
401 /* Whether xmlns="" has been encountered. */
402 bool hasUndeclaration = false;
406 const QVector<QXmlName> &forNode = namespaces.value(toPreNumber(n));
407 const int len = forNode.size();
408 bool stopInheritance = false;
410 for(int i = 0; i < len; ++i)
412 const QXmlName &nsb = forNode.at(i);
414 if(nsb.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
416 stopInheritance = true;
420 if(nsb.prefix() == StandardPrefixes::empty &&
421 nsb.namespaceURI() == StandardNamespaces::empty)
423 hasUndeclaration = true;
427 if(!hasPrefix(result, nsb.prefix()))
429 /* We've already encountered an undeclaration, so we're supposed to skip
431 if(hasUndeclaration && nsb.prefix() == StandardPrefixes::empty)
444 result.append(QXmlName(StandardNamespaces::xml, StandardLocalNames::empty, StandardPrefixes::xml));
449 void AccelTree::sendNamespaces(const QXmlNodeModelIndex &n,
450 QAbstractXmlReceiver *const receiver) const
452 Q_ASSERT(n.kind() == QXmlNodeModelIndex::Element);
454 const QXmlNodeModelIndex::Iterator::Ptr it(iterate(n, QXmlNodeModelIndex::AxisAncestorOrSelf));
455 QXmlNodeModelIndex next(it->next());
456 QVector<QXmlName::PrefixCode> alreadySent;
458 while(!next.isNull())
460 const PreNumber preNumber = toPreNumber(next);
462 const QVector<QXmlName> &nss = namespaces.value(preNumber);
464 /* This is by far the most common case. */
471 const int len = nss.count();
472 bool stopInheritance = false;
474 for(int i = 0; i < len; ++i)
476 const QXmlName &name = nss.at(i);
478 if(name.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
480 stopInheritance = true;
484 if(!alreadySent.contains(name.prefix()))
486 alreadySent.append(name.prefix());
487 receiver->namespaceBinding(name);
498 QString AccelTree::stringValue(const QXmlNodeModelIndex &ni) const
500 const PreNumber preNumber = toPreNumber(ni);
502 switch(kind(preNumber))
504 case QXmlNodeModelIndex::Element:
506 /* Concatenate all text nodes that are descendants of this node. */
507 if(!hasChildren(preNumber))
510 const AccelTree::PreNumber stop = preNumber + size(preNumber);
511 AccelTree::PreNumber pn = preNumber + 1; /* Jump over ourselves. */
514 for(; pn <= stop; ++pn)
516 if(kind(pn) == QXmlNodeModelIndex::Text)
519 result += CompressedWhitespace::decompress(data.value(pn));
521 result += data.value(pn);
527 case QXmlNodeModelIndex::Text:
529 if(isCompressed(preNumber))
530 return CompressedWhitespace::decompress(data.value(preNumber));
531 /* Else, fallthrough. It's not compressed so use it as it is. */
533 case QXmlNodeModelIndex::Attribute:
535 case QXmlNodeModelIndex::ProcessingInstruction:
537 case QXmlNodeModelIndex::Comment:
538 return data.value(preNumber);
539 case QXmlNodeModelIndex::Document:
541 /* Concatenate all text nodes in the whole document. */
544 // Perhaps we can QString::reserve() the result based on the size?
545 const AccelTree::PreNumber max = maximumPreNumber();
547 for(AccelTree::PreNumber i = 0; i <= max; ++i)
549 if(kind(i) == QXmlNodeModelIndex::Text)
552 result += CompressedWhitespace::decompress(data.value(i));
554 result += data.value(i);
562 Q_ASSERT_X(false, Q_FUNC_INFO,
563 "A node type that doesn't exist in the XPath Data Model was encountered.");
564 return QString(); /* Dummy, silence compiler warning. */
569 QVariant AccelTree::typedValue(const QXmlNodeModelIndex &n) const
571 return stringValue(n);
574 bool AccelTree::hasPrefix(const QVector<QXmlName> &nbs, const QXmlName::PrefixCode prefix)
576 const int size = nbs.size();
578 for(int i = 0; i < size; ++i)
580 if(nbs.at(i).prefix() == prefix)
587 ItemType::Ptr AccelTree::type(const QXmlNodeModelIndex &ni) const
589 /* kind() is manually inlined here to avoid a virtual call. */
590 return XPathHelper::typeFromKind(basicData.at(toPreNumber(ni)).kind());
593 Item::Iterator::Ptr AccelTree::sequencedTypedValue(const QXmlNodeModelIndex &n) const
595 const PreNumber preNumber = toPreNumber(n);
597 switch(kind(preNumber))
599 case QXmlNodeModelIndex::Element:
601 case QXmlNodeModelIndex::Document:
603 case QXmlNodeModelIndex::Attribute:
604 return makeSingletonIterator(Item(UntypedAtomic::fromValue(stringValue(n))));
606 case QXmlNodeModelIndex::Text:
608 case QXmlNodeModelIndex::ProcessingInstruction:
610 case QXmlNodeModelIndex::Comment:
611 return makeSingletonIterator(Item(AtomicString::fromValue(stringValue(n))));
614 Q_ASSERT_X(false, Q_FUNC_INFO,
615 qPrintable(QString::fromLatin1("A node type that doesn't exist "
616 "in the XPath Data Model was encountered.").arg(kind(preNumber))));
617 return Item::Iterator::Ptr(); /* Dummy, silence compiler warning. */
622 void AccelTree::copyNodeTo(const QXmlNodeModelIndex &node,
623 QAbstractXmlReceiver *const receiver,
624 const NodeCopySettings &settings) const
626 /* This code piece can be seen as a customized version of
627 * QAbstractXmlReceiver::item/sendAsNode(). */
629 Q_ASSERT(!node.isNull());
631 typedef QHash<QXmlName::PrefixCode, QXmlName::NamespaceCode> Binding;
632 QStack<Binding> outputted;
636 case QXmlNodeModelIndex::Element:
638 outputted.push(Binding());
640 /* Add the namespace for our element name. */
641 const QXmlName elementName(node.name());
643 receiver->startElement(elementName);
645 if(!settings.testFlag(InheritNamespaces))
646 receiver->namespaceBinding(QXmlName(StandardNamespaces::StopNamespaceInheritance, 0,
647 StandardPrefixes::StopNamespaceInheritance));
649 if(settings.testFlag(PreserveNamespaces))
650 node.sendNamespaces(receiver);
653 /* Find the namespaces that we actually use and add them to outputted. These are drawn
654 * from the element name, and the node's attributes. */
655 outputted.top().insert(elementName.prefix(), elementName.namespaceURI());
657 const QXmlNodeModelIndex::Iterator::Ptr attributes(iterate(node, QXmlNodeModelIndex::AxisAttribute));
658 QXmlNodeModelIndex attr(attributes->next());
660 while(!attr.isNull())
662 const QXmlName &attrName = attr.name();
663 outputted.top().insert(attrName.prefix(), attrName.namespaceURI());
664 attr = attributes->next();
667 Binding::const_iterator it(outputted.top().constBegin());
668 const Binding::const_iterator end(outputted.top().constEnd());
670 for(; it != end; ++it)
671 receiver->namespaceBinding(QXmlName(it.value(), 0, it.key()));
674 /* Send the attributes of the element. */
676 QXmlNodeModelIndex::Iterator::Ptr attributes(node.iterate(QXmlNodeModelIndex::AxisAttribute));
677 QXmlNodeModelIndex attribute(attributes->next());
679 while(!attribute.isNull())
681 const QString &v = attribute.stringValue();
682 receiver->attribute(attribute.name(), QStringRef(&v));
683 attribute = attributes->next();
687 /* Send the children of the element. */
688 copyChildren(node, receiver, settings);
690 receiver->endElement();
694 case QXmlNodeModelIndex::Document:
696 /* We need to intercept and grab the elements of the document node, such
697 * that we preserve/inherit preference applies to them. */
698 receiver->startDocument();
699 copyChildren(node, receiver, settings);
700 receiver->endDocument();
704 receiver->item(node);
709 QSourceLocation AccelTree::sourceLocation(const QXmlNodeModelIndex &index) const
711 const PreNumber key = toPreNumber(index);
712 if (sourcePositions.contains(key)) {
713 const QPair<qint64, qint64> position = sourcePositions.value(key);
714 return QSourceLocation(m_documentURI, position.first, position.second);
716 return QSourceLocation();
720 void AccelTree::copyChildren(const QXmlNodeModelIndex &node,
721 QAbstractXmlReceiver *const receiver,
722 const NodeCopySettings &settings) const
724 QXmlNodeModelIndex::Iterator::Ptr children(node.iterate(QXmlNodeModelIndex::AxisChild));
725 QXmlNodeModelIndex child(children->next());
727 while(!child.isNull())
729 copyNodeTo(child, receiver, settings);
730 child = children->next();
734 QXmlNodeModelIndex AccelTree::elementById(const QXmlName &id) const
736 const PreNumber pre = m_IDs.value(id.localName(), -1);
738 return QXmlNodeModelIndex();
740 return createIndex(pre);
743 QVector<QXmlNodeModelIndex> AccelTree::nodesByIdref(const QXmlName &) const
745 return QVector<QXmlNodeModelIndex>();