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//
41// W A R N I N G
42// -------------
43//
44// This file is not part of the Qt API. It exists purely as an
45// implementation detail. This header file may change from version to
46// version without notice, or even be removed.
47//
48// We mean it.
49
50#ifndef Patternist_AccelIterators_H
51#define Patternist_AccelIterators_H
52
53#include <private/qacceltree_p.h>
54#include <private/qitem_p.h>
55
56QT_BEGIN_NAMESPACE
57
58namespace QPatternist
59{
60 /**
61 * @short Abstract base class for Iterators for the AccelTree, that
62 * contains common functions and members.
63 *
64 * @author Frans Englich<frans.englich@nokia.com>
65 */
66 class AccelIterator : public QXmlNodeModelIndex::Iterator
67 {
68 public:
69 virtual xsInteger position() const;
70 virtual QXmlNodeModelIndex current() const;
71
72 protected:
73 inline AccelIterator(const AccelTree *const doc,
74 const AccelTree::PreNumber pre,
75 const AccelTree::PreNumber currentPre) : m_document(doc)
76 , m_preNumber(pre)
77 , m_currentPre(currentPre)
78 , m_position(0)
79
80 {
81 Q_ASSERT(m_document);
82 Q_ASSERT(m_preNumber >= 0);
83 }
84
85 inline QXmlNodeModelIndex closedExit()
86 {
87 m_position = -1;
88 m_current.reset();
89 return QXmlNodeModelIndex();
90 }
91
92 /**
93 * We do not own it.
94 */
95 const AccelTree *const m_document;
96
97 /**
98 * The pre number of the node that should be navigated from.
99 */
100 const AccelTree::PreNumber m_preNumber;
101 AccelTree::PreNumber m_currentPre;
102 xsInteger m_position;
103 QXmlNodeModelIndex m_current;
104 };
105
106 /**
107 * @short Iterates along the @c ancestor or @c ancestor-or-self axis in an AccelTree.
108 *
109 * @author Frans Englich<frans.englich@nokia.com>
110 */
111 template<const bool IncludeSelf>
112 class AncestorIterator : public AccelIterator
113 {
114 public:
115 /**
116 * @p pre is the node from which iteration starts
117 * from. In the @c ancestor axis it is excluded,
118 * while in @c ancestor-or-self it is included. @p pre
119 * must have at least one ancestor.
120 */
121 inline AncestorIterator(const AccelTree *const doc,
122 const AccelTree::PreNumber pre) : AccelIterator(doc, pre, IncludeSelf ? pre : doc->basicData.at(i: pre).parent())
123 {
124 Q_ASSERT(IncludeSelf || m_document->hasParent(pre));
125 }
126
127 virtual QXmlNodeModelIndex next()
128 {
129 if(m_currentPre == -1)
130 return closedExit();
131 else
132 {
133 ++m_position;
134 m_current = m_document->createIndex(m_currentPre);
135 m_currentPre = m_document->basicData.at(i: m_currentPre).parent();
136
137 return m_current;
138 }
139 }
140
141 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const
142 {
143 return QXmlNodeModelIndex::Iterator::Ptr(new AncestorIterator<IncludeSelf>(m_document, m_preNumber));
144 }
145 };
146
147 /**
148 * @short Iterates along the @c child axis in an AccelTree.
149 *
150 * @author Frans Englich<frans.englich@nokia.com>
151 */
152 class ChildIterator : public AccelIterator
153 {
154 public:
155 /**
156 * @p pre must have at least one child.
157 */
158 inline ChildIterator(const AccelTree *const doc,
159 const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + 1),
160 m_depth(m_document->depth(pre: m_currentPre))
161 {
162 Q_ASSERT(m_document->hasChildren(pre));
163
164 /* Skip the attributes, that are children in the pre/post plane, of
165 * the node we're applying the child axis to. */
166 while(m_document->kind(pre: m_currentPre) == QXmlNodeModelIndex::Attribute)
167 {
168 ++m_currentPre;
169 /* We check the depth here because we would otherwise include
170 * following siblings. */
171 if(m_currentPre > m_document->maximumPreNumber() || m_document->depth(pre: m_currentPre) != m_depth)
172 {
173 m_currentPre = -1;
174 break;
175 }
176 }
177 }
178
179 virtual QXmlNodeModelIndex next();
180 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
181
182 private:
183 const AccelTree::Depth m_depth;
184 };
185
186 /**
187 * @short Iterates along the sibling axes in an AccelTree.
188 *
189 * @author Frans Englich<frans.englich@nokia.com>
190 */
191 template<const bool IsFollowing>
192 class SiblingIterator : public AccelIterator
193 {
194 public:
195 inline SiblingIterator(const AccelTree *const doc,
196 const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + (IsFollowing ? 0 : -1)),
197 m_depth(doc->depth(pre))
198 {
199 Q_ASSERT_X(IsFollowing || pre != 0, "",
200 "When being preceding-sibling, the context node cannot be the first node in the document.");
201 Q_ASSERT_X(!IsFollowing || pre != m_document->maximumPreNumber(), "",
202 "When being following-sibling, the context node cannot be the last node in the document.");
203 }
204
205 virtual QXmlNodeModelIndex next()
206 {
207 if(m_currentPre == -1)
208 return QXmlNodeModelIndex();
209
210 if(IsFollowing)
211 {
212 /* Skip the descendants, and jump to the next node. */
213 m_currentPre += m_document->size(pre: m_currentPre) + 1;
214
215 if(m_currentPre > m_document->maximumPreNumber() || m_document->depth(pre: m_currentPre) != m_depth)
216 return closedExit();
217 else
218 {
219 ++m_position;
220 m_current = m_document->createIndex(m_currentPre);
221 return m_current;
222 }
223 }
224 else
225 {
226 while(m_document->depth(pre: m_currentPre) > m_depth)
227 --m_currentPre;
228
229 while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
230 --m_currentPre;
231
232 if(m_document->depth(pre: m_currentPre) == m_depth &&
233 m_document->kind(m_currentPre) != QXmlNodeModelIndex::Attribute)
234 {
235 m_current = m_document->createIndex(m_currentPre);
236 ++m_position;
237 --m_currentPre;
238 return m_current;
239 }
240 else
241 {
242 m_currentPre = -1;
243 return closedExit();
244 }
245 }
246 }
247
248 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const
249 {
250 return QXmlNodeModelIndex::Iterator::Ptr(new SiblingIterator<IsFollowing>(m_document, m_preNumber));
251 }
252
253 private:
254 const AccelTree::Depth m_depth;
255 };
256
257 /**
258 * @short Implements axis @c descendant and @c descendant-or-self for the
259 * AccelTree.
260 *
261 * @author Frans Englich <frans.englich@nokia.com>
262 */
263 template<const bool IncludeSelf>
264 class DescendantIterator : public AccelIterator
265 {
266 public:
267 /**
268 * @p pre must have at least one child.
269 */
270 inline DescendantIterator(const AccelTree *const doc,
271 const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + (IncludeSelf ? 0 : 1)),
272 m_postNumber(doc->postNumber(pre))
273 {
274 Q_ASSERT(IncludeSelf || m_document->hasChildren(pre));
275
276 /* Make sure that m_currentPre is the first node part of this axis.
277 * Since we're not including ourself, advance to the node after our
278 * attributes, if any. */
279 if(!IncludeSelf)
280 {
281 while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
282 {
283 ++m_currentPre;
284 /* We check the depth here because we would otherwise include
285 * following siblings. */
286 if(m_currentPre > m_document->maximumPreNumber() || m_document->postNumber(pre: m_currentPre) > m_postNumber)
287 {
288 m_currentPre = -1;
289 break;
290 }
291 }
292 }
293 }
294
295 virtual QXmlNodeModelIndex next()
296 {
297 if(m_currentPre == -1)
298 return closedExit();
299
300 ++m_position;
301 m_current = m_document->createIndex(m_currentPre);
302
303 ++m_currentPre;
304
305 if(m_currentPre > m_document->maximumPreNumber())
306 {
307 m_currentPre = -1;
308 return m_current;
309 }
310
311 if(m_document->postNumber(pre: m_currentPre) < m_postNumber)
312 {
313 while(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute)
314 {
315 ++m_currentPre;
316 if(m_currentPre > m_document->maximumPreNumber())
317 {
318 m_currentPre = -1;
319 break;
320 }
321 }
322 }
323 else
324 m_currentPre = -1;
325
326 return m_current;
327 }
328
329 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const
330 {
331 return QXmlNodeModelIndex::Iterator::Ptr(new DescendantIterator<IncludeSelf>(m_document, m_preNumber));
332 }
333
334 private:
335 const AccelTree::PreNumber m_postNumber;
336 };
337
338 /**
339 * @short Implements axis @c following for the AccelTree.
340 *
341 * @author Frans Englich <frans.englich@nokia.com>
342 */
343 class FollowingIterator : public AccelIterator
344 {
345 public:
346 /**
347 * @ pre must have at least one child.
348 */
349 inline FollowingIterator(const AccelTree *const doc,
350 const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre)
351 {
352 }
353
354 virtual QXmlNodeModelIndex next();
355 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
356 };
357
358 /**
359 * @short Implements axis @c preceding for the AccelTree.
360 *
361 * @author Frans Englich <frans.englich@nokia.com>
362 */
363 class PrecedingIterator : public AccelIterator
364 {
365 public:
366 /**
367 * @ pre must have at least one child.
368 */
369 inline PrecedingIterator(const AccelTree *const doc,
370 const AccelTree::PreNumber pre) : AccelIterator(doc, pre,
371 pre - 1 /* currentPre */)
372 , m_postNumber(m_document->postNumber(pre: m_preNumber))
373 {
374 }
375
376 virtual QXmlNodeModelIndex next();
377 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
378
379 private:
380 const AccelTree::PreNumber m_postNumber;
381 };
382
383 /**
384 * @short Implements axis @c attribute for the AccelTree.
385 *
386 * @author Frans Englich <frans.englich@nokia.com>
387 */
388 class AttributeIterator : public AccelIterator
389 {
390 public:
391 /**
392 * @p pre must have at least one child.
393 */
394 inline AttributeIterator(const AccelTree *const doc, const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + 1)
395 {
396 Q_ASSERT(m_document->hasChildren(pre));
397 Q_ASSERT(m_document->kind(m_currentPre) == QXmlNodeModelIndex::Attribute);
398 }
399
400 virtual QXmlNodeModelIndex next();
401 virtual QXmlNodeModelIndex::Iterator::Ptr copy() const;
402 };
403}
404
405QT_END_NAMESPACE
406
407#endif
408

source code of qtxmlpatterns/src/xmlpatterns/acceltree/qacceliterators_p.h