4747a2cc193124b37cbb40c7ef62a6dc38d9216d
[profile/ivi/qtxmlpatterns.git] / src / xmlpatterns / expr / qgeneralcomparison.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 "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