034ecab6d3998d588d5759a8656868b6b769f6e4
[profile/ivi/qtxmlpatterns.git] / src / xmlpatterns / expr / qarithmeticexpression.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
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
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
66 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:
97     DelegatingReflectionExpression(const Item &item,
98                                    const SourceLocationReflection *const reflection) : Literal(item)
99                                                                                      , m_reflection(reflection)
100     {
101     }
102
103     virtual const SourceLocationReflection *actualReflection() const
104     {
105         return m_reflection;
106     }
107
108 private:
109     const SourceLocationReflection *const m_reflection;
110 };
111
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
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
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
233 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
350 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
358 ExpressionVisitorResult::Ptr ArithmeticExpression::accept(const ExpressionVisitor::Ptr &visitor) const
359 {
360     return visitor->visit(this);
361 }
362
363 QT_END_NAMESPACE