| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtXmlPatterns module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <math.h> |
| |
| #include <qnumeric.h> |
| |
| #include "qabstractdatetime_p.h" |
| #include "qabstractduration_p.h" |
| #include "qabstractfloat_p.h" |
| #include "qdaytimeduration_p.h" |
| #include "qdecimal_p.h" |
| #include "qinteger_p.h" |
| #include "qpatternistlocale_p.h" |
| |
| #include "qatomicmathematicians_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| using namespace QPatternist; |
| |
| /* The translation strings is place here once, in order to reduce work for translators, |
| * and provide consistency. */ |
| |
| static inline QString idivZeroInvalid() |
| { |
| return QtXmlPatterns::tr("Integer division (%1) by zero (%2) is undefined.") |
| .arg(formatKeyword("idiv")) |
| .arg(formatData("0")); |
| } |
| |
| static inline QString divZeroInvalid() |
| { |
| return QtXmlPatterns::tr("Division (%1) by zero (%2) is undefined.") |
| .arg(formatKeyword("div")) |
| .arg(formatData("0")); |
| } |
| |
| static inline QString modZeroInvalid() |
| { |
| return QtXmlPatterns::tr("Modulus division (%1) by zero (%2) is undefined.") |
| .arg(formatKeyword("mod")) |
| .arg(formatData("0")); |
| } |
| |
| Item DecimalMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &context) const |
| { |
| switch(op) |
| { |
| case Div: |
| { |
| if(o2.as<Numeric>()->toInteger() == 0) |
| { |
| context->error(divZeroInvalid(), ReportContext::FOAR0001, this); |
| return Item(); /* Silences source code analyzer warning. */ |
| } |
| else |
| return toItem(Decimal::fromValue(o1.as<Numeric>()->toDecimal() / o2.as<Numeric>()->toDecimal())); |
| } |
| case IDiv: |
| { |
| if(o2.as<Numeric>()->toInteger() == 0) |
| { |
| context->error(idivZeroInvalid(), ReportContext::FOAR0001, this); |
| return Item(); /* Silences source code analyzer warning. */ |
| } |
| else |
| return Integer::fromValue(static_cast<xsInteger>(o1.as<Numeric>()->toDecimal() / |
| o2.as<Numeric>()->toDecimal())); |
| } |
| case Substract: |
| return toItem(Decimal::fromValue(o1.as<Numeric>()->toDecimal() - o2.as<Numeric>()->toDecimal())); |
| case Mod: |
| { |
| if(o2.as<Numeric>()->toInteger() == 0) |
| { |
| context->error(modZeroInvalid(), ReportContext::FOAR0001, this); |
| return Item(); /* Silences source code analyzer warning. */ |
| } |
| else |
| return toItem(Decimal::fromValue(::fmod(o1.as<Numeric>()->toDecimal(), o2.as<Numeric>()->toDecimal()))); |
| } |
| case Multiply: |
| return toItem(Decimal::fromValue(o1.as<Numeric>()->toDecimal() * o2.as<Numeric>()->toDecimal())); |
| case Add: |
| return toItem(Decimal::fromValue(o1.as<Numeric>()->toDecimal() + o2.as<Numeric>()->toDecimal())); |
| } |
| |
| Q_ASSERT(false); |
| return Item(); /* GCC unbarfer. */ |
| } |
| |
| Item IntegerMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &context) const |
| { |
| switch(op) |
| { |
| case Div: |
| if(o2.as<Numeric>()->toInteger() == 0) |
| { |
| context->error(divZeroInvalid(), ReportContext::FOAR0001, this); |
| return Item(); /* Silences source code analyzer warning. */ |
| } |
| else /* C++ automatically performs truncation of long integer(xsInteger). */ |
| return toItem(Decimal::fromValue(o1.as<Numeric>()->toDecimal() / o2.as<Numeric>()->toDecimal())); |
| case IDiv: |
| { |
| if(o2.as<Numeric>()->toInteger() == 0) |
| { |
| context->error(idivZeroInvalid(), ReportContext::FOAR0001, this); |
| return Item(); /* Silences source code analyzer warning. */ |
| } |
| else /* C++ automatically performs truncation of long integer(xsInteger). */ |
| return Integer::fromValue(o1.as<Numeric>()->toInteger() / o2.as<Numeric>()->toInteger()); |
| } |
| case Substract: |
| return Integer::fromValue(o1.as<Numeric>()->toInteger() - o2.as<Numeric>()->toInteger()); |
| case Mod: |
| { |
| const xsInteger divisor = o2.as<Numeric>()->toInteger(); |
| |
| if(divisor == 0) |
| { |
| context->error(modZeroInvalid(), ReportContext::FOAR0001, this); |
| return Item(); /* Silences source code analyzer warning. */ |
| } |
| else |
| return Integer::fromValue(o1.as<Numeric>()->toInteger() % divisor); |
| } |
| case Multiply: |
| return Integer::fromValue(o1.as<Numeric>()->toInteger() * o2.as<Numeric>()->toInteger()); |
| case Add: |
| return Integer::fromValue(o1.as<Numeric>()->toInteger() + o2.as<Numeric>()->toInteger()); |
| } |
| |
| Q_ASSERT(false); |
| return Item(); /* GCC unbarfer. */ |
| } |
| |
| Item DurationNumericMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &context) const |
| { |
| Q_ASSERT(op == Div || op == Multiply); |
| |
| const AbstractDuration::Ptr duration(o1.as<AbstractDuration>()); |
| const xsDouble dbl = o2.as<Numeric>()->toDouble(); |
| |
| switch(op) |
| { |
| case Div: |
| { |
| if(qIsInf(dbl)) |
| return duration->fromValue(0); |
| else if(qIsNaN(dbl)) |
| { |
| context->error(QtXmlPatterns::tr( |
| "Dividing a value of type %1 by %2 (not-a-number) " |
| "is not allowed.") |
| .arg(formatType(context->namePool(), |
| duration->type())) |
| .arg(formatData("NaN")), |
| ReportContext::FOCA0005, |
| this); |
| return Item(); |
| } |
| else if(Double::isEqual(dbl, 0)) |
| { |
| context->error(QtXmlPatterns::tr( |
| "Dividing a value of type %1 by %2 or %3 (plus or " |
| "minus zero) is not allowed.") |
| .arg(formatType(context->namePool(), |
| duration->type())) |
| .arg(formatData("-0")) |
| .arg(formatData("0")), |
| ReportContext::FODT0002, |
| this); |
| return Item(); |
| } |
| |
| return duration->fromValue(static_cast<AbstractDuration::Value>(duration->value() / dbl)); |
| } |
| case Multiply: |
| { |
| if(Double::isEqual(dbl, 0)) |
| return duration->fromValue(0); |
| else if(qIsNaN(dbl)) |
| { |
| context->error(QtXmlPatterns::tr( |
| "Dividing a value of type %1 by %2 (not-a-number) " |
| "is not allowed.") |
| .arg(formatType(context->namePool(), |
| duration->type())) |
| .arg(formatData("NaN")), |
| ReportContext::FOCA0005, |
| this); |
| return Item(); |
| } |
| else if(qIsInf(dbl)) |
| { |
| context->error(QtXmlPatterns::tr( |
| "Multiplication of a value of type %1 by %2 or %3 " |
| "(plus or minus infinity) is not allowed.") |
| .arg(formatType(context->namePool(), |
| duration->type())) |
| .arg(formatData("-INF")) |
| .arg(formatData("INF")), |
| ReportContext::FODT0002, |
| this); |
| return Item(); |
| } |
| |
| return duration->fromValue(static_cast<AbstractDuration::Value>(duration->value() * dbl)); |
| } |
| default: |
| { |
| Q_ASSERT(false); |
| return Item(); /* Silence warning. */ |
| } |
| } |
| } |
| |
| Item DurationDurationMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &) const |
| { |
| const AbstractDuration::Ptr duration(o1.as<AbstractDuration>()); |
| const AbstractDuration::Value op2 = o2.as<AbstractDuration>()->value(); |
| |
| switch(op) |
| { |
| case Div: |
| return toItem(Decimal::fromValue(static_cast<xsDecimal>(duration->value()) / op2)); |
| case Substract: |
| return duration->fromValue(duration->value() - op2); |
| case Add: |
| return duration->fromValue(duration->value() + op2); |
| default: |
| { |
| Q_ASSERT(false); |
| return Item(); /* Silence warning. */ |
| } |
| } |
| } |
| |
| OperandSwitcherMathematician:: |
| OperandSwitcherMathematician(const AtomicMathematician::Ptr &mathematician) : m_mather(mathematician) |
| { |
| Q_ASSERT(mathematician); |
| } |
| |
| Item OperandSwitcherMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &context) const |
| { |
| return m_mather->calculate(o2, op, o1, context); |
| } |
| |
| |
| Item DateTimeDurationMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &context) const |
| { |
| Q_ASSERT(op == Substract || op == Add); |
| |
| const AbstractDateTime::Ptr adt(o1.as<AbstractDateTime>()); |
| const AbstractDuration::Ptr dur(o2.as<AbstractDuration>()); |
| QDateTime dt(adt->toDateTime()); |
| //pDebug() << "DateTimeDurationMathematician::calculate():" << dt.toString(); |
| //dt.setDateOnly(false); |
| const qint8 sign = (op == Add ? 1 : -1) * (dur->isPositive() ? 1 : -1); |
| |
| // TODO milli seconds |
| dt = dt.addSecs(sign * (dur->seconds() + dur->minutes() * 60 + dur->hours() * 60 * 60)); |
| dt = dt.addDays(sign * dur->days()); |
| dt = dt.addMonths(sign * dur->months()); |
| dt = dt.addYears(sign * dur->years()); |
| |
| QString msg; |
| |
| if(AbstractDateTime::isRangeValid(dt.date(), msg)) |
| return adt->fromValue(dt); |
| else |
| { |
| context->error(msg, ReportContext::FODT0001, |
| this); |
| return Item(); |
| } |
| } |
| |
| Item AbstractDateTimeMathematician::calculate(const Item &o1, |
| const Operator op, |
| const Item &o2, |
| const QExplicitlySharedDataPointer<DynamicContext> &) const |
| { |
| Q_ASSERT(op == Substract || op == Add); |
| QDateTime dt1(o1.as<AbstractDateTime>()->toDateTime()); |
| QDateTime dt2(o2.as<AbstractDateTime>()->toDateTime()); |
| |
| const int diff = op == Add ? dt1.secsTo(dt2) : dt2.secsTo(dt1); |
| |
| return toItem(DayTimeDuration::fromSeconds(diff)); |
| } |
| |
| QT_END_NAMESPACE |