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 "qbuiltintypes_p.h"
41 #include "qcommonsequencetypes_p.h"
42 #include "qitemmappingiterator_p.h"
43 #include "qgenericsequencetype_p.h"
44 #include "qparentnodeaxis_p.h"
45 
46 #include "qaxisstep_p.h"
47 
48 QT_BEGIN_NAMESPACE
49 
50 using namespace QPatternist;
51 
52 namespace QPatternist
53 {
54     /**
55      * This operator is needed for the s_whenAxisNodeKindEmpty array. The @c int constructors
56      * ensure we invoke another operator| such that we don't get an infinite loop.
57      */
operator |(const QXmlNodeModelIndex::NodeKind & op1,const QXmlNodeModelIndex::NodeKind & op2)58     static inline QXmlNodeModelIndex::NodeKind operator|(const QXmlNodeModelIndex::NodeKind &op1, const QXmlNodeModelIndex::NodeKind &op2)
59     {
60         return QXmlNodeModelIndex::NodeKind(int(op1) | int(op2));
61     }
62 }
63 
64 /**
65  * @note The order is significant. It is of the same order as the values in QXmlNodeModelIndex::Axis is declared.
66  */
67 const QXmlNodeModelIndex::NodeKind AxisStep::s_whenAxisNodeKindEmpty[] =
68 {
69     QXmlNodeModelIndex::Attribute|QXmlNodeModelIndex::Text|QXmlNodeModelIndex::ProcessingInstruction|QXmlNodeModelIndex::Comment|QXmlNodeModelIndex::Namespace,   // child;
70     QXmlNodeModelIndex::Attribute|QXmlNodeModelIndex::Text|QXmlNodeModelIndex::ProcessingInstruction|QXmlNodeModelIndex::Comment|QXmlNodeModelIndex::Namespace,   // descendant;
71     QXmlNodeModelIndex::Document|QXmlNodeModelIndex::Attribute|QXmlNodeModelIndex::Text|QXmlNodeModelIndex::ProcessingInstruction|QXmlNodeModelIndex::Comment|QXmlNodeModelIndex::Namespace,// attribute;
72     QXmlNodeModelIndex::NodeKind(0),                         // self;
73     QXmlNodeModelIndex::NodeKind(0),                         // descendant-or-self;
74     QXmlNodeModelIndex::Document|QXmlNodeModelIndex::Attribute|QXmlNodeModelIndex::Text|QXmlNodeModelIndex::ProcessingInstruction|QXmlNodeModelIndex::Comment|QXmlNodeModelIndex::Namespace,    // namespace;
75     QXmlNodeModelIndex::Document,                                         // following;
76     QXmlNodeModelIndex::Document,                                         // parent;
77     QXmlNodeModelIndex::Document,                                         // ancestor
78     QXmlNodeModelIndex::Document|QXmlNodeModelIndex::Attribute|QXmlNodeModelIndex::Namespace,         // preceding-sibling;
79     QXmlNodeModelIndex::Document|QXmlNodeModelIndex::Attribute|QXmlNodeModelIndex::Namespace,         // following-sibling;
80     QXmlNodeModelIndex::Document,                                         // preceding;
81     QXmlNodeModelIndex::NodeKind(0)                          // ancestor-or-self;
82 };
83 
isAlwaysEmpty(const QXmlNodeModelIndex::Axis axis,const QXmlNodeModelIndex::NodeKind nodeKind)84 bool AxisStep::isAlwaysEmpty(const QXmlNodeModelIndex::Axis axis, const QXmlNodeModelIndex::NodeKind nodeKind)
85 {
86     return (s_whenAxisNodeKindEmpty[(1 >> axis) - 1] & nodeKind) != 0;
87 }
88 
AxisStep(const QXmlNodeModelIndex::Axis a,const ItemType::Ptr & nt)89 AxisStep::AxisStep(const QXmlNodeModelIndex::Axis a,
90                    const ItemType::Ptr &nt) : m_axis(a),
91                                               m_nodeTest(nt)
92 {
93     Q_ASSERT(m_nodeTest);
94     Q_ASSERT_X(BuiltinTypes::node->xdtTypeMatches(m_nodeTest), Q_FUNC_INFO,
95                "We assume we're a node type.");
96 }
97 
mapToItem(const QXmlNodeModelIndex & node,const DynamicContext::Ptr & context) const98 Item AxisStep::mapToItem(const QXmlNodeModelIndex &node,
99                          const DynamicContext::Ptr &context) const
100 {
101     Q_ASSERT(!node.isNull());
102     Q_ASSERT(Item(node).isNode());
103     Q_ASSERT(Item(node));
104     Q_UNUSED(context);
105 
106     if(m_nodeTest->itemMatches(Item(node)))
107         return Item(node);
108     else
109         return Item();
110 }
111 
evaluateSequence(const DynamicContext::Ptr & context) const112 Item::Iterator::Ptr AxisStep::evaluateSequence(const DynamicContext::Ptr &context) const
113 {
114     /* If we don't have a focus, it's either a bug or our parent isn't a Path
115      * that have advanced the focus iterator. Hence, attempt to advance the focus on our own. */
116     if(!context->contextItem())
117         context->focusIterator()->next();
118 
119     Q_ASSERT(context->contextItem());
120 
121     const QXmlNodeModelIndex::Iterator::Ptr source(context->contextItem().asNode().iterate(m_axis));
122 
123     return makeItemMappingIterator<Item>(ConstPtr(this), source, context);
124 }
125 
evaluateSingleton(const DynamicContext::Ptr & context) const126 Item AxisStep::evaluateSingleton(const DynamicContext::Ptr &context) const
127 {
128     /* If we don't have a focus, it's either a bug or our parent isn't a Path
129      * that have advanced the focus iterator. Hence, attempt to advance the focus on our own. */
130     if(!context->contextItem())
131         context->focusIterator()->next();
132 
133     Q_ASSERT(context->contextItem());
134 
135     const QXmlNodeModelIndex::Iterator::Ptr it(context->contextItem().asNode().iterate(m_axis));
136     QXmlNodeModelIndex next(it->next());
137 
138     while(!next.isNull())
139     {
140         const Item candidate(mapToItem(next, context));
141 
142         if(candidate)
143             return candidate;
144         else
145             next = it->next();
146     };
147 
148     return Item();
149 }
150 
typeCheck(const StaticContext::Ptr & context,const SequenceType::Ptr & reqType)151 Expression::Ptr AxisStep::typeCheck(const StaticContext::Ptr &context,
152                                     const SequenceType::Ptr &reqType)
153 {
154     if(m_axis == QXmlNodeModelIndex::AxisParent && *m_nodeTest == *BuiltinTypes::node)
155     {
156         /* We only rewrite parent::node() to ParentNodeAxis. */
157         return rewrite(Expression::Ptr(new ParentNodeAxis()), context)->typeCheck(context, reqType);
158     }
159     /* TODO temporarily disabled
160     else if(isAlwaysEmpty(m_axis, static_cast<const AnyNodeType *>(m_nodeTest.data())->nodeKind()))
161         return EmptySequence::create(this, context);
162         */
163     else
164         return EmptyContainer::typeCheck(context, reqType);
165 }
166 
staticType() const167 SequenceType::Ptr AxisStep::staticType() const
168 {
169     Cardinality cardinality;
170 
171     if(m_axis == QXmlNodeModelIndex::AxisSelf || m_axis == QXmlNodeModelIndex::AxisParent)
172         cardinality = Cardinality::zeroOrOne();
173     else
174         cardinality = Cardinality::zeroOrMore();
175 
176     return makeGenericSequenceType(m_nodeTest,
177                                    cardinality);
178 }
179 
expectedOperandTypes() const180 SequenceType::List AxisStep::expectedOperandTypes() const
181 {
182     SequenceType::List result;
183     result.append(CommonSequenceTypes::ZeroOrMoreNodes);
184     return result;
185 }
186 
properties() const187 Expression::Properties AxisStep::properties() const
188 {
189     return RequiresContextItem | DisableElimination;
190 }
191 
expectedContextItemType() const192 ItemType::Ptr AxisStep::expectedContextItemType() const
193 {
194     return BuiltinTypes::node;
195 }
196 
accept(const ExpressionVisitor::Ptr & visitor) const197 ExpressionVisitorResult::Ptr AxisStep::accept(const ExpressionVisitor::Ptr &visitor) const
198 {
199     return visitor->visit(this);
200 }
201 
axis() const202 QXmlNodeModelIndex::Axis AxisStep::axis() const
203 {
204     return m_axis;
205 }
206 
axisName(const QXmlNodeModelIndex::Axis axis)207 QString AxisStep::axisName(const QXmlNodeModelIndex::Axis axis)
208 {
209     const char *result = 0;
210 
211     switch(axis)
212     {
213         /* These must not be translated. */
214         case QXmlNodeModelIndex::AxisAncestorOrSelf:    result = "ancestor-or-self";    break;
215         case QXmlNodeModelIndex::AxisAncestor:          result = "ancestor";            break;
216         case QXmlNodeModelIndex::AxisAttributeOrTop:    result = "attribute-or-top";    break;
217         case QXmlNodeModelIndex::AxisAttribute:         result = "attribute";           break;
218         case QXmlNodeModelIndex::AxisChildOrTop:        result = "child-or-top";        break;
219         case QXmlNodeModelIndex::AxisChild:             result = "child";               break;
220         case QXmlNodeModelIndex::AxisDescendantOrSelf:  result = "descendant-or-self";  break;
221         case QXmlNodeModelIndex::AxisDescendant:        result = "descendant";          break;
222         case QXmlNodeModelIndex::AxisFollowing:         result = "following";           break;
223         case QXmlNodeModelIndex::AxisFollowingSibling:  result = "following-sibling";   break;
224         case QXmlNodeModelIndex::AxisNamespace:         result = "namespace";           break;
225         case QXmlNodeModelIndex::AxisParent:            result = "parent";              break;
226         case QXmlNodeModelIndex::AxisPreceding:         result = "preceding";           break;
227         case QXmlNodeModelIndex::AxisPrecedingSibling:  result = "preceding-sibling";   break;
228         case QXmlNodeModelIndex::AxisSelf:              result = "self";                break;
229     }
230 
231     Q_ASSERT_X(result, Q_FUNC_INFO, "An unknown axis type was apparently encountered.");
232     return QString::fromLatin1(result);
233 }
234 
patternPriority() const235 PatternPriority AxisStep::patternPriority() const
236 {
237     return static_cast<const AnyNodeType *>(m_nodeTest.data())->patternPriority();
238 }
239 
id() const240 Expression::ID AxisStep::id() const
241 {
242     return IDAxisStep;
243 }
244 
245 QT_END_NAMESPACE
246