| /**************************************************************************** |
| ** |
| ** 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 "qabstractfloat_p.h" |
| #include "qatomicstring_p.h" |
| #include "qcommonsequencetypes_p.h" |
| #include "qcommonvalues_p.h" |
| #include "qinteger_p.h" |
| #include "qliteral_p.h" |
| #include "qpatternistlocale_p.h" |
| #include "qschemanumeric_p.h" |
| |
| #include "qstringvaluefns_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| using namespace QPatternist; |
| |
| Item ConcatFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Expression::List::const_iterator end(m_operands.constEnd()); |
| Expression::List::const_iterator it(m_operands.constBegin()); |
| QString result; |
| |
| for(; it != end; ++it) |
| { |
| Item item((*it)->evaluateSingleton(context)); |
| |
| if(item) |
| result += item.stringValue(); |
| } |
| |
| return AtomicString::fromValue(result); |
| } |
| |
| Item StringJoinFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context)); |
| Q_ASSERT(it); |
| Item current(it->next()); |
| |
| if(!current) /* Exit early, don't evaluate the separator. */ |
| return CommonValues::EmptyString; |
| |
| QString result; |
| QString separator; |
| const Item isep(m_operands.at(1)->evaluateSingleton(context)); |
| |
| if(isep) |
| separator = isep.stringValue(); |
| |
| while(true) |
| { |
| result += current.stringValue(); |
| current = it->next(); |
| |
| if(!current) |
| break; |
| |
| result += separator; |
| } |
| |
| return result.isEmpty() |
| ? toItem(CommonValues::EmptyString) |
| : toItem(AtomicString::fromValue(result)); |
| } |
| |
| Expression::Ptr StringJoinFN::compress(const StaticContext::Ptr &context) |
| { |
| if(m_operands.first()->staticType()->cardinality().allowsMany()) |
| return FunctionCall::compress(context); |
| else |
| { |
| if(m_operands.first()->is(IDEmptySequence)) |
| return wrapLiteral(CommonValues::EmptyString, context, this); |
| else |
| return m_operands.first()->compress(context); |
| } |
| } |
| |
| Item SubstringFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| Item item(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!item) |
| return CommonValues::EmptyString; |
| |
| const QString str(item.stringValue()); |
| |
| const xsDouble dblStart = m_operands.at(1)->evaluateSingleton(context).as<Numeric>() |
| ->round()->toDouble(); |
| if(qIsNaN(dblStart)) |
| return CommonValues::EmptyString; |
| |
| /* XPath starts from 1, but C++ starts from 0. */ |
| xsInteger startingLoc = Double::fromValue(dblStart)->round()->toInteger() - 1; |
| |
| xsInteger length = 0; |
| if(m_operands.count() == 2) |
| length = str.length() - startingLoc; |
| else |
| { |
| const xsDouble dblLen = m_operands.at(2)->evaluateSingleton(context).as<Numeric>() |
| ->round()->toDouble(); |
| |
| if(qIsNaN(dblLen)) |
| return CommonValues::EmptyString; |
| |
| length = Double::fromValue(dblLen)->round()->toInteger(); |
| if(startingLoc > startingLoc + length) |
| return CommonValues::EmptyString; |
| } |
| |
| if(startingLoc < 0) |
| { |
| length = length + startingLoc; |
| startingLoc = 0; |
| } |
| |
| return AtomicString::fromValue(str.mid(startingLoc, length)); |
| } |
| |
| Item StringLengthFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item item(m_operands.first()->evaluateSingleton(context)); |
| |
| /* fn:string() is re-implemented "inline" here. */ |
| if(item) |
| return Integer::fromValue(item.stringValue().length()); |
| else |
| return CommonValues::IntegerZero; |
| } |
| |
| NormalizeUnicodeFN::NormalizeUnicodeFN() : m_normForm(QString::NormalizationForm_C) |
| { |
| } |
| |
| Item NormalizeSpaceFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item arg(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!arg) |
| return CommonValues::EmptyString; |
| |
| return toItem(AtomicString::fromValue(arg.stringValue().simplified())); |
| } |
| |
| Item NormalizeUnicodeFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item arg(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!arg) |
| return CommonValues::EmptyString; |
| |
| int normForm; |
| |
| /* The second argument has been removed, if we've already determined the form. */ |
| if(m_operands.count() == 1) |
| normForm = m_normForm; |
| else |
| { |
| normForm = determineNormalizationForm(context); |
| if(normForm == -1) |
| return toItem(AtomicString::fromValue(arg.stringValue())); |
| } |
| |
| return AtomicString::fromValue(arg.stringValue().normalized( |
| static_cast<QString::NormalizationForm>(normForm))); |
| } |
| |
| Expression::Ptr NormalizeUnicodeFN::compress(const StaticContext::Ptr &context) |
| { |
| const Expression::Ptr me(FunctionCall::compress(context)); |
| if(me != this) |
| return me; |
| |
| Q_ASSERT(m_operands.count() == 1 || m_operands.count() == 2); |
| |
| if(m_operands.count() == 1) |
| m_normForm = QString::NormalizationForm_C; |
| else if(m_operands.last()->is(IDStringValue)) |
| { |
| m_normForm = static_cast<QString::NormalizationForm>( |
| determineNormalizationForm(context->dynamicContext())); |
| |
| if (int(m_normForm) == -1) |
| return m_operands.first(); |
| |
| /* Remove the operand since we don't need it anymore. */ |
| m_operands.removeLast(); |
| } |
| |
| return me; |
| } |
| |
| int NormalizeUnicodeFN::determineNormalizationForm(const DynamicContext::Ptr &context) const |
| { |
| const QString strRepr(m_operands.last()->evaluateSingleton(context).stringValue().trimmed().toUpper()); |
| |
| /* TODO. Put these values in a QHash for faster lookup. Keep thread safety in mind. */ |
| if(strRepr.isEmpty()) |
| return -1; |
| else if(strRepr == QLatin1String("NFC")) |
| return QString::NormalizationForm_C; |
| else if(strRepr == QLatin1String("NFD")) |
| return QString::NormalizationForm_D; |
| else if(strRepr == QLatin1String("NFKC")) |
| return QString::NormalizationForm_KC; |
| else if(strRepr == QLatin1String("NFKD")) |
| return QString::NormalizationForm_KD; |
| else |
| { |
| /* What form is FULLY_NORMALIZED? Is a code path available for that somewhere? */ |
| context->error(QtXmlPatterns::tr("The normalization form %1 is " |
| "unsupported. The supported forms are " |
| "%2, %3, %4, and %5, and none, i.e. " |
| "the empty string (no normalization).") |
| .arg(formatKeyword(strRepr)) |
| .arg(formatKeyword("NFC")) |
| .arg(formatKeyword("NFD")) |
| .arg(formatKeyword("NFKC")) |
| .arg(formatKeyword("NFKD")), |
| ReportContext::FOCH0003, |
| this); |
| return QString::NormalizationForm_C; /* Silence compiler warning. */ |
| } |
| } |
| |
| Item UpperCaseFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item item(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!item) |
| return CommonValues::EmptyString; |
| |
| return AtomicString::fromValue(item.stringValue().toUpper()); |
| } |
| |
| Item LowerCaseFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item item(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!item) |
| return CommonValues::EmptyString; |
| |
| return AtomicString::fromValue(item.stringValue().toLower()); |
| } |
| |
| Item TranslateFN::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item item(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!item) |
| return CommonValues::EmptyString; |
| |
| const QString mapString(m_operands.at(1)->evaluateSingleton(context).stringValue()); |
| const QString arg(item.stringValue()); |
| |
| if(mapString.isEmpty()) |
| return AtomicString::fromValue(arg); |
| |
| const QString transString(m_operands.at(2)->evaluateSingleton(context).stringValue()); |
| const int transLen = transString.length(); |
| const int argLen = arg.length(); |
| |
| QString result; |
| result.reserve(argLen); |
| int outI = 0; |
| |
| for(int i = 0; i < argLen; ++i) |
| { |
| const QChar argCh(arg.at(i)); |
| const int mapPos = mapString.indexOf(argCh); |
| |
| if(mapPos == -1) |
| { |
| result[outI] = argCh; |
| ++outI; |
| continue; |
| } |
| else if(mapPos >= transLen) |
| continue; |
| |
| const QChar transCh(transString.at(mapPos)); |
| |
| if(transCh.isNull()) |
| continue; |
| |
| result[outI] = transCh; |
| ++outI; |
| } |
| |
| result.truncate(outI); |
| return AtomicString::fromValue(result); |
| } |
| |
| EncodeString::EncodeString(const QByteArray &excludeChars, |
| const QByteArray &includeChars) : m_excludeChars(excludeChars), |
| m_includeChars(includeChars) |
| { |
| } |
| |
| Item EncodeString::evaluateSingleton(const DynamicContext::Ptr &context) const |
| { |
| const Item item(m_operands.first()->evaluateSingleton(context)); |
| |
| if(!item) |
| return CommonValues::EmptyString; |
| |
| const QByteArray value = item.stringValue().toUtf8().toPercentEncoding(m_excludeChars, m_includeChars); |
| return AtomicString::fromValue(QLatin1String(value)); |
| } |
| |
| const char *const EncodeForURIFN::include = "#!*'()"; |
| |
| EncodeForURIFN::EncodeForURIFN() : EncodeString(QByteArray(), QByteArray::fromRawData(include, 6)) |
| { |
| } |
| |
| const char *const IriToURIFN::exclude = "#-_!~*'();?@&=+$,[]/:%"; |
| |
| IriToURIFN::IriToURIFN() : EncodeString(QByteArray::fromRawData(exclude, 22), QByteArray()) |
| { |
| } |
| |
| const char *const EscapeHtmlURIFN::include = "?&[]%"; |
| const char *const EscapeHtmlURIFN::exclude = " :;=@!./+*()-,#$'"; |
| |
| EscapeHtmlURIFN::EscapeHtmlURIFN() : EncodeString(QByteArray::fromRawData(exclude, 17), |
| QByteArray::fromRawData(include, 6)) |
| { |
| } |
| |
| QT_END_NAMESPACE |