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