/**************************************************************************** ** ** 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 "qabstractduration_p.h" #include "qabstractdatetime_p.h" #include "qbase64binary_p.h" #include "qboolean_p.h" #include "qdynamiccontext_p.h" #include "qqnamevalue_p.h" #include "qatomiccomparators_p.h" QT_BEGIN_NAMESPACE using namespace QPatternist; /* -------------------------------------------------- */ AtomicComparator::ComparisonResult StringComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { const int result = QString::compare(o1.stringValue(), o2.stringValue()); if(result > 0) return GreaterThan; else if(result < 0) return LessThan; else { Q_ASSERT(result == 0); return Equal; } } bool StringComparator::equals(const Item &o1, const Item &o2) const { return o1.stringValue() == o2.stringValue(); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ AtomicComparator::ComparisonResult CaseInsensitiveStringComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { const QString i1(o1.stringValue().toLower()); const QString i2(o2.stringValue().toLower()); const int result = QString::compare(i1, i2); if(result > 0) return GreaterThan; else if(result < 0) return LessThan; else { Q_ASSERT(result == 0); return Equal; } } bool CaseInsensitiveStringComparator::equals(const Item &o1, const Item &o2) const { const QString s1(o1.stringValue()); const QString s2(o2.stringValue()); return s1.length() == s2.length() && s1.startsWith(s2, Qt::CaseInsensitive); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ bool BinaryDataComparator::equals(const Item &o1, const Item &o2) const { return o1.as()->asByteArray() == o2.as()->asByteArray(); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ AtomicComparator::ComparisonResult BooleanComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { /* We know Boolean::evaluateEBV doesn't use the DynamicContext. */ const bool v1 = o1.as()->evaluateEBV(QExplicitlySharedDataPointer()); const bool v2 = o2.as()->evaluateEBV(QExplicitlySharedDataPointer()); if(v1 == v2) return Equal; else if(v1 == false) { Q_ASSERT(v2 == true); return LessThan; } else { Q_ASSERT(v1 == true && v2 == false); return GreaterThan; } } bool BooleanComparator::equals(const Item &o1, const Item &o2) const { /* Boolean is an atomic class. */ return o1.as() == o2.as(); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ AtomicComparator::ComparisonResult AbstractFloatComparator::compare(const Item &o1, const AtomicComparator::Operator op, const Item &o2) const { const xsDouble v1 = o1.as()->toDouble(); const xsDouble v2 = o2.as()->toDouble(); if(Double::isEqual(v1, v2)) return Equal; else if(v1 < v2) return LessThan; else if(v1 > v2) return GreaterThan; else { /* We have NaN values. Make sure we don't return a result which would * signify success for the operator in question. */ if((op & OperatorGreaterThan) == OperatorGreaterThan) return LessThan; else { Q_ASSERT((op & OperatorLessThan) == OperatorLessThan); return GreaterThan; } } } bool AbstractFloatComparator::equals(const Item &o1, const Item &o2) const { return Double::isEqual(o1.as()->toDouble(), o2.as()->toDouble()); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ AtomicComparator::ComparisonResult DecimalComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { const xsDecimal v1 = o1.as()->toDecimal(); const xsDecimal v2 = o2.as()->toDecimal(); if(Double::isEqual(v1, v2)) return Equal; else if(v1 < v2) return LessThan; else return GreaterThan; } bool DecimalComparator::equals(const Item &o1, const Item &o2) const { return Double::isEqual(o1.as()->toDecimal(), o2.as()->toDecimal()); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ AtomicComparator::ComparisonResult IntegerComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { const Numeric *const num1 = o1.as(); const Numeric *const num2 = o2.as(); /** * Consider: * xs:unsignedLong("100") > xs:unsignedLong("18446744073709551615") * * If we perform math on the values as if they were xsInteger, the right * operand overflows, wraps around, and the expression evaluates to false. * Hence we have this code to deal with it. * * This is runtime code, it would have been better if we had separate * AtomicComparator classes for signed and unsigned values, but the changes * required to the lookup code are extensive. */ if(num1->isSigned() || num2->isSigned()) { const xsInteger v1 = o1.as()->toInteger(); const xsInteger v2 = o2.as()->toInteger(); if(v1 == v2) return Equal; else if(v1 < v2) return LessThan; else return GreaterThan; } else { const qulonglong v1 = o1.as()->toUnsignedInteger(); const qulonglong v2 = o2.as()->toUnsignedInteger(); if(v1 == v2) return Equal; else if(v1 < v2) return LessThan; else return GreaterThan; } } bool IntegerComparator::equals(const Item &o1, const Item &o2) const { return o1.as()->toInteger() == o2.as()->toInteger(); } /* -------------------------------------------------- */ /* -------------------------------------------------- */ bool QNameComparator::equals(const Item &o1, const Item &o2) const { return o1.as()->m_qName == o2.as()->m_qName; } /* -------------------------------------------------- */ /* -------------------------------------------------- */ bool AbstractDateTimeComparator::equals(const Item &o1, const Item &o2) const { const QDateTime dt1(o1.as()->toDateTime()); const QDateTime dt2(o2.as()->toDateTime()); /* pDebug() << "COMPARING:" << o1->as()->toDateTime().toString() << o2->as()->toDateTime().toString(); pDebug() << "DATE ONLY:" << o1->as()->toDateTime().isDateOnly() << o2->as()->toDateTime().isDateOnly(); */ return dt1 == dt2 && dt1.timeSpec() == dt2.timeSpec(); } AtomicComparator::ComparisonResult AbstractDateTimeComparator::compare(const Item &operand1, const AtomicComparator::Operator, const Item &operand2) const { const QDateTime &dt1 = operand1.as()->toDateTime(); const QDateTime &dt2 = operand2.as()->toDateTime(); if(dt1 == dt2) return Equal; else if(dt1 < dt2) return LessThan; else return GreaterThan; } /* -------------------------------------------------- */ /* -------------------------------------------------- */ bool AbstractDurationComparator::equals(const Item &o1, const Item &o2) const { /* We use AbstractDuration::operator==() */ return *o1.as() == *o2.as(); } QDateTime AbstractDurationComparator::addDurationToDateTime(const QDateTime &dateTime, const AbstractDuration *const duration) { QDateTime result(dateTime); qint64 seconds = 0; const qint8 signMultiplier = (duration->isPositive() ? 1 : -1); result = result.addYears(signMultiplier * duration->years()); result = result.addMonths(signMultiplier * duration->months()); result = result.addDays(signMultiplier * duration->days()); seconds = 60 * 60 * duration->hours(); seconds += 60 * duration->minutes(); seconds += duration->seconds(); result = result.addSecs(signMultiplier * seconds); result = result.addMSecs(signMultiplier * duration->mseconds()); return result; } AtomicComparator::ComparisonResult AbstractDurationComparator::compare(const Item &o1, const AtomicComparator::Operator, const Item &o2) const { const AbstractDuration *const duration = o1.as(); const AbstractDuration *const otherDuration = o2.as(); const QDateTime dateTime1(QDate(1696, 9, 1), QTime(0, 0, 0), Qt::UTC); const QDateTime dateTime2(QDate(1697, 2, 1), QTime(0, 0, 0), Qt::UTC); const QDateTime dateTime3(QDate(1903, 3, 1), QTime(0, 0, 0), Qt::UTC); const QDateTime dateTime4(QDate(1903, 7, 1), QTime(0, 0, 0), Qt::UTC); const QDateTime durationDateTime1 = addDurationToDateTime(dateTime1, duration); const QDateTime durationDateTime2 = addDurationToDateTime(dateTime2, duration); const QDateTime durationDateTime3 = addDurationToDateTime(dateTime3, duration); const QDateTime durationDateTime4 = addDurationToDateTime(dateTime4, duration); const QDateTime otherDurationDateTime1 = addDurationToDateTime(dateTime1, otherDuration); const QDateTime otherDurationDateTime2 = addDurationToDateTime(dateTime2, otherDuration); const QDateTime otherDurationDateTime3 = addDurationToDateTime(dateTime3, otherDuration); const QDateTime otherDurationDateTime4 = addDurationToDateTime(dateTime4, otherDuration); if (durationDateTime1 > otherDurationDateTime1 && durationDateTime2 > otherDurationDateTime2 && durationDateTime3 > otherDurationDateTime3 && durationDateTime4 > otherDurationDateTime4) { return GreaterThan; } else if (durationDateTime1 < otherDurationDateTime1 && durationDateTime2 < otherDurationDateTime2 && durationDateTime3 < otherDurationDateTime3 && durationDateTime4 < otherDurationDateTime4) { return LessThan; } else if (*duration == *otherDuration) { return Equal; } else { return Incomparable; } } /* -------------------------------------------------- */ QT_END_NAMESPACE