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
54QT_BEGIN_NAMESPACE
55
56using namespace QPatternist;
57
58/* The translation strings is place here once, in order to reduce work for translators,
59 * and provide consistency. */
60
61static 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
68static 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
75static 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
82Item 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
132Item 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
181Item 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
265Item 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
289OperandSwitcherMathematician::
290OperandSwitcherMathematician(const AtomicMathematician::Ptr &mathematician) : m_mather(mathematician)
291{
292 Q_ASSERT(mathematician);
293}
294
295Item 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
304Item 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
336Item 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
350QT_END_NAMESPACE
351

source code of qtxmlpatterns/src/xmlpatterns/data/qatomicmathematicians.cpp