/**************************************************************************** ** ** 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() ->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() ->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(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( 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