1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <QStack>
43 
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"
51 #include "qdebug_p.h"
52 #include "quntypedatomic_p.h"
53 #include "qxpathhelper_p.h"
54 
55 QT_BEGIN_NAMESPACE
56 
57 using namespace QPatternist;
58 
59 namespace QPatternist {
60 
61     class AccelTreePrivate : public QAbstractXmlNodeModelPrivate
62     {
63         public:
AccelTreePrivate(AccelTree * accelTree)64             AccelTreePrivate(AccelTree *accelTree)
65                 : m_accelTree(accelTree)
66             {
67             }
68 
sourceLocation(const QXmlNodeModelIndex & index) const69             virtual QSourceLocation sourceLocation(const QXmlNodeModelIndex &index) const
70             {
71                 return m_accelTree->sourceLocation(index);
72             }
73 
74         private:
75             AccelTree *m_accelTree;
76     };
77 }
78 
AccelTree(const QUrl & docURI,const QUrl & bURI)79 AccelTree::AccelTree(const QUrl &docURI, const QUrl &bURI)
80      : QAbstractXmlNodeModel(new AccelTreePrivate(this))
81      , m_documentURI(docURI)
82      , m_baseURI(bURI)
83 {
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);
87     data.reserve(30);
88 }
89 
printStats(const NamePool::Ptr & np) const90 void AccelTree::printStats(const NamePool::Ptr &np) const
91 {
92     Q_ASSERT(np);
93 #ifdef QT_NO_DEBUG
94     Q_UNUSED(np); /* Needed when compiling in release mode. */
95 #else
96     const int len = basicData.count();
97 
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)
104     {
105         const BasicNodeData &v = basicData.at(i);
106         pDebug() << '|' << i
107                  << "\t\t|" << v.depth()
108                  << "\t|" << v.size()
109                  << "\t|" << postNumber(i)
110                  << "\t|" << v.kind()
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))
113                                                                                           : data.value(i))
114                  << "\t|";
115         /*
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)
121                  << '|';
122                  */
123     }
124     pDebug() << "+---------------+-------+-------+---------------+-------+--------------+";
125     pDebug() << "Namespaces(" << namespaces.count() << "):";
126 
127     QHashIterator<PreNumber, QVector<QXmlName> > it(namespaces);
128     while(it.hasNext())
129     {
130         it.next();
131 
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());
135     }
136 
137 #endif
138 }
139 
baseUri(const QXmlNodeModelIndex & ni) const140 QUrl AccelTree::baseUri(const QXmlNodeModelIndex &ni) const
141 {
142     switch(kind(toPreNumber(ni)))
143     {
144         case QXmlNodeModelIndex::Document:
145             return baseUri();
146         case QXmlNodeModelIndex::Element:
147         {
148             const QXmlNodeModelIndex::Iterator::Ptr it(iterate(ni, QXmlNodeModelIndex::AxisAttribute));
149             QXmlNodeModelIndex next(it->next());
150 
151             while(!next.isNull())
152             {
153                 if(next.name() == QXmlName(StandardNamespaces::xml, StandardLocalNames::base))
154                 {
155                     const QUrl candidate(next.stringValue());
156                     //  TODO. The xml:base spec says to do URI escaping here.
157 
158                     if(!candidate.isValid())
159                         return QUrl();
160                     else if(candidate.isRelative())
161                     {
162                         const QXmlNodeModelIndex par(parent(ni));
163 
164                         if(par.isNull())
165                             return baseUri().resolved(candidate);
166                         else
167                             return par.baseUri().resolved(candidate);
168                     }
169                     else
170                         return candidate;
171                 }
172 
173                 next = it->next();
174             }
175 
176             /* We have no xml:base-attribute. Can any parent supply us a base URI? */
177             const QXmlNodeModelIndex par(parent(ni));
178 
179             if(par.isNull())
180                 return baseUri();
181             else
182                 return par.baseUri();
183         }
184         case QXmlNodeModelIndex::ProcessingInstruction:
185         /* Fallthrough. */
186         case QXmlNodeModelIndex::Comment:
187         /* Fallthrough. */
188         case QXmlNodeModelIndex::Attribute:
189         /* Fallthrough. */
190         case QXmlNodeModelIndex::Text:
191         {
192             const QXmlNodeModelIndex par(ni.iterate(QXmlNodeModelIndex::AxisParent)->next());
193             if(par.isNull())
194                 return QUrl();
195             else
196                 return par.baseUri();
197         }
198         case QXmlNodeModelIndex::Namespace:
199             return QUrl();
200     }
201 
202     Q_ASSERT_X(false, Q_FUNC_INFO, "This line is never supposed to be reached.");
203     return QUrl();
204 }
205 
documentUri(const QXmlNodeModelIndex & ni) const206 QUrl AccelTree::documentUri(const QXmlNodeModelIndex &ni) const
207 {
208     if(kind(toPreNumber(ni)) == QXmlNodeModelIndex::Document)
209         return documentUri();
210     else
211         return QUrl();
212 }
213 
kind(const QXmlNodeModelIndex & ni) const214 QXmlNodeModelIndex::NodeKind AccelTree::kind(const QXmlNodeModelIndex &ni) const
215 {
216     return kind(toPreNumber(ni));
217 }
218 
compareOrder(const QXmlNodeModelIndex & ni1,const QXmlNodeModelIndex & ni2) const219 QXmlNodeModelIndex::DocumentOrder AccelTree::compareOrder(const QXmlNodeModelIndex &ni1,
220                                                           const QXmlNodeModelIndex &ni2) const
221 {
222     Q_ASSERT_X(ni1.model() == ni2.model(), Q_FUNC_INFO,
223                "The API docs guarantees the two nodes are from the same model");
224 
225     const PreNumber p1 = ni1.data();
226     const PreNumber p2 = ni2.data();
227 
228     if(p1 == p2)
229         return QXmlNodeModelIndex::Is;
230     else if(p1 < p2)
231         return QXmlNodeModelIndex::Precedes;
232     else
233         return QXmlNodeModelIndex::Follows;
234 }
235 
root(const QXmlNodeModelIndex &) const236 QXmlNodeModelIndex AccelTree::root(const QXmlNodeModelIndex &) const
237 {
238     return createIndex(qint64(0));
239 }
240 
parent(const QXmlNodeModelIndex & ni) const241 QXmlNodeModelIndex AccelTree::parent(const QXmlNodeModelIndex &ni) const
242 {
243     const AccelTree::PreNumber p = basicData.at(toPreNumber(ni)).parent();
244 
245     if(p == -1)
246         return QXmlNodeModelIndex();
247     else
248         return createIndex(p);
249 }
250 
iterate(const QXmlNodeModelIndex & ni,QXmlNodeModelIndex::Axis axis) const251 QXmlNodeModelIndex::Iterator::Ptr AccelTree::iterate(const QXmlNodeModelIndex &ni,
252                                                      QXmlNodeModelIndex::Axis axis) const
253 {
254     const PreNumber preNumber = toPreNumber(ni);
255 
256     switch(axis)
257     {
258         case QXmlNodeModelIndex::AxisChildOrTop:
259         {
260             if(!hasParent(preNumber))
261             {
262                 switch(kind(preNumber))
263                 {
264                     case QXmlNodeModelIndex::Comment:
265                     /* Fallthrough. */
266                     case QXmlNodeModelIndex::ProcessingInstruction:
267                     /* Fallthrough. */
268                     case QXmlNodeModelIndex::Element:
269                     /* Fallthrough. */
270                     case QXmlNodeModelIndex::Text:
271                         return makeSingletonIterator(ni);
272                     case QXmlNodeModelIndex::Attribute:
273                     /* Fallthrough. */
274                     case QXmlNodeModelIndex::Document:
275                     /* Fallthrough. */
276                     case QXmlNodeModelIndex::Namespace:
277                         /* Do nothing. */;
278                 }
279             }
280             /* Else, fallthrough to AxisChild. */
281         }
282         case QXmlNodeModelIndex::AxisChild:
283         {
284             if(hasChildren(preNumber))
285                 return QXmlNodeModelIndex::Iterator::Ptr(new ChildIterator(this, preNumber));
286             else
287                 return makeEmptyIterator<QXmlNodeModelIndex>();
288         }
289         case QXmlNodeModelIndex::AxisAncestor:
290         {
291             if(hasParent(preNumber))
292                 return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<false>(this, preNumber));
293             else
294                 return makeEmptyIterator<QXmlNodeModelIndex>();
295         }
296         case QXmlNodeModelIndex::AxisAncestorOrSelf:
297             return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<true>(this, preNumber));
298         case QXmlNodeModelIndex::AxisParent:
299         {
300             if(hasParent(preNumber))
301                 return makeSingletonIterator(createIndex(parent(preNumber)));
302             else
303                 return makeEmptyIterator<QXmlNodeModelIndex>();
304         }
305         case QXmlNodeModelIndex::AxisDescendant:
306         {
307             if(hasChildren(preNumber))
308                 return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<false>(this, preNumber));
309             else
310                 return makeEmptyIterator<QXmlNodeModelIndex>();
311         }
312         case QXmlNodeModelIndex::AxisDescendantOrSelf:
313             return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<true>(this, preNumber));
314         case QXmlNodeModelIndex::AxisFollowing:
315         {
316             if(preNumber == maximumPreNumber())
317                 return makeEmptyIterator<QXmlNodeModelIndex>();
318             else
319                 return QXmlNodeModelIndex::Iterator::Ptr(new FollowingIterator(this, preNumber));
320         }
321         case QXmlNodeModelIndex::AxisAttributeOrTop:
322         {
323             if(!hasParent(preNumber) && kind(preNumber) == QXmlNodeModelIndex::Attribute)
324                 return makeSingletonIterator(ni);
325             /* Else, falthrough to AxisAttribute. */
326         }
327         case QXmlNodeModelIndex::AxisAttribute:
328         {
329             if(hasChildren(preNumber) && kind(preNumber + 1) == QXmlNodeModelIndex::Attribute)
330                 return QXmlNodeModelIndex::Iterator::Ptr(new AttributeIterator(this, preNumber));
331             else
332                 return makeEmptyIterator<QXmlNodeModelIndex>();
333         }
334         case QXmlNodeModelIndex::AxisPreceding:
335         {
336             if(preNumber == 0)
337                 return makeEmptyIterator<QXmlNodeModelIndex>();
338             else
339                 return QXmlNodeModelIndex::Iterator::Ptr(new PrecedingIterator(this, preNumber));
340         }
341         case QXmlNodeModelIndex::AxisSelf:
342             return makeSingletonIterator(createIndex(toPreNumber(ni)));
343         case QXmlNodeModelIndex::AxisFollowingSibling:
344         {
345             if(preNumber == maximumPreNumber())
346                 return makeEmptyIterator<QXmlNodeModelIndex>();
347             else
348                 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<true>(this, preNumber));
349         }
350         case QXmlNodeModelIndex::AxisPrecedingSibling:
351         {
352             if(preNumber == 0)
353                 return makeEmptyIterator<QXmlNodeModelIndex>();
354             else
355                 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<false>(this, preNumber));
356         }
357         case QXmlNodeModelIndex::AxisNamespace:
358             return makeEmptyIterator<QXmlNodeModelIndex>();
359     }
360 
361     Q_ASSERT(false);
362     return QXmlNodeModelIndex::Iterator::Ptr();
363 }
364 
nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis,const QXmlNodeModelIndex &) const365 QXmlNodeModelIndex AccelTree::nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis,
366                                                  const QXmlNodeModelIndex&) const
367 {
368     Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
369     return QXmlNodeModelIndex();
370 }
371 
attributes(const QXmlNodeModelIndex & element) const372 QVector<QXmlNodeModelIndex> AccelTree::attributes(const QXmlNodeModelIndex &element) const
373 {
374     Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
375     Q_UNUSED(element);
376     return QVector<QXmlNodeModelIndex>();
377 }
378 
name(const QXmlNodeModelIndex & ni) const379 QXmlName AccelTree::name(const QXmlNodeModelIndex &ni) const
380 {
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));
385 }
386 
namespaceBindings(const QXmlNodeModelIndex & ni) const387 QVector<QXmlName> AccelTree::namespaceBindings(const QXmlNodeModelIndex &ni) const
388 {
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. */
394 
395     const PreNumber preNumber = toPreNumber(ni);
396 
397     const QXmlNodeModelIndex::Iterator::Ptr it(new AncestorIterator<true>(this, preNumber));
398     QVector<QXmlName> result;
399     QXmlNodeModelIndex n(it->next());
400 
401     /* Whether xmlns="" has been encountered. */
402     bool hasUndeclaration = false;
403 
404     while(!n.isNull())
405     {
406         const QVector<QXmlName> &forNode = namespaces.value(toPreNumber(n));
407         const int len = forNode.size();
408         bool stopInheritance = false;
409 
410         for(int i = 0; i < len; ++i)
411         {
412             const QXmlName &nsb = forNode.at(i);
413 
414             if(nsb.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
415             {
416                 stopInheritance = true;
417                 continue;
418             }
419 
420             if(nsb.prefix() == StandardPrefixes::empty &&
421                nsb.namespaceURI() == StandardNamespaces::empty)
422             {
423                 hasUndeclaration = true;
424                 continue;
425             }
426 
427             if(!hasPrefix(result, nsb.prefix()))
428             {
429                 /* We've already encountered an undeclaration, so we're supposed to skip
430                  * them. */
431                 if(hasUndeclaration && nsb.prefix() == StandardPrefixes::empty)
432                     continue;
433                 else
434                     result.append(nsb);
435             }
436         }
437 
438         if(stopInheritance)
439             break;
440         else
441             n = it->next();
442     }
443 
444     result.append(QXmlName(StandardNamespaces::xml, StandardLocalNames::empty, StandardPrefixes::xml));
445 
446     return result;
447 }
448 
sendNamespaces(const QXmlNodeModelIndex & n,QAbstractXmlReceiver * const receiver) const449 void AccelTree::sendNamespaces(const QXmlNodeModelIndex &n,
450                                QAbstractXmlReceiver *const receiver) const
451 {
452     Q_ASSERT(n.kind() == QXmlNodeModelIndex::Element);
453 
454     const QXmlNodeModelIndex::Iterator::Ptr it(iterate(n, QXmlNodeModelIndex::AxisAncestorOrSelf));
455     QXmlNodeModelIndex next(it->next());
456     QVector<QXmlName::PrefixCode> alreadySent;
457 
458     while(!next.isNull())
459     {
460         const PreNumber preNumber = toPreNumber(next);
461 
462         const QVector<QXmlName> &nss = namespaces.value(preNumber);
463 
464         /* This is by far the most common case. */
465         if(nss.isEmpty())
466         {
467             next = it->next();
468             continue;
469         }
470 
471         const int len = nss.count();
472         bool stopInheritance = false;
473 
474         for(int i = 0; i < len; ++i)
475         {
476             const QXmlName &name = nss.at(i);
477 
478             if(name.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
479             {
480                 stopInheritance = true;
481                 continue;
482             }
483 
484             if(!alreadySent.contains(name.prefix()))
485             {
486                 alreadySent.append(name.prefix());
487                 receiver->namespaceBinding(name);
488             }
489         }
490 
491         if(stopInheritance)
492             break;
493         else
494             next = it->next();
495     }
496 }
497 
stringValue(const QXmlNodeModelIndex & ni) const498 QString AccelTree::stringValue(const QXmlNodeModelIndex &ni) const
499 {
500     const PreNumber preNumber = toPreNumber(ni);
501 
502     switch(kind(preNumber))
503     {
504         case QXmlNodeModelIndex::Element:
505         {
506             /* Concatenate all text nodes that are descendants of this node. */
507             if(!hasChildren(preNumber))
508                 return QString();
509 
510             const AccelTree::PreNumber stop = preNumber + size(preNumber);
511             AccelTree::PreNumber pn = preNumber + 1; /* Jump over ourselves. */
512             QString result;
513 
514             for(; pn <= stop; ++pn)
515             {
516                 if(kind(pn) == QXmlNodeModelIndex::Text)
517                 {
518                     if(isCompressed(pn))
519                         result += CompressedWhitespace::decompress(data.value(pn));
520                     else
521                         result += data.value(pn);
522                 }
523             }
524 
525             return result;
526         }
527         case QXmlNodeModelIndex::Text:
528         {
529             if(isCompressed(preNumber))
530                 return CompressedWhitespace::decompress(data.value(preNumber));
531             /* Else, fallthrough. It's not compressed so use it as it is. */
532         }
533         case QXmlNodeModelIndex::Attribute:
534         /* Fallthrough */
535         case QXmlNodeModelIndex::ProcessingInstruction:
536         /* Fallthrough */
537         case QXmlNodeModelIndex::Comment:
538             return data.value(preNumber);
539         case QXmlNodeModelIndex::Document:
540         {
541             /* Concatenate all text nodes in the whole document. */
542 
543             QString result;
544             // Perhaps we can QString::reserve() the result based on the size?
545             const AccelTree::PreNumber max = maximumPreNumber();
546 
547             for(AccelTree::PreNumber i = 0; i <= max; ++i)
548             {
549                 if(kind(i) == QXmlNodeModelIndex::Text)
550                 {
551                     if(isCompressed(i))
552                         result += CompressedWhitespace::decompress(data.value(i));
553                     else
554                         result += data.value(i);
555                 }
556             }
557 
558             return result;
559         }
560         default:
561         {
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. */
565         }
566     }
567 }
568 
typedValue(const QXmlNodeModelIndex & n) const569 QVariant AccelTree::typedValue(const QXmlNodeModelIndex &n) const
570 {
571     return stringValue(n);
572 }
573 
hasPrefix(const QVector<QXmlName> & nbs,const QXmlName::PrefixCode prefix)574 bool AccelTree::hasPrefix(const QVector<QXmlName> &nbs, const QXmlName::PrefixCode prefix)
575 {
576     const int size = nbs.size();
577 
578     for(int i = 0; i < size; ++i)
579     {
580         if(nbs.at(i).prefix() == prefix)
581             return true;
582     }
583 
584     return false;
585 }
586 
type(const QXmlNodeModelIndex & ni) const587 ItemType::Ptr AccelTree::type(const QXmlNodeModelIndex &ni) const
588 {
589     /* kind() is manually inlined here to avoid a virtual call. */
590     return XPathHelper::typeFromKind(basicData.at(toPreNumber(ni)).kind());
591 }
592 
sequencedTypedValue(const QXmlNodeModelIndex & n) const593 Item::Iterator::Ptr AccelTree::sequencedTypedValue(const QXmlNodeModelIndex &n) const
594 {
595     const PreNumber preNumber = toPreNumber(n);
596 
597     switch(kind(preNumber))
598     {
599         case QXmlNodeModelIndex::Element:
600         /* Fallthrough. */
601         case QXmlNodeModelIndex::Document:
602         /* Fallthrough. */
603         case QXmlNodeModelIndex::Attribute:
604             return makeSingletonIterator(Item(UntypedAtomic::fromValue(stringValue(n))));
605 
606         case QXmlNodeModelIndex::Text:
607         /* Fallthrough. */
608         case QXmlNodeModelIndex::ProcessingInstruction:
609         /* Fallthrough. */
610         case QXmlNodeModelIndex::Comment:
611             return makeSingletonIterator(Item(AtomicString::fromValue(stringValue(n))));
612         default:
613         {
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. */
618         }
619     }
620 }
621 
copyNodeTo(const QXmlNodeModelIndex & node,QAbstractXmlReceiver * const receiver,const NodeCopySettings & settings) const622 void AccelTree::copyNodeTo(const QXmlNodeModelIndex &node,
623                            QAbstractXmlReceiver *const receiver,
624                            const NodeCopySettings &settings) const
625 {
626     /* This code piece can be seen as a customized version of
627      * QAbstractXmlReceiver::item/sendAsNode(). */
628     Q_ASSERT(receiver);
629     Q_ASSERT(!node.isNull());
630 
631     typedef QHash<QXmlName::PrefixCode, QXmlName::NamespaceCode> Binding;
632     QStack<Binding> outputted;
633 
634     switch(node.kind())
635     {
636         case QXmlNodeModelIndex::Element:
637         {
638             outputted.push(Binding());
639 
640             /* Add the namespace for our element name. */
641             const QXmlName elementName(node.name());
642 
643             receiver->startElement(elementName);
644 
645             if(!settings.testFlag(InheritNamespaces))
646                 receiver->namespaceBinding(QXmlName(StandardNamespaces::StopNamespaceInheritance, 0,
647                                                     StandardPrefixes::StopNamespaceInheritance));
648 
649             if(settings.testFlag(PreserveNamespaces))
650                 node.sendNamespaces(receiver);
651             else
652             {
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());
656 
657                 const QXmlNodeModelIndex::Iterator::Ptr attributes(iterate(node, QXmlNodeModelIndex::AxisAttribute));
658                 QXmlNodeModelIndex attr(attributes->next());
659 
660                 while(!attr.isNull())
661                 {
662                     const QXmlName &attrName = attr.name();
663                     outputted.top().insert(attrName.prefix(), attrName.namespaceURI());
664                     attr = attributes->next();
665                 }
666 
667                 Binding::const_iterator it(outputted.top().constBegin());
668                 const Binding::const_iterator end(outputted.top().constEnd());
669 
670                 for(; it != end; ++it)
671                     receiver->namespaceBinding(QXmlName(it.value(), 0, it.key()));
672             }
673 
674             /* Send the attributes of the element. */
675             {
676                 QXmlNodeModelIndex::Iterator::Ptr attributes(node.iterate(QXmlNodeModelIndex::AxisAttribute));
677                 QXmlNodeModelIndex attribute(attributes->next());
678 
679                 while(!attribute.isNull())
680                 {
681                     const QString &v = attribute.stringValue();
682                     receiver->attribute(attribute.name(), QStringRef(&v));
683                     attribute = attributes->next();
684                 }
685             }
686 
687             /* Send the children of the element. */
688             copyChildren(node, receiver, settings);
689 
690             receiver->endElement();
691             outputted.pop();
692             break;
693         }
694         case QXmlNodeModelIndex::Document:
695         {
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();
701             break;
702         }
703         default:
704             receiver->item(node);
705     }
706 
707 }
708 
sourceLocation(const QXmlNodeModelIndex & index) const709 QSourceLocation AccelTree::sourceLocation(const QXmlNodeModelIndex &index) const
710 {
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);
715     } else {
716         return QSourceLocation();
717     }
718 }
719 
copyChildren(const QXmlNodeModelIndex & node,QAbstractXmlReceiver * const receiver,const NodeCopySettings & settings) const720 void AccelTree::copyChildren(const QXmlNodeModelIndex &node,
721                              QAbstractXmlReceiver *const receiver,
722                              const NodeCopySettings &settings) const
723 {
724     QXmlNodeModelIndex::Iterator::Ptr children(node.iterate(QXmlNodeModelIndex::AxisChild));
725     QXmlNodeModelIndex child(children->next());
726 
727     while(!child.isNull())
728     {
729         copyNodeTo(child, receiver, settings);
730         child = children->next();
731     }
732 }
733 
elementById(const QXmlName & id) const734 QXmlNodeModelIndex AccelTree::elementById(const QXmlName &id) const
735 {
736     const PreNumber pre = m_IDs.value(id.localName(), -1);
737     if(pre == -1)
738         return QXmlNodeModelIndex();
739     else
740         return createIndex(pre);
741 }
742 
nodesByIdref(const QXmlName &) const743 QVector<QXmlNodeModelIndex> AccelTree::nodesByIdref(const QXmlName &) const
744 {
745     return QVector<QXmlNodeModelIndex>();
746 }
747 
748 QT_END_NAMESPACE
749 
750