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:
AccelIterator(const AccelTree * const doc,const AccelTree::PreNumber pre,const AccelTree::PreNumber currentPre)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 
closedExit()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          */
AncestorIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)121         inline AncestorIterator(const AccelTree *const doc,
122                                 const AccelTree::PreNumber pre) : AccelIterator(doc, pre, IncludeSelf ? pre : doc->basicData.at(pre).parent())
123         {
124             Q_ASSERT(IncludeSelf || m_document->hasParent(pre));
125         }
126 
next()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(m_currentPre).parent();
136 
137                 return m_current;
138             }
139         }
140 
copy()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          */
ChildIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)158         inline ChildIterator(const AccelTree *const doc,
159                              const AccelTree::PreNumber pre) : AccelIterator(doc, pre, pre + 1),
160                                                                m_depth(m_document->depth(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(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(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:
SiblingIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)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 
next()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(m_currentPre) + 1;
214 
215                 if(m_currentPre > m_document->maximumPreNumber() || m_document->depth(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(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(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 
copy()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          */
DescendantIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)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(m_currentPre) > m_postNumber)
287                     {
288                         m_currentPre = -1;
289                         break;
290                     }
291                 }
292             }
293         }
294 
next()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(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 
copy()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          */
FollowingIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)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          */
PrecedingIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)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(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          */
AttributeIterator(const AccelTree * const doc,const AccelTree::PreNumber pre)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