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 "qabstractduration_p.h" |
41 | #include "qabstractdatetime_p.h" |
42 | #include "qbase64binary_p.h" |
43 | #include "qboolean_p.h" |
44 | #include "qdynamiccontext_p.h" |
45 | #include "qqnamevalue_p.h" |
46 | |
47 | #include "qatomiccomparators_p.h" |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | using namespace QPatternist; |
52 | |
53 | /* -------------------------------------------------- */ |
54 | AtomicComparator::ComparisonResult |
55 | StringComparator::compare(const Item &o1, |
56 | const AtomicComparator::Operator, |
57 | const Item &o2) const |
58 | { |
59 | const int result = QString::compare(s1: o1.stringValue(), s2: o2.stringValue()); |
60 | |
61 | if(result > 0) |
62 | return GreaterThan; |
63 | else if(result < 0) |
64 | return LessThan; |
65 | else |
66 | { |
67 | Q_ASSERT(result == 0); |
68 | return Equal; |
69 | } |
70 | } |
71 | |
72 | bool StringComparator::equals(const Item &o1, |
73 | const Item &o2) const |
74 | { |
75 | return o1.stringValue() == o2.stringValue(); |
76 | } |
77 | /* -------------------------------------------------- */ |
78 | |
79 | /* -------------------------------------------------- */ |
80 | AtomicComparator::ComparisonResult |
81 | CaseInsensitiveStringComparator::compare(const Item &o1, |
82 | const AtomicComparator::Operator, |
83 | const Item &o2) const |
84 | { |
85 | const QString i1(o1.stringValue().toLower()); |
86 | const QString i2(o2.stringValue().toLower()); |
87 | const int result = QString::compare(s1: i1, s2: i2); |
88 | |
89 | if(result > 0) |
90 | return GreaterThan; |
91 | else if(result < 0) |
92 | return LessThan; |
93 | else |
94 | { |
95 | Q_ASSERT(result == 0); |
96 | return Equal; |
97 | } |
98 | } |
99 | |
100 | bool CaseInsensitiveStringComparator::equals(const Item &o1, |
101 | const Item &o2) const |
102 | { |
103 | const QString s1(o1.stringValue()); |
104 | const QString s2(o2.stringValue()); |
105 | |
106 | return s1.length() == s2.length() && |
107 | s1.startsWith(s: s2, cs: Qt::CaseInsensitive); |
108 | } |
109 | /* -------------------------------------------------- */ |
110 | |
111 | /* -------------------------------------------------- */ |
112 | bool BinaryDataComparator::equals(const Item &o1, |
113 | const Item &o2) const |
114 | { |
115 | return o1.as<Base64Binary>()->asByteArray() == |
116 | o2.as<Base64Binary>()->asByteArray(); |
117 | } |
118 | /* -------------------------------------------------- */ |
119 | |
120 | /* -------------------------------------------------- */ |
121 | AtomicComparator::ComparisonResult |
122 | BooleanComparator::compare(const Item &o1, |
123 | const AtomicComparator::Operator, |
124 | const Item &o2) const |
125 | { |
126 | /* We know Boolean::evaluateEBV doesn't use the DynamicContext. */ |
127 | const bool v1 = o1.as<AtomicValue>()->evaluateEBV(context: QExplicitlySharedDataPointer<DynamicContext>()); |
128 | const bool v2 = o2.as<AtomicValue>()->evaluateEBV(context: QExplicitlySharedDataPointer<DynamicContext>()); |
129 | |
130 | if(v1 == v2) |
131 | return Equal; |
132 | else if(v1 == false) |
133 | { |
134 | Q_ASSERT(v2 == true); |
135 | return LessThan; |
136 | } |
137 | else |
138 | { |
139 | Q_ASSERT(v1 == true && v2 == false); |
140 | return GreaterThan; |
141 | } |
142 | } |
143 | |
144 | bool BooleanComparator::equals(const Item &o1, |
145 | const Item &o2) const |
146 | { |
147 | /* Boolean is an atomic class. */ |
148 | return o1.as<AtomicValue>() == o2.as<AtomicValue>(); |
149 | } |
150 | /* -------------------------------------------------- */ |
151 | |
152 | /* -------------------------------------------------- */ |
153 | AtomicComparator::ComparisonResult |
154 | AbstractFloatComparator::compare(const Item &o1, |
155 | const AtomicComparator::Operator op, |
156 | const Item &o2) const |
157 | { |
158 | const xsDouble v1 = o1.as<Numeric>()->toDouble(); |
159 | const xsDouble v2 = o2.as<Numeric>()->toDouble(); |
160 | |
161 | if(Double::isEqual(a: v1, b: v2)) |
162 | return Equal; |
163 | else if(v1 < v2) |
164 | return LessThan; |
165 | else if(v1 > v2) |
166 | return GreaterThan; |
167 | else |
168 | { |
169 | /* We have NaN values. Make sure we don't return a result which would |
170 | * signify success for the operator in question. */ |
171 | if((op & OperatorGreaterThan) == OperatorGreaterThan) |
172 | return LessThan; |
173 | else |
174 | { |
175 | Q_ASSERT((op & OperatorLessThan) == OperatorLessThan); |
176 | return GreaterThan; |
177 | } |
178 | } |
179 | } |
180 | |
181 | bool AbstractFloatComparator::equals(const Item &o1, |
182 | const Item &o2) const |
183 | { |
184 | return Double::isEqual(a: o1.as<Numeric>()->toDouble(), b: o2.as<Numeric>()->toDouble()); |
185 | } |
186 | /* -------------------------------------------------- */ |
187 | |
188 | /* -------------------------------------------------- */ |
189 | AtomicComparator::ComparisonResult |
190 | DecimalComparator::compare(const Item &o1, |
191 | const AtomicComparator::Operator, |
192 | const Item &o2) const |
193 | { |
194 | const xsDecimal v1 = o1.as<Numeric>()->toDecimal(); |
195 | const xsDecimal v2 = o2.as<Numeric>()->toDecimal(); |
196 | |
197 | if(Double::isEqual(a: v1, b: v2)) |
198 | return Equal; |
199 | else if(v1 < v2) |
200 | return LessThan; |
201 | else |
202 | return GreaterThan; |
203 | } |
204 | |
205 | bool DecimalComparator::equals(const Item &o1, |
206 | const Item &o2) const |
207 | { |
208 | return Double::isEqual(a: o1.as<Numeric>()->toDecimal(), b: o2.as<Numeric>()->toDecimal()); |
209 | } |
210 | /* -------------------------------------------------- */ |
211 | |
212 | /* -------------------------------------------------- */ |
213 | AtomicComparator::ComparisonResult |
214 | IntegerComparator::compare(const Item &o1, |
215 | const AtomicComparator::Operator, |
216 | const Item &o2) const |
217 | { |
218 | const Numeric *const num1 = o1.as<Numeric>(); |
219 | const Numeric *const num2 = o2.as<Numeric>(); |
220 | |
221 | /** |
222 | * Consider: |
223 | * xs:unsignedLong("100") > xs:unsignedLong("18446744073709551615") |
224 | * |
225 | * If we perform math on the values as if they were xsInteger, the right |
226 | * operand overflows, wraps around, and the expression evaluates to false. |
227 | * Hence we have this code to deal with it. |
228 | * |
229 | * This is runtime code, it would have been better if we had separate |
230 | * AtomicComparator classes for signed and unsigned values, but the changes |
231 | * required to the lookup code are extensive. |
232 | */ |
233 | if(num1->isSigned() || num2->isSigned()) |
234 | { |
235 | const xsInteger v1 = o1.as<Numeric>()->toInteger(); |
236 | const xsInteger v2 = o2.as<Numeric>()->toInteger(); |
237 | |
238 | if(v1 == v2) |
239 | return Equal; |
240 | else if(v1 < v2) |
241 | return LessThan; |
242 | else |
243 | return GreaterThan; |
244 | } |
245 | else |
246 | { |
247 | const qulonglong v1 = o1.as<Numeric>()->toUnsignedInteger(); |
248 | const qulonglong v2 = o2.as<Numeric>()->toUnsignedInteger(); |
249 | |
250 | if(v1 == v2) |
251 | return Equal; |
252 | else if(v1 < v2) |
253 | return LessThan; |
254 | else |
255 | return GreaterThan; |
256 | } |
257 | } |
258 | |
259 | bool IntegerComparator::equals(const Item &o1, |
260 | const Item &o2) const |
261 | { |
262 | return o1.as<Numeric>()->toInteger() == o2.as<Numeric>()->toInteger(); |
263 | } |
264 | |
265 | /* -------------------------------------------------- */ |
266 | |
267 | /* -------------------------------------------------- */ |
268 | bool QNameComparator::equals(const Item &o1, |
269 | const Item &o2) const |
270 | { |
271 | return o1.as<QNameValue>()->m_qName == |
272 | o2.as<QNameValue>()->m_qName; |
273 | } |
274 | /* -------------------------------------------------- */ |
275 | |
276 | /* -------------------------------------------------- */ |
277 | bool AbstractDateTimeComparator::equals(const Item &o1, |
278 | const Item &o2) const |
279 | { |
280 | const QDateTime dt1(o1.as<AbstractDateTime>()->toDateTime()); |
281 | const QDateTime dt2(o2.as<AbstractDateTime>()->toDateTime()); |
282 | |
283 | /* |
284 | pDebug() << "COMPARING:" |
285 | << o1->as<AbstractDateTime>()->toDateTime().toString() |
286 | << o2->as<AbstractDateTime>()->toDateTime().toString(); |
287 | pDebug() << "DATE ONLY:" |
288 | << o1->as<AbstractDateTime>()->toDateTime().isDateOnly() |
289 | << o2->as<AbstractDateTime>()->toDateTime().isDateOnly(); |
290 | */ |
291 | return dt1 == dt2 && |
292 | dt1.timeSpec() == dt2.timeSpec(); |
293 | } |
294 | |
295 | AtomicComparator::ComparisonResult |
296 | AbstractDateTimeComparator::compare(const Item &operand1, |
297 | const AtomicComparator::Operator, |
298 | const Item &operand2) const |
299 | { |
300 | const QDateTime &dt1 = operand1.as<AbstractDateTime>()->toDateTime(); |
301 | const QDateTime &dt2 = operand2.as<AbstractDateTime>()->toDateTime(); |
302 | |
303 | if(dt1 == dt2) |
304 | return Equal; |
305 | else if(dt1 < dt2) |
306 | return LessThan; |
307 | else |
308 | return GreaterThan; |
309 | } |
310 | /* -------------------------------------------------- */ |
311 | |
312 | /* -------------------------------------------------- */ |
313 | bool AbstractDurationComparator::equals(const Item &o1, |
314 | const Item &o2) const |
315 | { |
316 | /* We use AbstractDuration::operator==() */ |
317 | return *o1.as<AbstractDuration>() == |
318 | *o2.as<AbstractDuration>(); |
319 | } |
320 | |
321 | QDateTime AbstractDurationComparator::addDurationToDateTime(const QDateTime &dateTime, |
322 | const AbstractDuration *const duration) |
323 | { |
324 | QDateTime result(dateTime); |
325 | qint64 seconds = 0; |
326 | |
327 | const qint8 signMultiplier = (duration->isPositive() ? 1 : -1); |
328 | |
329 | result = result.addYears(years: signMultiplier * duration->years()); |
330 | result = result.addMonths(months: signMultiplier * duration->months()); |
331 | result = result.addDays(days: signMultiplier * duration->days()); |
332 | |
333 | seconds = 60 * 60 * duration->hours(); |
334 | seconds += 60 * duration->minutes(); |
335 | seconds += duration->seconds(); |
336 | |
337 | result = result.addSecs(secs: signMultiplier * seconds); |
338 | result = result.addMSecs(msecs: signMultiplier * duration->mseconds()); |
339 | |
340 | return result; |
341 | } |
342 | |
343 | AtomicComparator::ComparisonResult |
344 | AbstractDurationComparator::compare(const Item &o1, |
345 | const AtomicComparator::Operator, |
346 | const Item &o2) const |
347 | { |
348 | const AbstractDuration *const duration = o1.as<AbstractDuration>(); |
349 | const AbstractDuration *const otherDuration = o2.as<AbstractDuration>(); |
350 | |
351 | const QDateTime dateTime1(QDate(1696, 9, 1), QTime(0, 0, 0), Qt::UTC); |
352 | const QDateTime dateTime2(QDate(1697, 2, 1), QTime(0, 0, 0), Qt::UTC); |
353 | const QDateTime dateTime3(QDate(1903, 3, 1), QTime(0, 0, 0), Qt::UTC); |
354 | const QDateTime dateTime4(QDate(1903, 7, 1), QTime(0, 0, 0), Qt::UTC); |
355 | |
356 | const QDateTime durationDateTime1 = addDurationToDateTime(dateTime: dateTime1, duration); |
357 | const QDateTime durationDateTime2 = addDurationToDateTime(dateTime: dateTime2, duration); |
358 | const QDateTime durationDateTime3 = addDurationToDateTime(dateTime: dateTime3, duration); |
359 | const QDateTime durationDateTime4 = addDurationToDateTime(dateTime: dateTime4, duration); |
360 | |
361 | const QDateTime otherDurationDateTime1 = addDurationToDateTime(dateTime: dateTime1, duration: otherDuration); |
362 | const QDateTime otherDurationDateTime2 = addDurationToDateTime(dateTime: dateTime2, duration: otherDuration); |
363 | const QDateTime otherDurationDateTime3 = addDurationToDateTime(dateTime: dateTime3, duration: otherDuration); |
364 | const QDateTime otherDurationDateTime4 = addDurationToDateTime(dateTime: dateTime4, duration: otherDuration); |
365 | |
366 | if (durationDateTime1 > otherDurationDateTime1 && |
367 | durationDateTime2 > otherDurationDateTime2 && |
368 | durationDateTime3 > otherDurationDateTime3 && |
369 | durationDateTime4 > otherDurationDateTime4) { |
370 | return GreaterThan; |
371 | } else if (durationDateTime1 < otherDurationDateTime1 && |
372 | durationDateTime2 < otherDurationDateTime2 && |
373 | durationDateTime3 < otherDurationDateTime3 && |
374 | durationDateTime4 < otherDurationDateTime4) { |
375 | return LessThan; |
376 | } else if (*duration == *otherDuration) { |
377 | return Equal; |
378 | } else { |
379 | return Incomparable; |
380 | } |
381 | } |
382 | |
383 | /* -------------------------------------------------- */ |
384 | QT_END_NAMESPACE |
385 | |