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 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | namespace 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 | |
405 | QT_END_NAMESPACE |
406 | |
407 | #endif |
408 | |