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 "qboolean_p.h"
41#include "qbuiltintypes_p.h"
42#include "qcommonsequencetypes_p.h"
43#include "qemptysequence_p.h"
44#include "qgenericsequencetype_p.h"
45#include "qliteral_p.h"
46#include "qpatternistlocale_p.h"
47#include "qschemanumeric_p.h"
48#include "quntypedatomicconverter_p.h"
49
50#include "qarithmeticexpression_p.h"
51
52QT_BEGIN_NAMESPACE
53
54using namespace QPatternist;
55
56ArithmeticExpression::ArithmeticExpression(const Expression::Ptr &op1,
57 const AtomicMathematician::Operator op,
58 const Expression::Ptr &op2) : PairContainer(op1, op2)
59 , m_op(op)
60 , m_isCompat(false)
61{
62}
63
64Item ArithmeticExpression::evaluateSingleton(const DynamicContext::Ptr &context) const
65{
66 const Item op1(m_operand1->evaluateSingleton(context));
67 if(!op1)
68 return Item();
69
70 const Item op2(m_operand2->evaluateSingleton(context));
71 if(!op2)
72 return Item();
73
74 return flexiblyCalculate(op1, op: m_op, op2, mather: m_mather, context, reflection: this,
75 code: ReportContext::XPTY0004, isCompat: m_isCompat);
76}
77
78/**
79 * Since ArithmeticExpression::flexiblyCalculate() creates Expression instances
80 * at runtime, we have the problem of having SourceLocationReflections for them
81 * in the case that we run into a runtime error, since the locations are always
82 * located at compile time.
83 *
84 * This class simply delegates the reflection over to an existing expression.
85 *
86 * I only managed to trigger this with "current() + 1", where current()
87 * evaluates to an invalid representation for @c xs:double.
88 *
89 * @since 4.5
90 * @author Frans Englich <frans.englich@nokia.com>
91 */
92class DelegatingReflectionExpression : public Literal
93{
94public:
95 DelegatingReflectionExpression(const Item &item,
96 const SourceLocationReflection *const reflection) : Literal(item)
97 , m_reflection(reflection)
98 {
99 }
100
101 virtual const SourceLocationReflection *actualReflection() const
102 {
103 return m_reflection;
104 }
105
106private:
107 const SourceLocationReflection *const m_reflection;
108};
109
110Item ArithmeticExpression::flexiblyCalculate(const Item &op1,
111 const AtomicMathematician::Operator op,
112 const Item &op2,
113 const AtomicMathematician::Ptr &mather,
114 const DynamicContext::Ptr &context,
115 const SourceLocationReflection *const reflection,
116 const ReportContext::ErrorCode code,
117 const bool isCompat)
118{
119 if(mather)
120 return mather->calculate(operand1: op1, op, operand2: op2, context);
121
122 /* This is a very heavy code path. */
123 Expression::Ptr a1(new DelegatingReflectionExpression(op1, reflection));
124 Expression::Ptr a2(new DelegatingReflectionExpression(op2, reflection));
125
126 const AtomicMathematician::Ptr ingela(fetchMathematician(t1&: a1, t2&: a2, op, issueError: true, context, reflection, code, isCompat));
127
128 return ingela->calculate(operand1: a1->evaluateSingleton(context),
129 op,
130 operand2: a2->evaluateSingleton(context),
131 context);
132}
133
134Expression::Ptr ArithmeticExpression::typeCheck(const StaticContext::Ptr &context,
135 const SequenceType::Ptr &reqType)
136{
137 m_isCompat = context->compatModeEnabled();
138
139 const Expression::Ptr me(PairContainer::typeCheck(context, reqType));
140 const ItemType::Ptr t1(m_operand1->staticType()->itemType());
141 const ItemType::Ptr t2(m_operand2->staticType()->itemType());
142
143 if(*CommonSequenceTypes::Empty == *t1 ||
144 *CommonSequenceTypes::Empty == *t2)
145 {
146 return EmptySequence::create(replacementFor: this, context);
147 }
148
149 if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
150 *BuiltinTypes::xsAnyAtomicType == *t2 ||
151 *BuiltinTypes::numeric == *t1 ||
152 *BuiltinTypes::numeric == *t2)
153 {
154 /* The static type of (at least) one of the operands could not
155 * be narrowed further than xs:anyAtomicType, so we do the operator
156 * lookup at runtime. */
157 return me;
158 }
159
160 m_mather = fetchMathematician(t1&: m_operand1, t2&: m_operand2, op: m_op, issueError: true, context, reflection: this,
161 code: ReportContext::XPTY0004, isCompat: m_isCompat);
162
163 return me;
164}
165
166AtomicMathematician::Ptr
167ArithmeticExpression::fetchMathematician(Expression::Ptr &op1,
168 Expression::Ptr &op2,
169 const AtomicMathematician::Operator op,
170 const bool issueError,
171 const ReportContext::Ptr &context,
172 const SourceLocationReflection *const reflection,
173 const ReportContext::ErrorCode code,
174 const bool isCompat)
175{
176 ItemType::Ptr t1(op1->staticType()->itemType());
177 ItemType::Ptr t2(op2->staticType()->itemType());
178
179 if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: t1)
180 || (isCompat && (BuiltinTypes::xsString->xdtTypeMatches(other: t1)
181 || BuiltinTypes::xsDecimal->xdtTypeMatches(other: t1))))
182 {
183 op1 = Expression::Ptr(new UntypedAtomicConverter(op1, BuiltinTypes::xsDouble));
184 /* The types might have changed, reload. */
185 t1 = op1->staticType()->itemType();
186 }
187
188 if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: t2)
189 || (isCompat && (BuiltinTypes::xsString->xdtTypeMatches(other: t1)
190 || BuiltinTypes::xsDecimal->xdtTypeMatches(other: t1))))
191 {
192 op2 = Expression::Ptr(new UntypedAtomicConverter(op2, BuiltinTypes::xsDouble));
193 /* The types might have changed, reload. */
194 t2 = op2->staticType()->itemType();
195 }
196
197 const AtomicMathematicianLocator::Ptr locator
198 (static_cast<const AtomicType *>(t1.data())->mathematicianLocator());
199
200 if(!locator)
201 {
202 if(!issueError)
203 return AtomicMathematician::Ptr();
204
205 context->error(message: QtXmlPatterns::tr(
206 sourceText: "Operator %1 cannot be used on type %2.")
207 .arg(a: formatKeyword(keyword: AtomicMathematician::displayName(op)))
208 .arg(a: formatType(np: context->namePool(), type: t1)),
209 errorCode: code, reflection);
210 return AtomicMathematician::Ptr();
211 }
212
213 const AtomicMathematician::Ptr comp
214 (static_cast<const AtomicType *>(t2.data())->accept(visitor: locator, param: op, reflection));
215
216 if(comp)
217 return comp;
218
219 if(!issueError)
220 return AtomicMathematician::Ptr();
221
222 context->error(message: QtXmlPatterns::tr(sourceText: "Operator %1 cannot be used on "
223 "atomic values of type %2 and %3.")
224 .arg(a: formatKeyword(keyword: AtomicMathematician::displayName(op)))
225 .arg(a: formatType(np: context->namePool(), type: t1))
226 .arg(a: formatType(np: context->namePool(), type: t2)),
227 errorCode: code, reflection);
228 return AtomicMathematician::Ptr();
229}
230
231SequenceType::Ptr ArithmeticExpression::staticType() const
232{
233 Cardinality card;
234
235 /* These variables are important because they ensure staticType() only
236 * gets called once from this function. Before, this lead to strange
237 * semi-infinite recursion involving many arithmetic expressions. */
238 const SequenceType::Ptr st1(m_operand1->staticType());
239 const SequenceType::Ptr st2(m_operand2->staticType());
240
241 if(st1->cardinality().allowsEmpty() ||
242 st2->cardinality().allowsEmpty())
243 {
244 card = Cardinality::zeroOrOne();
245 }
246 else
247 card = Cardinality::exactlyOne();
248
249 if(m_op == AtomicMathematician::IDiv)
250 return makeGenericSequenceType(itemType: BuiltinTypes::xsInteger, cardinality: card);
251
252 const ItemType::Ptr t1(st1->itemType());
253 const ItemType::Ptr t2(st2->itemType());
254 ItemType::Ptr returnType;
255
256 /* Please, make this beautiful? */
257 if(BuiltinTypes::xsTime->xdtTypeMatches(other: t1) ||
258 BuiltinTypes::xsDate->xdtTypeMatches(other: t1) ||
259 BuiltinTypes::xsDateTime->xdtTypeMatches(other: t1))
260 {
261 if(BuiltinTypes::xsDuration->xdtTypeMatches(other: t2))
262 returnType = t1;
263 else
264 returnType = BuiltinTypes::xsDayTimeDuration;
265 }
266 else if(BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(other: t1))
267 {
268 if(m_op == AtomicMathematician::Div &&
269 BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(other: t2))
270 {
271 returnType = BuiltinTypes::xsDecimal;
272 }
273 else if(BuiltinTypes::numeric->xdtTypeMatches(other: t2))
274 returnType = BuiltinTypes::xsYearMonthDuration;
275 else
276 returnType = t2;
277 }
278 else if(BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(other: t2))
279 {
280 returnType = BuiltinTypes::xsYearMonthDuration;
281 }
282 else if(BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(other: t1))
283 {
284 if(m_op == AtomicMathematician::Div &&
285 BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(other: t2))
286 {
287 returnType = BuiltinTypes::xsDecimal;
288 }
289 else if(BuiltinTypes::numeric->xdtTypeMatches(other: t2))
290 returnType = BuiltinTypes::xsDayTimeDuration;
291 else
292 returnType = t2;
293 }
294 else if(BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(other: t2))
295 {
296 returnType = BuiltinTypes::xsDayTimeDuration;
297 }
298 else if(BuiltinTypes::xsDouble->xdtTypeMatches(other: t1) ||
299 BuiltinTypes::xsDouble->xdtTypeMatches(other: t2))
300 {
301 returnType = BuiltinTypes::xsDouble;
302 }
303 else if(BuiltinTypes::xsFloat->xdtTypeMatches(other: t1) ||
304 BuiltinTypes::xsFloat->xdtTypeMatches(other: t2))
305 {
306 if(m_isCompat)
307 returnType = BuiltinTypes::xsFloat;
308 else
309 returnType = BuiltinTypes::xsDouble;
310 }
311 else if(BuiltinTypes::xsInteger->xdtTypeMatches(other: t1) &&
312 BuiltinTypes::xsInteger->xdtTypeMatches(other: t2))
313 {
314 if(m_isCompat)
315 returnType = BuiltinTypes::xsDouble;
316 else
317 {
318 /* "A div B numeric numeric op:numeric-divide(A, B)
319 * numeric; but xs:decimal if both operands are xs:integer" */
320 if(m_op == AtomicMathematician::Div)
321 returnType = BuiltinTypes::xsDecimal;
322 else
323 returnType = BuiltinTypes::xsInteger;
324 }
325 }
326 else if(m_isCompat && (BuiltinTypes::xsInteger->xdtTypeMatches(other: t1) &&
327 BuiltinTypes::xsInteger->xdtTypeMatches(other: t2)))
328 {
329 returnType = BuiltinTypes::xsDouble;
330 }
331 else
332 {
333 /* If typeCheck() has been called, our operands conform to expectedOperandTypes(), and
334 * the types are hence either xs:decimals, or xs:anyAtomicType(meaning the static type could
335 * not be inferred), or empty-sequence(). So we use the union of the two types. The combinations
336 * could also be wrong.*/
337 returnType = t1 | t2;
338
339 /* However, if we're called before typeCheck(), we could potentially have nodes, so we need to make
340 * sure that the type is at least atomic. */
341 if(!BuiltinTypes::xsAnyAtomicType->xdtTypeMatches(other: returnType))
342 returnType = BuiltinTypes::xsAnyAtomicType;
343 }
344
345 return makeGenericSequenceType(itemType: returnType, cardinality: card);
346}
347
348SequenceType::List ArithmeticExpression::expectedOperandTypes() const
349{
350 SequenceType::List result;
351 result.append(t: CommonSequenceTypes::ZeroOrOneAtomicType);
352 result.append(t: CommonSequenceTypes::ZeroOrOneAtomicType);
353 return result;
354}
355
356ExpressionVisitorResult::Ptr ArithmeticExpression::accept(const ExpressionVisitor::Ptr &visitor) const
357{
358 return visitor->visit(this);
359}
360
361QT_END_NAMESPACE
362

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