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 <math.h> |
41 | |
42 | #include <qnumeric.h> |
43 | |
44 | #include "qabstractdatetime_p.h" |
45 | #include "qabstractduration_p.h" |
46 | #include "qabstractfloat_p.h" |
47 | #include "qdaytimeduration_p.h" |
48 | #include "qdecimal_p.h" |
49 | #include "qinteger_p.h" |
50 | #include "qpatternistlocale_p.h" |
51 | |
52 | #include "qatomicmathematicians_p.h" |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | using namespace QPatternist; |
57 | |
58 | /* The translation strings is place here once, in order to reduce work for translators, |
59 | * and provide consistency. */ |
60 | |
61 | static inline QString idivZeroInvalid() |
62 | { |
63 | return QtXmlPatterns::tr(sourceText: "Integer division (%1) by zero (%2) is undefined." ) |
64 | .arg(a: formatKeyword(keyword: "idiv" )) |
65 | .arg(a: formatData(data: "0" )); |
66 | } |
67 | |
68 | static inline QString divZeroInvalid() |
69 | { |
70 | return QtXmlPatterns::tr(sourceText: "Division (%1) by zero (%2) is undefined." ) |
71 | .arg(a: formatKeyword(keyword: "div" )) |
72 | .arg(a: formatData(data: "0" )); |
73 | } |
74 | |
75 | static inline QString modZeroInvalid() |
76 | { |
77 | return QtXmlPatterns::tr(sourceText: "Modulus division (%1) by zero (%2) is undefined." ) |
78 | .arg(a: formatKeyword(keyword: "mod" )) |
79 | .arg(a: formatData(data: "0" )); |
80 | } |
81 | |
82 | Item DecimalMathematician::calculate(const Item &o1, |
83 | const Operator op, |
84 | const Item &o2, |
85 | const QExplicitlySharedDataPointer<DynamicContext> &context) const |
86 | { |
87 | switch(op) |
88 | { |
89 | case Div: |
90 | { |
91 | if(o2.as<Numeric>()->toInteger() == 0) |
92 | { |
93 | context->error(message: divZeroInvalid(), errorCode: ReportContext::FOAR0001, reflection: this); |
94 | return Item(); /* Silences source code analyzer warning. */ |
95 | } |
96 | else |
97 | return toItem(atomicValue: Decimal::fromValue(num: o1.as<Numeric>()->toDecimal() / o2.as<Numeric>()->toDecimal())); |
98 | } |
99 | case IDiv: |
100 | { |
101 | if(o2.as<Numeric>()->toInteger() == 0) |
102 | { |
103 | context->error(message: idivZeroInvalid(), errorCode: ReportContext::FOAR0001, reflection: this); |
104 | return Item(); /* Silences source code analyzer warning. */ |
105 | } |
106 | else |
107 | return Integer::fromValue(num: static_cast<xsInteger>(o1.as<Numeric>()->toDecimal() / |
108 | o2.as<Numeric>()->toDecimal())); |
109 | } |
110 | case Substract: |
111 | return toItem(atomicValue: Decimal::fromValue(num: o1.as<Numeric>()->toDecimal() - o2.as<Numeric>()->toDecimal())); |
112 | case Mod: |
113 | { |
114 | if(o2.as<Numeric>()->toInteger() == 0) |
115 | { |
116 | context->error(message: modZeroInvalid(), errorCode: ReportContext::FOAR0001, reflection: this); |
117 | return Item(); /* Silences source code analyzer warning. */ |
118 | } |
119 | else |
120 | return toItem(atomicValue: Decimal::fromValue(num: ::fmod(x: o1.as<Numeric>()->toDecimal(), y: o2.as<Numeric>()->toDecimal()))); |
121 | } |
122 | case Multiply: |
123 | return toItem(atomicValue: Decimal::fromValue(num: o1.as<Numeric>()->toDecimal() * o2.as<Numeric>()->toDecimal())); |
124 | case Add: |
125 | return toItem(atomicValue: Decimal::fromValue(num: o1.as<Numeric>()->toDecimal() + o2.as<Numeric>()->toDecimal())); |
126 | } |
127 | |
128 | Q_ASSERT(false); |
129 | return Item(); /* GCC unbarfer. */ |
130 | } |
131 | |
132 | Item IntegerMathematician::calculate(const Item &o1, |
133 | const Operator op, |
134 | const Item &o2, |
135 | const QExplicitlySharedDataPointer<DynamicContext> &context) const |
136 | { |
137 | switch(op) |
138 | { |
139 | case Div: |
140 | if(o2.as<Numeric>()->toInteger() == 0) |
141 | { |
142 | context->error(message: divZeroInvalid(), errorCode: ReportContext::FOAR0001, reflection: this); |
143 | return Item(); /* Silences source code analyzer warning. */ |
144 | } |
145 | else /* C++ automatically performs truncation of long integer(xsInteger). */ |
146 | return toItem(atomicValue: Decimal::fromValue(num: o1.as<Numeric>()->toDecimal() / o2.as<Numeric>()->toDecimal())); |
147 | case IDiv: |
148 | { |
149 | if(o2.as<Numeric>()->toInteger() == 0) |
150 | { |
151 | context->error(message: idivZeroInvalid(), errorCode: ReportContext::FOAR0001, reflection: this); |
152 | return Item(); /* Silences source code analyzer warning. */ |
153 | } |
154 | else /* C++ automatically performs truncation of long integer(xsInteger). */ |
155 | return Integer::fromValue(num: o1.as<Numeric>()->toInteger() / o2.as<Numeric>()->toInteger()); |
156 | } |
157 | case Substract: |
158 | return Integer::fromValue(num: o1.as<Numeric>()->toInteger() - o2.as<Numeric>()->toInteger()); |
159 | case Mod: |
160 | { |
161 | const xsInteger divisor = o2.as<Numeric>()->toInteger(); |
162 | |
163 | if(divisor == 0) |
164 | { |
165 | context->error(message: modZeroInvalid(), errorCode: ReportContext::FOAR0001, reflection: this); |
166 | return Item(); /* Silences source code analyzer warning. */ |
167 | } |
168 | else |
169 | return Integer::fromValue(num: o1.as<Numeric>()->toInteger() % divisor); |
170 | } |
171 | case Multiply: |
172 | return Integer::fromValue(num: o1.as<Numeric>()->toInteger() * o2.as<Numeric>()->toInteger()); |
173 | case Add: |
174 | return Integer::fromValue(num: o1.as<Numeric>()->toInteger() + o2.as<Numeric>()->toInteger()); |
175 | } |
176 | |
177 | Q_ASSERT(false); |
178 | return Item(); /* GCC unbarfer. */ |
179 | } |
180 | |
181 | Item DurationNumericMathematician::calculate(const Item &o1, |
182 | const Operator op, |
183 | const Item &o2, |
184 | const QExplicitlySharedDataPointer<DynamicContext> &context) const |
185 | { |
186 | Q_ASSERT(op == Div || op == Multiply); |
187 | |
188 | const AbstractDuration::Ptr duration(o1.as<AbstractDuration>()); |
189 | const xsDouble dbl = o2.as<Numeric>()->toDouble(); |
190 | |
191 | switch(op) |
192 | { |
193 | case Div: |
194 | { |
195 | if(qIsInf(d: dbl)) |
196 | return duration->fromValue(val: 0); |
197 | else if(qIsNaN(d: dbl)) |
198 | { |
199 | context->error(message: QtXmlPatterns::tr( |
200 | sourceText: "Dividing a value of type %1 by %2 (not-a-number) " |
201 | "is not allowed." ) |
202 | .arg(a: formatType(np: context->namePool(), |
203 | type: duration->type())) |
204 | .arg(a: formatData(data: "NaN" )), |
205 | errorCode: ReportContext::FOCA0005, |
206 | reflection: this); |
207 | return Item(); |
208 | } |
209 | else if(Double::isEqual(a: dbl, b: 0)) |
210 | { |
211 | context->error(message: QtXmlPatterns::tr( |
212 | sourceText: "Dividing a value of type %1 by %2 or %3 (plus or " |
213 | "minus zero) is not allowed." ) |
214 | .arg(a: formatType(np: context->namePool(), |
215 | type: duration->type())) |
216 | .arg(a: formatData(data: "-0" )) |
217 | .arg(a: formatData(data: "0" )), |
218 | errorCode: ReportContext::FODT0002, |
219 | reflection: this); |
220 | return Item(); |
221 | } |
222 | |
223 | return duration->fromValue(val: static_cast<AbstractDuration::Value>(duration->value() / dbl)); |
224 | } |
225 | case Multiply: |
226 | { |
227 | if(Double::isEqual(a: dbl, b: 0)) |
228 | return duration->fromValue(val: 0); |
229 | else if(qIsNaN(d: dbl)) |
230 | { |
231 | context->error(message: QtXmlPatterns::tr( |
232 | sourceText: "Dividing a value of type %1 by %2 (not-a-number) " |
233 | "is not allowed." ) |
234 | .arg(a: formatType(np: context->namePool(), |
235 | type: duration->type())) |
236 | .arg(a: formatData(data: "NaN" )), |
237 | errorCode: ReportContext::FOCA0005, |
238 | reflection: this); |
239 | return Item(); |
240 | } |
241 | else if(qIsInf(d: dbl)) |
242 | { |
243 | context->error(message: QtXmlPatterns::tr( |
244 | sourceText: "Multiplication of a value of type %1 by %2 or %3 " |
245 | "(plus or minus infinity) is not allowed." ) |
246 | .arg(a: formatType(np: context->namePool(), |
247 | type: duration->type())) |
248 | .arg(a: formatData(data: "-INF" )) |
249 | .arg(a: formatData(data: "INF" )), |
250 | errorCode: ReportContext::FODT0002, |
251 | reflection: this); |
252 | return Item(); |
253 | } |
254 | |
255 | return duration->fromValue(val: static_cast<AbstractDuration::Value>(duration->value() * dbl)); |
256 | } |
257 | default: |
258 | { |
259 | Q_ASSERT(false); |
260 | return Item(); /* Silence warning. */ |
261 | } |
262 | } |
263 | } |
264 | |
265 | Item DurationDurationMathematician::calculate(const Item &o1, |
266 | const Operator op, |
267 | const Item &o2, |
268 | const QExplicitlySharedDataPointer<DynamicContext> &) const |
269 | { |
270 | const AbstractDuration::Ptr duration(o1.as<AbstractDuration>()); |
271 | const AbstractDuration::Value op2 = o2.as<AbstractDuration>()->value(); |
272 | |
273 | switch(op) |
274 | { |
275 | case Div: |
276 | return toItem(atomicValue: Decimal::fromValue(num: static_cast<xsDecimal>(duration->value()) / op2)); |
277 | case Substract: |
278 | return duration->fromValue(val: duration->value() - op2); |
279 | case Add: |
280 | return duration->fromValue(val: duration->value() + op2); |
281 | default: |
282 | { |
283 | Q_ASSERT(false); |
284 | return Item(); /* Silence warning. */ |
285 | } |
286 | } |
287 | } |
288 | |
289 | OperandSwitcherMathematician:: |
290 | OperandSwitcherMathematician(const AtomicMathematician::Ptr &mathematician) : m_mather(mathematician) |
291 | { |
292 | Q_ASSERT(mathematician); |
293 | } |
294 | |
295 | Item OperandSwitcherMathematician::calculate(const Item &o1, |
296 | const Operator op, |
297 | const Item &o2, |
298 | const QExplicitlySharedDataPointer<DynamicContext> &context) const |
299 | { |
300 | return m_mather->calculate(operand1: o2, op, operand2: o1, context); |
301 | } |
302 | |
303 | |
304 | Item DateTimeDurationMathematician::calculate(const Item &o1, |
305 | const Operator op, |
306 | const Item &o2, |
307 | const QExplicitlySharedDataPointer<DynamicContext> &context) const |
308 | { |
309 | Q_ASSERT(op == Substract || op == Add); |
310 | |
311 | const AbstractDateTime::Ptr adt(o1.as<AbstractDateTime>()); |
312 | const AbstractDuration::Ptr dur(o2.as<AbstractDuration>()); |
313 | QDateTime dt(adt->toDateTime()); |
314 | //pDebug() << "DateTimeDurationMathematician::calculate():" << dt.toString(); |
315 | //dt.setDateOnly(false); |
316 | const qint8 sign = (op == Add ? 1 : -1) * (dur->isPositive() ? 1 : -1); |
317 | |
318 | // TODO milli seconds |
319 | dt = dt.addSecs(secs: sign * (dur->seconds() + dur->minutes() * 60 + dur->hours() * 60 * 60)); |
320 | dt = dt.addDays(days: sign * dur->days()); |
321 | dt = dt.addMonths(months: sign * dur->months()); |
322 | dt = dt.addYears(years: sign * dur->years()); |
323 | |
324 | QString msg; |
325 | |
326 | if(AbstractDateTime::isRangeValid(date: dt.date(), message&: msg)) |
327 | return adt->fromValue(dt); |
328 | else |
329 | { |
330 | context->error(message: msg, errorCode: ReportContext::FODT0001, |
331 | reflection: this); |
332 | return Item(); |
333 | } |
334 | } |
335 | |
336 | Item AbstractDateTimeMathematician::calculate(const Item &o1, |
337 | const Operator op, |
338 | const Item &o2, |
339 | const QExplicitlySharedDataPointer<DynamicContext> &) const |
340 | { |
341 | Q_ASSERT(op == Substract || op == Add); |
342 | QDateTime dt1(o1.as<AbstractDateTime>()->toDateTime()); |
343 | QDateTime dt2(o2.as<AbstractDateTime>()->toDateTime()); |
344 | |
345 | const int diff = op == Add ? dt1.secsTo(dt2) : dt2.secsTo(dt1); |
346 | |
347 | return toItem(atomicValue: DayTimeDuration::fromSeconds(secs: diff)); |
348 | } |
349 | |
350 | QT_END_NAMESPACE |
351 | |