src/xmlpatterns/expr/qgeneralcomparison.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 "qbuiltintypes_p.h"
       
    43 #include "qcommonsequencetypes_p.h"
       
    44 #include "qcommonvalues_p.h"
       
    45 #include "qebvextractor_p.h"
       
    46 #include "qitem_p.h"
       
    47 #include "qliteral_p.h"
       
    48 #include "qoptimizationpasses_p.h"
       
    49 #include "quntypedatomicconverter_p.h"
       
    50 #include "qvaluecomparison_p.h"
       
    51 
       
    52 #include "qgeneralcomparison_p.h"
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 using namespace QPatternist;
       
    57 
       
    58 GeneralComparison::GeneralComparison(const Expression::Ptr &op1,
       
    59                                      const AtomicComparator::Operator op,
       
    60                                      const Expression::Ptr &op2,
       
    61                                      const bool isBackwardsCompat) : PairContainer(op1, op2)
       
    62                                                                    , m_operator(op)
       
    63                                                                    , m_isBackwardsCompat(isBackwardsCompat)
       
    64 {
       
    65 }
       
    66 
       
    67 bool GeneralComparison::generalCompare(const Item &op1,
       
    68                                        const Item &op2,
       
    69                                        const DynamicContext::Ptr &context) const
       
    70 {
       
    71     Q_ASSERT(op1);
       
    72     Q_ASSERT(op2);
       
    73 
       
    74     if(comparator())
       
    75         return compare(op1, op2, comparator(), m_operator);
       
    76 
       
    77     Expression::Ptr a1(new Literal(op1));
       
    78     Expression::Ptr a2(new Literal(op2));
       
    79 
       
    80     const AtomicComparator::Ptr comp(fetchGeneralComparator(a1, a2, context));
       
    81     /* The fetchGeneralComparator call may rewrite a1 and/or a2. */
       
    82     Q_ASSERT(a1);
       
    83     Q_ASSERT(a2);
       
    84     Q_ASSERT(comp);
       
    85 
       
    86     return compare(a1->evaluateSingleton(context),
       
    87                    a2->evaluateSingleton(context),
       
    88                    comp,
       
    89                    m_operator);
       
    90 }
       
    91 
       
    92 bool GeneralComparison::evaluateEBV(const DynamicContext::Ptr &context) const
       
    93 {
       
    94     const Item::Iterator::Ptr it1(m_operand1->evaluateSequence(context));
       
    95     Item item1(it1->next());
       
    96 
       
    97     if(!item1)
       
    98         return false;
       
    99 
       
   100     const Item::Iterator::Ptr it2(m_operand2->evaluateSequence(context));
       
   101     Item::List cache;
       
   102     Item item2;
       
   103 
       
   104     while(true)
       
   105     {
       
   106         item2 = it2->next();
       
   107         if(!item2)
       
   108             break;
       
   109 
       
   110         if(generalCompare(item1, item2, context))
       
   111             return true;
       
   112 
       
   113         cache.append(item2);
       
   114     }
       
   115 
       
   116     while(true)
       
   117     {
       
   118         item1 = it1->next();
       
   119 
       
   120         if(!item1)
       
   121             return false;
       
   122 
       
   123         const Item::List::const_iterator end(cache.constEnd());
       
   124         Item::List::const_iterator it(cache.constBegin());
       
   125 
       
   126         for(; it != end; ++it)
       
   127             if(generalCompare(item1, *it, context))
       
   128                 return true;
       
   129     }
       
   130 
       
   131     Q_ASSERT(false);
       
   132     return false;
       
   133 }
       
   134 
       
   135 Expression::Ptr GeneralComparison::compress(const StaticContext::Ptr &context)
       
   136 {
       
   137     const Expression::Ptr me(PairContainer::compress(context));
       
   138 
       
   139     if(me != this)
       
   140         return me;
       
   141 
       
   142     if(ValueComparison::isCaseInsensitiveCompare(m_operand1, m_operand2))
       
   143         useCaseInsensitiveComparator();
       
   144 
       
   145     return me;
       
   146 }
       
   147 
       
   148 Expression::Ptr GeneralComparison::typeCheck(const StaticContext::Ptr &context,
       
   149                                              const SequenceType::Ptr &reqType)
       
   150 {
       
   151 
       
   152     const Expression::Ptr me(PairContainer::typeCheck(context, reqType));
       
   153 
       
   154     const ItemType::Ptr t1(m_operand1->staticType()->itemType());
       
   155     const ItemType::Ptr t2(m_operand2->staticType()->itemType());
       
   156 
       
   157     if(*CommonSequenceTypes::Empty == *t1 ||
       
   158        *CommonSequenceTypes::Empty == *t2)
       
   159     {
       
   160         return wrapLiteral(CommonValues::BooleanFalse, context, this);
       
   161     }
       
   162 
       
   163     if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
       
   164        *BuiltinTypes::xsAnyAtomicType == *t2)
       
   165         return me;
       
   166 
       
   167     prepareComparison(fetchGeneralComparator(m_operand1, m_operand2, context));
       
   168 
       
   169     if(!m_operand1->staticType()->cardinality().allowsMany() &&
       
   170        !m_operand2->staticType()->cardinality().allowsMany())
       
   171     {
       
   172         /* Rewrite to a ValueComparison whose operands uses typing rules
       
   173          * as for an general comparison(that's what's done above). */
       
   174         return rewrite(Expression::Ptr(new ValueComparison(m_operand1,
       
   175                                                            m_operator,
       
   176                                                            m_operand2))->typeCheck(context, reqType),
       
   177                        context);
       
   178     }
       
   179     else
       
   180         return me;
       
   181 }
       
   182 
       
   183 void GeneralComparison::updateType(ItemType::Ptr &type,
       
   184                                    const Expression::Ptr &source)
       
   185 {
       
   186     type = source->staticType()->itemType();
       
   187 }
       
   188 
       
   189 AtomicComparator::Ptr GeneralComparison::fetchGeneralComparator(Expression::Ptr &op1,
       
   190                                                                 Expression::Ptr &op2,
       
   191                                                                 const ReportContext::Ptr &context) const
       
   192 {
       
   193     ItemType::Ptr t1(op1->staticType()->itemType());
       
   194     ItemType::Ptr t2(op2->staticType()->itemType());
       
   195 
       
   196     /* a. "If one of the atomic values is an instance of xs:untypedAtomic and
       
   197      *    the other is an instance of a numeric type, then the xs:untypedAtomic
       
   198      *    value is cast to the type xs:double." */
       
   199     if(BuiltinTypes::numeric->xdtTypeMatches(t1) &&
       
   200        BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t2))
       
   201     {
       
   202         op2 = Expression::Ptr(new UntypedAtomicConverter(op2, BuiltinTypes::xsDouble));
       
   203 
       
   204         /* The types might have changed, reload. */
       
   205         updateType(t2, op2);
       
   206     }
       
   207     else if(BuiltinTypes::numeric->xdtTypeMatches(t2) &&
       
   208             BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1))
       
   209     {
       
   210         op1 = Expression::Ptr(new UntypedAtomicConverter(op1, BuiltinTypes::xsDouble));
       
   211 
       
   212         /* The types might have changed, reload. */
       
   213         updateType(t1, op1);
       
   214     }
       
   215     /* "If XPath 1.0 compatibility mode is true, a general comparison is
       
   216      *  evaluated by applying the following rules, in order:
       
   217      *  1. If either operand is a single atomic value that is an instance of
       
   218      *  xs:boolean, then the other operand is converted to xs:boolean by taking
       
   219      *  its effective boolean value."
       
   220      *
       
   221      * Notably, it's not conversion to boolean, it is EBV extraction.
       
   222      */
       
   223     else if(m_isBackwardsCompat && BuiltinTypes::xsBoolean->xdtTypeMatches(t1))
       
   224     {
       
   225         op2 = Expression::Ptr(new EBVExtractor(op2));
       
   226         updateType(t2, op2);
       
   227     }
       
   228     else if(m_isBackwardsCompat && BuiltinTypes::xsBoolean->xdtTypeMatches(t2))
       
   229     {
       
   230         op1 = Expression::Ptr(new EBVExtractor(op1));
       
   231         updateType(t1, op1);
       
   232     }
       
   233     /* b. "If one of the atomic values is an instance of xs:untypedAtomic and
       
   234      *    the other is an instance of xs:untypedAtomic or xs:string, then the
       
   235      *    xs:untypedAtomic value (or values) is (are) cast to the type xs:string."
       
   236      *
       
   237      * c. "If one of the atomic values is an instance of xs:untypedAtomic and the
       
   238      *    other is not an instance of xs:string, xs:untypedAtomic, or any numeric
       
   239      *    type, then the xs:untypedAtomic value is cast to the dynamic type of the
       
   240      *    other value." */
       
   241     else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1) &&
       
   242             !BuiltinTypes::xsString->xdtTypeMatches(t2) &&
       
   243             !BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t2) &&
       
   244             !BuiltinTypes::xsAnyURI->xdtTypeMatches(t2))
       
   245     {
       
   246         op1 = Expression::Ptr(new UntypedAtomicConverter(op1, t2));
       
   247         updateType(t1, op1);
       
   248     }
       
   249     else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t2) &&
       
   250             !BuiltinTypes::xsString->xdtTypeMatches(t1) &&
       
   251             !BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1) &&
       
   252             !BuiltinTypes::xsAnyURI->xdtTypeMatches(t1))
       
   253     {
       
   254         op2 = Expression::Ptr(new UntypedAtomicConverter(op2, t1));
       
   255         updateType(t2, op2);
       
   256     }
       
   257 
       
   258     /* d. "After performing the conversions described above, the atomic
       
   259      *     values are compared using one of the value comparison operators
       
   260      *     eq, ne, lt, le, gt, or ge, depending on whether the general comparison
       
   261      *     operator was =, !=, <, <=, >, or >=. The values have the required
       
   262      *     magnitude relationship if and only if the result of this value comparison
       
   263      *     is true." */
       
   264 
       
   265     return fetchComparator(t1, t2, context);
       
   266 }
       
   267 
       
   268 OptimizationPass::List GeneralComparison::optimizationPasses() const
       
   269 {
       
   270     Q_ASSERT(!OptimizationPasses::comparisonPasses.isEmpty());
       
   271     return OptimizationPasses::comparisonPasses;
       
   272 }
       
   273 
       
   274 SequenceType::List GeneralComparison::expectedOperandTypes() const
       
   275 {
       
   276     SequenceType::List result;
       
   277     result.append(CommonSequenceTypes::ZeroOrMoreAtomicTypes);
       
   278     result.append(CommonSequenceTypes::ZeroOrMoreAtomicTypes);
       
   279     return result;
       
   280 }
       
   281 
       
   282 SequenceType::Ptr GeneralComparison::staticType() const
       
   283 {
       
   284     return CommonSequenceTypes::ExactlyOneBoolean;
       
   285 }
       
   286 
       
   287 ExpressionVisitorResult::Ptr GeneralComparison::accept(const ExpressionVisitor::Ptr &visitor) const
       
   288 {
       
   289     return visitor->visit(this);
       
   290 }
       
   291 
       
   292 Expression::ID GeneralComparison::id() const
       
   293 {
       
   294     return IDGeneralComparison;
       
   295 }
       
   296 
       
   297 QT_END_NAMESPACE