1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QStack>
41
42#include "qabstractxmlreceiver.h"
43#include "qabstractxmlnodemodel_p.h"
44#include "qacceliterators_p.h"
45#include "qacceltree_p.h"
46#include "qatomicstring_p.h"
47#include "qcommonvalues_p.h"
48#include "qcompressedwhitespace_p.h"
49#include "qxmldebug_p.h"
50#include "quntypedatomic_p.h"
51#include "qxpathhelper_p.h"
52
53QT_BEGIN_NAMESPACE
54
55using namespace QPatternist;
56
57namespace QPatternist {
58
59 class AccelTreePrivate : public QAbstractXmlNodeModelPrivate
60 {
61 public:
62 AccelTreePrivate(AccelTree *accelTree)
63 : m_accelTree(accelTree)
64 {
65 }
66
67 virtual QSourceLocation sourceLocation(const QXmlNodeModelIndex &index) const
68 {
69 return m_accelTree->sourceLocation(index);
70 }
71
72 private:
73 AccelTree *m_accelTree;
74 };
75}
76
77AccelTree::AccelTree(const QUrl &docURI, const QUrl &bURI)
78 : QAbstractXmlNodeModel(new AccelTreePrivate(this))
79 , m_documentURI(docURI)
80 , m_baseURI(bURI)
81{
82 /* Pre-allocate at least a little bit. */
83 // TODO. Do it according to what an average 4 KB doc contains.
84 basicData.reserve(asize: 100);
85 data.reserve(asize: 30);
86}
87
88void AccelTree::printStats(const NamePool::Ptr &np) const
89{
90 Q_ASSERT(np);
91#ifdef QT_NO_DEBUG
92 Q_UNUSED(np); /* Needed when compiling in release mode. */
93#else
94 const int len = basicData.count();
95
96 pDebug() << "AccelTree stats for" << (m_documentURI.isEmpty() ? QString::fromLatin1(str: "<empty URI>") : m_documentURI.toString());
97 pDebug() << "Maximum pre number:" << maximumPreNumber();
98 pDebug() << "+---------------+-------+-------+---------------+-------+--------------+-------+";
99 pDebug() << "| Pre number | Depth | Size | Post Number | Kind | Name | Value |";
100 pDebug() << "+---------------+-------+-------+---------------+-------+--------------+-------+";
101 for(int i = 0; i < len; ++i)
102 {
103 const BasicNodeData &v = basicData.at(i);
104 pDebug() << '|' << i
105 << "\t\t|" << v.depth()
106 << "\t|" << v.size()
107 << "\t|" << postNumber(pre: i)
108 << "\t|" << v.kind()
109 << "\t\t|" << (v.name().isNull() ? QString::fromLatin1(str: "(none)") : np->displayName(qName: v.name()))
110 << "\t\t|" << ((v.kind() == QXmlNodeModelIndex::Text && isCompressed(pre: i)) ? CompressedWhitespace::decompress(input: data.value(akey: i))
111 : data.value(akey: i))
112 << "\t|";
113 /*
114 pDebug() << '|' << QString().arg(i, 14)
115 << '|' << QString().arg(v.depth(), 6)
116 << '|' << QString().arg(v.size(), 6)
117 << '|' << QString().arg(postNumber(i), 14)
118 << '|' << QString().arg(v.kind(), 6)
119 << '|';
120 */
121 }
122 pDebug() << "+---------------+-------+-------+---------------+-------+--------------+";
123 pDebug() << "Namespaces(" << namespaces.count() << "):";
124
125 for (auto it = namespaces.cbegin(), end = namespaces.cend(); it != end; ++it) {
126 pDebug() << "PreNumber: " << QString::number(it.key());
127 for(int i = 0; i < it.value().count(); ++i)
128 pDebug() << "\t\t" << np->stringForPrefix(code: it.value().at(i).prefix()) << " = " << np->stringForNamespace(code: it.value().at(i).namespaceURI());
129 }
130
131#endif
132}
133
134QUrl AccelTree::baseUri(const QXmlNodeModelIndex &ni) const
135{
136 switch(kind(pre: toPreNumber(n: ni)))
137 {
138 case QXmlNodeModelIndex::Document:
139 return baseUri();
140 case QXmlNodeModelIndex::Element:
141 {
142 const QXmlNodeModelIndex::Iterator::Ptr it(iterate(ni, axis: QXmlNodeModelIndex::AxisAttribute));
143 QXmlNodeModelIndex next(it->next());
144
145 while(!next.isNull())
146 {
147 if(next.name() == QXmlName(StandardNamespaces::xml, StandardLocalNames::base))
148 {
149 const QUrl candidate(next.stringValue());
150 // TODO. The xml:base spec says to do URI escaping here.
151
152 if(!candidate.isValid())
153 return QUrl();
154 else if(candidate.isRelative())
155 {
156 const QXmlNodeModelIndex par(parent(ni));
157
158 if(par.isNull())
159 return baseUri().resolved(relative: candidate);
160 else
161 return par.baseUri().resolved(relative: candidate);
162 }
163 else
164 return candidate;
165 }
166
167 next = it->next();
168 }
169
170 /* We have no xml:base-attribute. Can any parent supply us a base URI? */
171 const QXmlNodeModelIndex par(parent(ni));
172
173 if(par.isNull())
174 return baseUri();
175 else
176 return par.baseUri();
177 }
178 case QXmlNodeModelIndex::ProcessingInstruction:
179 case QXmlNodeModelIndex::Comment:
180 case QXmlNodeModelIndex::Attribute:
181 case QXmlNodeModelIndex::Text:
182 {
183 const QXmlNodeModelIndex par(ni.iterate(axis: QXmlNodeModelIndex::AxisParent)->next());
184 if(par.isNull())
185 return QUrl();
186 else
187 return par.baseUri();
188 }
189 case QXmlNodeModelIndex::Namespace:
190 return QUrl();
191 }
192
193 Q_ASSERT_X(false, Q_FUNC_INFO, "This line is never supposed to be reached.");
194 return QUrl();
195}
196
197QUrl AccelTree::documentUri(const QXmlNodeModelIndex &ni) const
198{
199 if(kind(pre: toPreNumber(n: ni)) == QXmlNodeModelIndex::Document)
200 return documentUri();
201 else
202 return QUrl();
203}
204
205QXmlNodeModelIndex::NodeKind AccelTree::kind(const QXmlNodeModelIndex &ni) const
206{
207 return kind(pre: toPreNumber(n: ni));
208}
209
210QXmlNodeModelIndex::DocumentOrder AccelTree::compareOrder(const QXmlNodeModelIndex &ni1,
211 const QXmlNodeModelIndex &ni2) const
212{
213 Q_ASSERT_X(ni1.model() == ni2.model(), Q_FUNC_INFO,
214 "The API docs guarantees the two nodes are from the same model");
215
216 const PreNumber p1 = ni1.data();
217 const PreNumber p2 = ni2.data();
218
219 if(p1 == p2)
220 return QXmlNodeModelIndex::Is;
221 else if(p1 < p2)
222 return QXmlNodeModelIndex::Precedes;
223 else
224 return QXmlNodeModelIndex::Follows;
225}
226
227QXmlNodeModelIndex AccelTree::root(const QXmlNodeModelIndex &) const
228{
229 return createIndex(data: qint64(0));
230}
231
232QXmlNodeModelIndex AccelTree::parent(const QXmlNodeModelIndex &ni) const
233{
234 const AccelTree::PreNumber p = basicData.at(i: toPreNumber(n: ni)).parent();
235
236 if(p == -1)
237 return QXmlNodeModelIndex();
238 else
239 return createIndex(data: p);
240}
241
242QXmlNodeModelIndex::Iterator::Ptr AccelTree::iterate(const QXmlNodeModelIndex &ni,
243 QXmlNodeModelIndex::Axis axis) const
244{
245 const PreNumber preNumber = toPreNumber(n: ni);
246
247 switch(axis)
248 {
249 case QXmlNodeModelIndex::AxisChildOrTop:
250 {
251 if(!hasParent(pre: preNumber))
252 {
253 switch(kind(pre: preNumber))
254 {
255 case QXmlNodeModelIndex::Comment:
256 case QXmlNodeModelIndex::ProcessingInstruction:
257 case QXmlNodeModelIndex::Element:
258 case QXmlNodeModelIndex::Text:
259 return makeSingletonIterator(item: ni);
260 case QXmlNodeModelIndex::Attribute:
261 case QXmlNodeModelIndex::Document:
262 case QXmlNodeModelIndex::Namespace:
263 /* Do nothing. */;
264 }
265 }
266 Q_FALLTHROUGH();
267 }
268 case QXmlNodeModelIndex::AxisChild:
269 {
270 if(hasChildren(pre: preNumber))
271 return QXmlNodeModelIndex::Iterator::Ptr(new ChildIterator(this, preNumber));
272 else
273 return makeEmptyIterator<QXmlNodeModelIndex>();
274 }
275 case QXmlNodeModelIndex::AxisAncestor:
276 {
277 if(hasParent(pre: preNumber))
278 return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<false>(this, preNumber));
279 else
280 return makeEmptyIterator<QXmlNodeModelIndex>();
281 }
282 case QXmlNodeModelIndex::AxisAncestorOrSelf:
283 return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<true>(this, preNumber));
284 case QXmlNodeModelIndex::AxisParent:
285 {
286 if(hasParent(pre: preNumber))
287 return makeSingletonIterator(item: createIndex(data: parent(pre: preNumber)));
288 else
289 return makeEmptyIterator<QXmlNodeModelIndex>();
290 }
291 case QXmlNodeModelIndex::AxisDescendant:
292 {
293 if(hasChildren(pre: preNumber))
294 return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<false>(this, preNumber));
295 else
296 return makeEmptyIterator<QXmlNodeModelIndex>();
297 }
298 case QXmlNodeModelIndex::AxisDescendantOrSelf:
299 return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<true>(this, preNumber));
300 case QXmlNodeModelIndex::AxisFollowing:
301 {
302 if(preNumber == maximumPreNumber())
303 return makeEmptyIterator<QXmlNodeModelIndex>();
304 else
305 return QXmlNodeModelIndex::Iterator::Ptr(new FollowingIterator(this, preNumber));
306 }
307 case QXmlNodeModelIndex::AxisAttributeOrTop:
308 {
309 if(!hasParent(pre: preNumber) && kind(pre: preNumber) == QXmlNodeModelIndex::Attribute)
310 return makeSingletonIterator(item: ni);
311 Q_FALLTHROUGH();
312 }
313 case QXmlNodeModelIndex::AxisAttribute:
314 {
315 if(hasChildren(pre: preNumber) && kind(pre: preNumber + 1) == QXmlNodeModelIndex::Attribute)
316 return QXmlNodeModelIndex::Iterator::Ptr(new AttributeIterator(this, preNumber));
317 else
318 return makeEmptyIterator<QXmlNodeModelIndex>();
319 }
320 case QXmlNodeModelIndex::AxisPreceding:
321 {
322 if(preNumber == 0)
323 return makeEmptyIterator<QXmlNodeModelIndex>();
324 else
325 return QXmlNodeModelIndex::Iterator::Ptr(new PrecedingIterator(this, preNumber));
326 }
327 case QXmlNodeModelIndex::AxisSelf:
328 return makeSingletonIterator(item: createIndex(data: toPreNumber(n: ni)));
329 case QXmlNodeModelIndex::AxisFollowingSibling:
330 {
331 if(preNumber == maximumPreNumber())
332 return makeEmptyIterator<QXmlNodeModelIndex>();
333 else
334 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<true>(this, preNumber));
335 }
336 case QXmlNodeModelIndex::AxisPrecedingSibling:
337 {
338 if(preNumber == 0)
339 return makeEmptyIterator<QXmlNodeModelIndex>();
340 else
341 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<false>(this, preNumber));
342 }
343 case QXmlNodeModelIndex::AxisNamespace:
344 return makeEmptyIterator<QXmlNodeModelIndex>();
345 }
346
347 Q_ASSERT(false);
348 return QXmlNodeModelIndex::Iterator::Ptr();
349}
350
351QXmlNodeModelIndex AccelTree::nextFromSimpleAxis(QAbstractXmlNodeModel::SimpleAxis,
352 const QXmlNodeModelIndex&) const
353{
354 Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
355 return QXmlNodeModelIndex();
356}
357
358QVector<QXmlNodeModelIndex> AccelTree::attributes(const QXmlNodeModelIndex &element) const
359{
360 Q_ASSERT_X(false, Q_FUNC_INFO, "This function is not supposed to be called.");
361 Q_UNUSED(element);
362 return QVector<QXmlNodeModelIndex>();
363}
364
365QXmlName AccelTree::name(const QXmlNodeModelIndex &ni) const
366{
367 /* If this node type does not have a name(for instance, it's a comment)
368 * we will return the default constructed value, which is conformant with
369 * this function's contract. */
370 return name(pre: toPreNumber(n: ni));
371}
372
373QVector<QXmlName> AccelTree::namespaceBindings(const QXmlNodeModelIndex &ni) const
374{
375 /* We get a hold of the ancestor, and loop them in reverse document
376 * order(first the parent, then the parent's parent, etc). As soon
377 * we find a binding that hasn't already been added, we add it to the
378 * result list. In that way, declarations appearing further down override
379 * those further up. */
380
381 const PreNumber preNumber = toPreNumber(n: ni);
382
383 const QXmlNodeModelIndex::Iterator::Ptr it(new AncestorIterator<true>(this, preNumber));
384 QVector<QXmlName> result;
385 QXmlNodeModelIndex n(it->next());
386
387 /* Whether xmlns="" has been encountered. */
388 bool hasUndeclaration = false;
389
390 while(!n.isNull())
391 {
392 const QVector<QXmlName> &forNode = namespaces.value(akey: toPreNumber(n));
393 const int len = forNode.size();
394 bool stopInheritance = false;
395
396 for(int i = 0; i < len; ++i)
397 {
398 const QXmlName &nsb = forNode.at(i);
399
400 if(nsb.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
401 {
402 stopInheritance = true;
403 continue;
404 }
405
406 if(nsb.prefix() == StandardPrefixes::empty &&
407 nsb.namespaceURI() == StandardNamespaces::empty)
408 {
409 hasUndeclaration = true;
410 continue;
411 }
412
413 if(!hasPrefix(nbs: result, prefix: nsb.prefix()))
414 {
415 /* We've already encountered an undeclaration, so we're supposed to skip
416 * them. */
417 if(hasUndeclaration && nsb.prefix() == StandardPrefixes::empty)
418 continue;
419 else
420 result.append(t: nsb);
421 }
422 }
423
424 if(stopInheritance)
425 break;
426 else
427 n = it->next();
428 }
429
430 result.append(t: QXmlName(StandardNamespaces::xml, StandardLocalNames::empty, StandardPrefixes::xml));
431
432 return result;
433}
434
435void AccelTree::sendNamespaces(const QXmlNodeModelIndex &n,
436 QAbstractXmlReceiver *const receiver) const
437{
438 Q_ASSERT(n.kind() == QXmlNodeModelIndex::Element);
439
440 const QXmlNodeModelIndex::Iterator::Ptr it(iterate(ni: n, axis: QXmlNodeModelIndex::AxisAncestorOrSelf));
441 QXmlNodeModelIndex next(it->next());
442 QVector<QXmlName::PrefixCode> alreadySent;
443
444 while(!next.isNull())
445 {
446 const PreNumber preNumber = toPreNumber(n: next);
447
448 const QVector<QXmlName> &nss = namespaces.value(akey: preNumber);
449
450 /* This is by far the most common case. */
451 if(nss.isEmpty())
452 {
453 next = it->next();
454 continue;
455 }
456
457 const int len = nss.count();
458 bool stopInheritance = false;
459
460 for(int i = 0; i < len; ++i)
461 {
462 const QXmlName &name = nss.at(i);
463
464 if(name.namespaceURI() == StandardNamespaces::StopNamespaceInheritance)
465 {
466 stopInheritance = true;
467 continue;
468 }
469
470 if(!alreadySent.contains(t: name.prefix()))
471 {
472 alreadySent.append(t: name.prefix());
473 receiver->namespaceBinding(name);
474 }
475 }
476
477 if(stopInheritance)
478 break;
479 else
480 next = it->next();
481 }
482}
483
484QString AccelTree::stringValue(const QXmlNodeModelIndex &ni) const
485{
486 const PreNumber preNumber = toPreNumber(n: ni);
487
488 switch(kind(pre: preNumber))
489 {
490 case QXmlNodeModelIndex::Element:
491 {
492 /* Concatenate all text nodes that are descendants of this node. */
493 if(!hasChildren(pre: preNumber))
494 return QString();
495
496 const AccelTree::PreNumber stop = preNumber + size(pre: preNumber);
497 AccelTree::PreNumber pn = preNumber + 1; /* Jump over ourselves. */
498 QString result;
499
500 for(; pn <= stop; ++pn)
501 {
502 if(kind(pre: pn) == QXmlNodeModelIndex::Text)
503 {
504 if(isCompressed(pre: pn))
505 result += CompressedWhitespace::decompress(input: data.value(akey: pn));
506 else
507 result += data.value(akey: pn);
508 }
509 }
510
511 return result;
512 }
513 case QXmlNodeModelIndex::Text:
514 {
515 if(isCompressed(pre: preNumber))
516 return CompressedWhitespace::decompress(input: data.value(akey: preNumber));
517 /* It's not compressed so use it as it is. */
518 Q_FALLTHROUGH();
519 }
520 case QXmlNodeModelIndex::Attribute:
521 case QXmlNodeModelIndex::ProcessingInstruction:
522 case QXmlNodeModelIndex::Comment:
523 return data.value(akey: preNumber);
524 case QXmlNodeModelIndex::Document:
525 {
526 /* Concatenate all text nodes in the whole document. */
527
528 QString result;
529 // Perhaps we can QString::reserve() the result based on the size?
530 const AccelTree::PreNumber max = maximumPreNumber();
531
532 for(AccelTree::PreNumber i = 0; i <= max; ++i)
533 {
534 if(kind(pre: i) == QXmlNodeModelIndex::Text)
535 {
536 if(isCompressed(pre: i))
537 result += CompressedWhitespace::decompress(input: data.value(akey: i));
538 else
539 result += data.value(akey: i);
540 }
541 }
542
543 return result;
544 }
545 default:
546 {
547 Q_ASSERT_X(false, Q_FUNC_INFO,
548 "A node type that doesn't exist in the XPath Data Model was encountered.");
549 return QString(); /* Dummy, silence compiler warning. */
550 }
551 }
552}
553
554QVariant AccelTree::typedValue(const QXmlNodeModelIndex &n) const
555{
556 return stringValue(ni: n);
557}
558
559bool AccelTree::hasPrefix(const QVector<QXmlName> &nbs, const QXmlName::PrefixCode prefix)
560{
561 const int size = nbs.size();
562
563 for(int i = 0; i < size; ++i)
564 {
565 if(nbs.at(i).prefix() == prefix)
566 return true;
567 }
568
569 return false;
570}
571
572ItemType::Ptr AccelTree::type(const QXmlNodeModelIndex &ni) const
573{
574 /* kind() is manually inlined here to avoid a virtual call. */
575 return XPathHelper::typeFromKind(nodeKind: basicData.at(i: toPreNumber(n: ni)).kind());
576}
577
578Item::Iterator::Ptr AccelTree::sequencedTypedValue(const QXmlNodeModelIndex &n) const
579{
580 const PreNumber preNumber = toPreNumber(n);
581
582 switch(kind(pre: preNumber))
583 {
584 case QXmlNodeModelIndex::Element:
585 case QXmlNodeModelIndex::Document:
586 case QXmlNodeModelIndex::Attribute:
587 return makeSingletonIterator(item: Item(UntypedAtomic::fromValue(value: stringValue(ni: n))));
588
589 case QXmlNodeModelIndex::Text:
590 case QXmlNodeModelIndex::ProcessingInstruction:
591 case QXmlNodeModelIndex::Comment:
592 return makeSingletonIterator(item: Item(AtomicString::fromValue(value: stringValue(ni: n))));
593 default:
594 {
595 Q_ASSERT_X(false, Q_FUNC_INFO,
596 qPrintable(QString::fromLatin1("A node type that doesn't exist "
597 "in the XPath Data Model was encountered.").arg(kind(preNumber))));
598 return Item::Iterator::Ptr(); /* Dummy, silence compiler warning. */
599 }
600 }
601}
602
603void AccelTree::copyNodeTo(const QXmlNodeModelIndex &node,
604 QAbstractXmlReceiver *const receiver,
605 const NodeCopySettings &settings) const
606{
607 /* This code piece can be seen as a customized version of
608 * QAbstractXmlReceiver::item/sendAsNode(). */
609 Q_ASSERT(receiver);
610 Q_ASSERT(!node.isNull());
611
612 typedef QHash<QXmlName::PrefixCode, QXmlName::NamespaceCode> Binding;
613 QStack<Binding> outputted;
614
615 switch(node.kind())
616 {
617 case QXmlNodeModelIndex::Element:
618 {
619 outputted.push(t: Binding());
620
621 /* Add the namespace for our element name. */
622 const QXmlName elementName(node.name());
623
624 receiver->startElement(name: elementName);
625
626 if(!settings.testFlag(flag: InheritNamespaces))
627 receiver->namespaceBinding(name: QXmlName(StandardNamespaces::StopNamespaceInheritance, 0,
628 StandardPrefixes::StopNamespaceInheritance));
629
630 if(settings.testFlag(flag: PreserveNamespaces))
631 node.sendNamespaces(receiver);
632 else
633 {
634 /* Find the namespaces that we actually use and add them to outputted. These are drawn
635 * from the element name, and the node's attributes. */
636 outputted.top().insert(akey: elementName.prefix(), avalue: elementName.namespaceURI());
637
638 const QXmlNodeModelIndex::Iterator::Ptr attributes(iterate(ni: node, axis: QXmlNodeModelIndex::AxisAttribute));
639 QXmlNodeModelIndex attr(attributes->next());
640
641 while(!attr.isNull())
642 {
643 const QXmlName &attrName = attr.name();
644 outputted.top().insert(akey: attrName.prefix(), avalue: attrName.namespaceURI());
645 attr = attributes->next();
646 }
647
648 Binding::const_iterator it(outputted.top().constBegin());
649 const Binding::const_iterator end(outputted.top().constEnd());
650
651 for(; it != end; ++it)
652 receiver->namespaceBinding(name: QXmlName(it.value(), 0, it.key()));
653 }
654
655 /* Send the attributes of the element. */
656 {
657 QXmlNodeModelIndex::Iterator::Ptr attributes(node.iterate(axis: QXmlNodeModelIndex::AxisAttribute));
658 QXmlNodeModelIndex attribute(attributes->next());
659
660 while(!attribute.isNull())
661 {
662 const QString &v = attribute.stringValue();
663 receiver->attribute(name: attribute.name(), value: QStringRef(&v));
664 attribute = attributes->next();
665 }
666 }
667
668 /* Send the children of the element. */
669 copyChildren(node, receiver, settings);
670
671 receiver->endElement();
672 outputted.pop();
673 break;
674 }
675 case QXmlNodeModelIndex::Document:
676 {
677 /* We need to intercept and grab the elements of the document node, such
678 * that we preserve/inherit preference applies to them. */
679 receiver->startDocument();
680 copyChildren(node, receiver, settings);
681 receiver->endDocument();
682 break;
683 }
684 default:
685 receiver->item(item: node);
686 }
687
688}
689
690QSourceLocation AccelTree::sourceLocation(const QXmlNodeModelIndex &index) const
691{
692 const PreNumber key = toPreNumber(n: index);
693 if (sourcePositions.contains(akey: key)) {
694 const QPair<qint64, qint64> position = sourcePositions.value(akey: key);
695 return QSourceLocation(m_documentURI, position.first, position.second);
696 } else {
697 return QSourceLocation();
698 }
699}
700
701void AccelTree::copyChildren(const QXmlNodeModelIndex &node,
702 QAbstractXmlReceiver *const receiver,
703 const NodeCopySettings &settings) const
704{
705 QXmlNodeModelIndex::Iterator::Ptr children(node.iterate(axis: QXmlNodeModelIndex::AxisChild));
706 QXmlNodeModelIndex child(children->next());
707
708 while(!child.isNull())
709 {
710 copyNodeTo(node: child, receiver, settings);
711 child = children->next();
712 }
713}
714
715QXmlNodeModelIndex AccelTree::elementById(const QXmlName &id) const
716{
717 const PreNumber pre = m_IDs.value(akey: id.localName(), adefaultValue: -1);
718 if(pre == -1)
719 return QXmlNodeModelIndex();
720 else
721 return createIndex(data: pre);
722}
723
724QVector<QXmlNodeModelIndex> AccelTree::nodesByIdref(const QXmlName &) const
725{
726 return QVector<QXmlNodeModelIndex>();
727}
728
729QT_END_NAMESPACE
730
731

source code of qtxmlpatterns/src/xmlpatterns/acceltree/qacceltree.cpp