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

source code of qtxmlpatterns/src/xmlpatterns/expr/qgeneralcomparison.cpp