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 "qgenericsequencetype_p.h" |
43 | #include "qnodesort_p.h" |
44 | #include "qpatternistlocale_p.h" |
45 | #include "qsequencemappingiterator_p.h" |
46 | #include "qtypechecker_p.h" |
47 | |
48 | #include "qpath_p.h" |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | using namespace QPatternist; |
53 | |
54 | Path::Path(const Expression::Ptr &operand1, |
55 | const Expression::Ptr &operand2, |
56 | const Kind kind) : PairContainer(operand1, operand2) |
57 | , m_hasCreatedSorter(kind != RegularPath) |
58 | , m_isLast(false) |
59 | , m_checkXPTY0018(kind == RegularPath) |
60 | , m_kind(kind) |
61 | { |
62 | } |
63 | |
64 | /*! \internal */ |
65 | Path::~Path() |
66 | { |
67 | } |
68 | |
69 | Item::Iterator::Ptr Path::mapToSequence(const Item &item, |
70 | const DynamicContext::Ptr &context) const |
71 | { |
72 | /* item is the focus here. That is in <e/>/1, item is <e/>. However, |
73 | * we don't use it, since the context item is accessed through |
74 | * DynamicContext::focusIterator() and friends. */ |
75 | Q_ASSERT(item); |
76 | Q_UNUSED(item); /* Needed when compiling in release mode. */ |
77 | return m_operand2->evaluateSequence(context); |
78 | } |
79 | |
80 | Item::Iterator::Ptr Path::evaluateSequence(const DynamicContext::Ptr &context) const |
81 | { |
82 | /* Note, we use the old context for m_operand1. */ |
83 | const Item::Iterator::Ptr source(m_operand1->evaluateSequence(context)); |
84 | |
85 | const DynamicContext::Ptr focus(context->createFocus()); |
86 | focus->setFocusIterator(source); |
87 | |
88 | const Item::Iterator::Ptr result(makeSequenceMappingIterator<Item>(mapper: ConstPtr(this), source, context: focus)); |
89 | |
90 | if(m_checkXPTY0018) |
91 | { |
92 | /* This is an expensive code path, but it should happen very rarely. */ |
93 | |
94 | enum FoundItem |
95 | { |
96 | FoundNone, |
97 | FoundNode, |
98 | FoundAtomicValue |
99 | } hasFound = FoundNone; |
100 | |
101 | Item::List whenChecked; |
102 | |
103 | Item next(result->next()); |
104 | |
105 | while(next) |
106 | { |
107 | const FoundItem found = next.isAtomicValue() ? FoundAtomicValue : FoundNode; |
108 | |
109 | if(hasFound != FoundNone && hasFound != found) |
110 | { |
111 | /* It's an atomic value and we've already found a node. Mixed content. */ |
112 | context->error(message: QtXmlPatterns::tr(sourceText: "The last step in a path must contain either nodes " |
113 | "or atomic values. It cannot be a mixture between the two." ), |
114 | errorCode: ReportContext::XPTY0018, reflection: this); |
115 | } |
116 | else |
117 | hasFound = found; |
118 | |
119 | whenChecked.append(t: next); |
120 | next = result->next(); |
121 | } |
122 | |
123 | return makeListIterator(list: whenChecked); |
124 | } |
125 | else |
126 | return result; |
127 | } |
128 | |
129 | Item Path::evaluateSingleton(const DynamicContext::Ptr &context) const |
130 | { |
131 | /* This function is called if both operands' cardinality is exactly-one. Therefore |
132 | * we manually go forward in the focus by calling next(). |
133 | * |
134 | * We don't check for XPTY0018, only in evaluateSequence(), since if we're guaranteed |
135 | * to evaluate to one item, we can only evaluate to one node or one atomic value. |
136 | */ |
137 | |
138 | /* Note, we use the old context for m_operand1. */ |
139 | const Item::Iterator::Ptr source(m_operand1->evaluateSequence(context)); |
140 | |
141 | const DynamicContext::Ptr focus(context->createFocus()); |
142 | focus->setFocusIterator(source); |
143 | |
144 | /* This test is needed because if the focus is empty, we don't want to(nor can't) evaluate |
145 | * the next step. */ |
146 | // TODO Why are we at all invoked then? |
147 | if(source->next()) |
148 | return m_operand2->evaluateSingleton(context: focus); |
149 | else |
150 | return Item(); |
151 | } |
152 | |
153 | void Path::evaluateToSequenceReceiver(const DynamicContext::Ptr &context) const |
154 | { |
155 | /* Note, we use the old context for m_operand1. */ |
156 | const Item::Iterator::Ptr source(m_operand1->evaluateSequence(context)); |
157 | |
158 | const DynamicContext::Ptr focus(context->createFocus()); |
159 | focus->setFocusIterator(source); |
160 | |
161 | while(source->next()) |
162 | m_operand2->evaluateToSequenceReceiver(context: focus); |
163 | } |
164 | |
165 | Expression::Ptr Path::compress(const StaticContext::Ptr &context) |
166 | { |
167 | const Expression::Ptr me(PairContainer::compress(context)); |
168 | |
169 | /* "./expr" is by now equal to "expr" since we've done |
170 | * focus/type checks, and a node sorter has been inserted. */ |
171 | if(m_operand1->is(i: IDContextItem)) |
172 | return m_operand2; |
173 | |
174 | /* We do this as late as we can, such that we pick up the most recent type |
175 | * from the operand. */ |
176 | if(m_isLast && m_kind != XSLTForEach && m_operand2->staticType()->itemType() == BuiltinTypes::item) |
177 | m_checkXPTY0018 = true; |
178 | |
179 | return me; |
180 | } |
181 | |
182 | Expression::Ptr Path::typeCheck(const StaticContext::Ptr &context, |
183 | const SequenceType::Ptr &reqType) |
184 | { |
185 | m_operand2->announceFocusType(itemType: newFocusType()); |
186 | |
187 | /* Here we apply the function conversion first, and with the error code |
188 | * that we want -- XPTY0019 instead of XPTY0004. Unfortunately |
189 | * PairContainer::typeCheck() will do the type check again, which is |
190 | * redundant in the case of when we're not XSLTForEach. |
191 | * |
192 | * If we're XSLTForEach, it means we're a synthetic "map" expression for |
193 | * implementing various XSL-T expressions, and hence don't have the |
194 | * constraint of XPTY0019. |
195 | * |
196 | * It's important that typeCheck() is run for the operands(of course), and the call to |
197 | * PairContainer::typeCheck() ensures that below, in the case that we're XSL-T code. |
198 | * |
199 | * The type we expect, CommonSequenceTypes::ZeroOrMoreNodes() needs to be in sync with |
200 | * what we return in expectedOperandTypes(). */ |
201 | if(m_kind != XSLTForEach) |
202 | { |
203 | m_operand1 = TypeChecker::applyFunctionConversion(operand: m_operand1, |
204 | reqType: CommonSequenceTypes::ZeroOrMoreNodes, |
205 | context, |
206 | code: m_kind == ForApplyTemplate ? ReportContext::XTTE0520 |
207 | : ReportContext::XPTY0019); |
208 | } |
209 | |
210 | /* If our step ends with atomic values, we cannot sort. |
211 | * |
212 | * We must smack the NodeSortExpression ontop before calling typeCheck(), since the latter |
213 | * may insert an Atomizer, as possibly mandated by reqType. By doing it after, the Atomizer |
214 | * will be a parent to NodeSortExpression, as opposed to a child. |
215 | */ |
216 | if(!m_hasCreatedSorter) |
217 | { |
218 | m_hasCreatedSorter = true; |
219 | |
220 | return NodeSortExpression::wrapAround(operand: Expression::Ptr(this), context)->typeCheck(context, reqType); |
221 | } |
222 | else |
223 | return PairContainer::typeCheck(context, reqType); |
224 | } |
225 | |
226 | SequenceType::List Path::expectedOperandTypes() const |
227 | { |
228 | SequenceType::List result; |
229 | |
230 | /* This value needs to be in sync with what we pass to |
231 | * applyFunctionConversion() in typeCheck() above. |
232 | * |
233 | * We don't have the XPTY0019 restriction when we're synthetic XSL-T code. |
234 | */ |
235 | if(m_kind == XSLTForEach) |
236 | result.append(t: CommonSequenceTypes::ZeroOrMoreItems); |
237 | else |
238 | result.append(t: CommonSequenceTypes::ZeroOrMoreNodes); |
239 | |
240 | result.append(t: CommonSequenceTypes::ZeroOrMoreItems); |
241 | return result; |
242 | } |
243 | |
244 | SequenceType::Ptr Path::staticType() const |
245 | { |
246 | const SequenceType::Ptr opType(m_operand2->staticType()); |
247 | |
248 | /* For each parent step, we evaluate the child step. So multiply the two |
249 | * cardinalities. */ |
250 | return makeGenericSequenceType(itemType: opType->itemType(), |
251 | cardinality: m_operand1->staticType()->cardinality() * opType->cardinality()); |
252 | } |
253 | |
254 | ExpressionVisitorResult::Ptr Path::accept(const ExpressionVisitor::Ptr &visitor) const |
255 | { |
256 | return visitor->visit(this); |
257 | } |
258 | |
259 | Expression::Properties Path::properties() const |
260 | { |
261 | return CreatesFocusForLast | ((m_operand1->properties() | m_operand2->properties()) & (RequiresCurrentItem | DisableElimination)); |
262 | } |
263 | |
264 | ItemType::Ptr Path::newFocusType() const |
265 | { |
266 | return m_operand1->staticType()->itemType(); |
267 | } |
268 | |
269 | Expression::ID Path::id() const |
270 | { |
271 | return IDPath; |
272 | } |
273 | |
274 | QT_END_NAMESPACE |
275 | |