src/xmlpatterns/expr/qarithmeticexpression.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    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