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 "qcommonvalues_p.h"
43#include "qdistinctiterator_p.h"
44#include "qebvextractor_p.h"
45#include "qemptysequence_p.h"
46#include "qgenericsequencetype_p.h"
47#include "qindexofiterator_p.h"
48#include "qinsertioniterator_p.h"
49#include "qinteger_p.h"
50#include "qremovaliterator_p.h"
51#include "qsequencegeneratingfns_p.h"
52#include "qsubsequenceiterator_p.h"
53
54#include "qsequencefns_p.h"
55
56QT_BEGIN_NAMESPACE
57
58using namespace QPatternist;
59
60bool BooleanFN::evaluateEBV(const DynamicContext::Ptr &context) const
61{
62 return m_operands.first()->evaluateEBV(context);
63}
64
65Expression::Ptr BooleanFN::typeCheck(const StaticContext::Ptr &context,
66 const SequenceType::Ptr &reqType)
67{
68 return EBVExtractor::typeCheck<FunctionCall>(context, reqType, caller: this);
69}
70
71Item::Iterator::Ptr IndexOfFN::evaluateSequence(const DynamicContext::Ptr &context) const
72{
73 return Item::Iterator::Ptr(new IndexOfIterator(m_operands.first()->evaluateSequence(context),
74 m_operands.at(i: 1)->evaluateSingleton(context),
75 comparator(), context,
76 ConstPtr(this)));
77}
78
79Expression::Ptr IndexOfFN::typeCheck(const StaticContext::Ptr &context,
80 const SequenceType::Ptr &reqType)
81{
82 const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));
83 const ItemType::Ptr t1(m_operands.first()->staticType()->itemType());
84 const ItemType::Ptr t2(m_operands.at(i: 1)->staticType()->itemType());
85
86 if(*CommonSequenceTypes::Empty == *t1 ||
87 *CommonSequenceTypes::Empty == *t2)
88 {
89 return EmptySequence::create(replacementFor: this, context);
90 }
91 else
92 {
93 prepareComparison(c: fetchComparator(t1, t2, context));
94 return me;
95 }
96}
97
98Item::Iterator::Ptr DistinctValuesFN::evaluateSequence(const DynamicContext::Ptr &context) const
99{
100 return Item::Iterator::Ptr(new DistinctIterator(m_operands.first()->evaluateSequence(context),
101 comparator(),
102 ConstPtr(this),
103 context));
104}
105
106Expression::Ptr DistinctValuesFN::typeCheck(const StaticContext::Ptr &context,
107 const SequenceType::Ptr &reqType)
108{
109 const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));
110 const ItemType::Ptr t1(m_operands.first()->staticType()->itemType());
111
112 if(*CommonSequenceTypes::Empty == *t1)
113 return EmptySequence::create(replacementFor: this, context);
114 else if(!m_operands.first()->staticType()->cardinality().allowsMany())
115 return m_operands.first();
116 else if(BuiltinTypes::xsAnyAtomicType->xdtTypeMatches(other: t1))
117 return me;
118 else
119 {
120 prepareComparison(c: fetchComparator(t1, t2: t1, context));
121 return me;
122 }
123}
124
125SequenceType::Ptr DistinctValuesFN::staticType() const
126{
127 const SequenceType::Ptr t(m_operands.first()->staticType());
128 return makeGenericSequenceType(itemType: t->itemType(),
129 cardinality: t->cardinality().allowsMany() ? Cardinality::oneOrMore()
130 : Cardinality::exactlyOne());
131}
132
133Item::Iterator::Ptr InsertBeforeFN::evaluateSequence(const DynamicContext::Ptr &context) const
134{
135 const Item::Iterator::Ptr target(m_operands.first()->evaluateSequence(context));
136 const Item::Iterator::Ptr inserts(m_operands.at(i: 2)->evaluateSequence(context));
137
138 xsInteger position = m_operands.at(i: 1)->evaluateSingleton(context).as<Numeric>()->toInteger();
139
140 if(position < 1)
141 position = 1;
142
143 return Item::Iterator::Ptr(new InsertionIterator(target, position, inserts));
144}
145
146Item InsertBeforeFN::evaluateSingleton(const DynamicContext::Ptr &context) const
147{
148 return evaluateSequence(context)->next();
149}
150
151SequenceType::Ptr InsertBeforeFN::staticType() const
152{
153 const SequenceType::Ptr t1(m_operands.first()->staticType());
154 const SequenceType::Ptr t2(m_operands.last()->staticType());
155
156 return makeGenericSequenceType(itemType: t1->itemType() | t2->itemType(),
157 cardinality: t1->cardinality() + t2->cardinality());
158}
159
160Item::Iterator::Ptr RemoveFN::evaluateSequence(const DynamicContext::Ptr &context) const
161{
162 const xsInteger pos = m_operands.last()->evaluateSingleton(context).as<Numeric>()->toInteger();
163 Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context));
164
165 if(pos < 1)
166 return it;
167
168 return Item::Iterator::Ptr(new RemovalIterator(it, pos));
169}
170
171Item RemoveFN::evaluateSingleton(const DynamicContext::Ptr &context) const
172{
173 const xsInteger pos = m_operands.last()->evaluateSingleton(context).as<Numeric>()->toInteger();
174 if(pos <= 1)
175 return Item();
176
177 return m_operands.first()->evaluateSingleton(context);
178}
179
180SequenceType::Ptr RemoveFN::staticType() const
181{
182 const SequenceType::Ptr opType(m_operands.first()->staticType());
183 const Cardinality c(opType->cardinality());
184
185 if(c.minimum() == 0)
186 return makeGenericSequenceType(itemType: opType->itemType(), cardinality: c);
187 else
188 {
189 return makeGenericSequenceType(itemType: opType->itemType(),
190 cardinality: Cardinality::fromRange(minimum: c.minimum() - 1,
191 maximum: c.maximum()));
192 }
193}
194
195Item::Iterator::Ptr ReverseFN::evaluateSequence(const DynamicContext::Ptr &context) const
196{
197 return m_operands.first()->evaluateSequence(context)->toReversed();
198}
199
200Expression::Ptr ReverseFN::typeCheck(const StaticContext::Ptr &context,
201 const SequenceType::Ptr &reqType)
202{
203 if(m_operands.first()->staticType()->cardinality().allowsMany())
204 return FunctionCall::typeCheck(context, reqType);
205 else
206 return m_operands.first()->typeCheck(context, reqType);
207}
208
209SequenceType::Ptr ReverseFN::staticType() const
210{
211 return m_operands.first()->staticType();
212}
213
214SubsequenceFN::SubsequenceFN() : m_hasTypeChecked(false)
215{
216}
217
218Expression::Ptr SubsequenceFN::typeCheck(const StaticContext::Ptr &context,
219 const SequenceType::Ptr &reqType)
220{
221 m_hasTypeChecked = true;
222 return FunctionCall::typeCheck(context, reqType);
223}
224
225Item::Iterator::Ptr SubsequenceFN::evaluateSequence(const DynamicContext::Ptr &context) const
226{
227 Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context));
228
229 xsInteger startingLoc = m_operands.at(i: 1)->evaluateSingleton(context).as<Numeric>()->round()->toInteger();
230 xsInteger length = -1;
231
232 if(m_operands.count() == 3)
233 {
234 length = m_operands.last()->evaluateSingleton(context).as<Numeric>()->toInteger();
235
236 if(startingLoc + length < 1 || (startingLoc > (startingLoc + length)))
237 return CommonValues::emptyIterator;
238 }
239
240 /* F&O, 15.1.10, "If $startingLoc is zero or negative, the
241 * subsequence includes items from the beginning of the $sourceSeq." */
242 if(startingLoc < 1)
243 startingLoc = 1;
244
245 if(length < 1 && length != -1)
246 return CommonValues::emptyIterator;
247 return Item::Iterator::Ptr(new SubsequenceIterator(it, startingLoc, length));
248}
249
250Item SubsequenceFN::evaluateSingleton(const DynamicContext::Ptr &context) const
251{
252 return evaluateSequence(context)->next();
253}
254
255Expression::Ptr SubsequenceFN::compress(const StaticContext::Ptr &context)
256{
257 const Expression::Ptr me(FunctionCall::compress(context));
258 if(me != this)
259 return me;
260
261 const Expression::Ptr lenArg(m_operands.value(i: 2));
262 if(lenArg && lenArg->isEvaluated())
263 {
264 const xsInteger length = lenArg->as<Literal>()->item().as<Numeric>()->round()->toInteger();
265
266 if(length <= 0)
267 return EmptySequence::create(replacementFor: this, context);
268 }
269
270 return me;
271}
272
273SequenceType::Ptr SubsequenceFN::staticType() const
274{
275 const SequenceType::Ptr opType(m_operands.first()->staticType());
276 const Cardinality opCard(opType->cardinality());
277
278 /* Optimization: we can do much stronger inference here. If the length is a
279 * constant, we can constrain the range at least upwards of the
280 * cardinality, for instance. */
281
282 /* The subsequence(expr, 1, 1), add empty-sequence() to the static type.
283 *
284 * Note that we cannot do all these inferences before we've typechecked our
285 * operands. The only known case of where our staticType() is called before
286 * typeCheck() is through xmlpatternsview, although it wouldn't be
287 * surprising if the more exotic paths can achieve that too.
288 */
289 if(m_hasTypeChecked &&
290 m_operands.at(i: 1)->isEvaluated() &&
291 m_operands.count() == 3 &&
292 m_operands.at(i: 2)->isEvaluated() &&
293 m_operands.at(i: 1)->as<Literal>()->item().as<Numeric>()->round()->toInteger() == 1 &&
294 m_operands.at(i: 2)->as<Literal>()->item().as<Numeric>()->round()->toInteger() == 1)
295 {
296 return makeGenericSequenceType(itemType: opType->itemType(),
297 cardinality: opCard.toWithoutMany());
298 }
299 else
300 {
301 return makeGenericSequenceType(itemType: opType->itemType(),
302 cardinality: opCard | Cardinality::zeroOrOne());
303 }
304}
305
306Expression::Ptr DocFN::typeCheck(const StaticContext::Ptr &context,
307 const SequenceType::Ptr &reqType)
308{
309 /* See the doxygen documentation for this function for the explanation
310 * to why this implementation is here, as opposed to in
311 * qsequencegeneratingfns.cpp. */
312
313 Q_ASSERT(context);
314
315 prepareStaticBaseURI(context);
316
317 const Expression::Ptr uriOp(m_operands.first());
318
319 if(!uriOp->isEvaluated())
320 return Expression::Ptr(FunctionCall::typeCheck(context, reqType));
321
322 const Item uriItem(uriOp->evaluateSingleton(context: context->dynamicContext()));
323
324 if(!uriItem)
325 return EmptySequence::create(replacementFor: this, context)->typeCheck(context, reqType); // TODO test this
326
327 /* These two lines were previously in a separate function but are now duplicated
328 * in DocFN::evaluateSingleton(), as part of a workaround for solaris-cc-64. */
329 const QUrl mayRela(AnyURI::toQUrl<ReportContext::FODC0005>(value: uriItem.stringValue(), context, r: this));
330 const QUrl uri(context->resolveURI(relative: mayRela, baseURI: staticBaseURI()));
331
332 /* The URI is supplied statically, so, let's try to be clever. */
333 Q_ASSERT_X(context->resourceLoader(), Q_FUNC_INFO,
334 "No resource loader is set in the StaticContext.");
335 m_type = context->resourceLoader()->announceDocument(uri, usageHint: ResourceLoader::MayUse);
336
337 if(m_type)
338 {
339 Q_ASSERT(CommonSequenceTypes::ZeroOrOneDocumentNode->matches(m_type));
340 return Expression::Ptr(FunctionCall::typeCheck(context, reqType));
341 }
342 else
343 {
344 context->error(message: QtXmlPatterns::tr(sourceText: "It will not be possible to retrieve %1.").arg(a: formatURI(uri)),
345 errorCode: ReportContext::FODC0002, reflection: this);
346 return Expression::Ptr();
347 }
348}
349
350QT_END_NAMESPACE
351

source code of qtxmlpatterns/src/xmlpatterns/functions/qsequencefns.cpp