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
50QT_BEGIN_NAMESPACE
51
52using namespace QPatternist;
53
54Path::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 */
65Path::~Path()
66{
67}
68
69Item::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
80Item::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
129Item 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
153void 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
165Expression::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
182Expression::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
226SequenceType::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
244SequenceType::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
254ExpressionVisitorResult::Ptr Path::accept(const ExpressionVisitor::Ptr &visitor) const
255{
256 return visitor->visit(this);
257}
258
259Expression::Properties Path::properties() const
260{
261 return CreatesFocusForLast | ((m_operand1->properties() | m_operand2->properties()) & (RequiresCurrentItem | DisableElimination));
262}
263
264ItemType::Ptr Path::newFocusType() const
265{
266 return m_operand1->staticType()->itemType();
267}
268
269Expression::ID Path::id() const
270{
271 return IDPath;
272}
273
274QT_END_NAMESPACE
275

source code of qtxmlpatterns/src/xmlpatterns/expr/qpath.cpp