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 "qabstractfloat_p.h"
41#include "qatomicstring_p.h"
42#include "qcommonsequencetypes_p.h"
43#include "qcommonvalues_p.h"
44#include "qinteger_p.h"
45#include "qliteral_p.h"
46#include "qpatternistlocale_p.h"
47#include "qschemanumeric_p.h"
48
49#include "qstringvaluefns_p.h"
50
51QT_BEGIN_NAMESPACE
52
53using namespace QPatternist;
54
55Item ConcatFN::evaluateSingleton(const DynamicContext::Ptr &context) const
56{
57 const Expression::List::const_iterator end(m_operands.constEnd());
58 Expression::List::const_iterator it(m_operands.constBegin());
59 QString result;
60
61 for(; it != end; ++it)
62 {
63 Item item((*it)->evaluateSingleton(context));
64
65 if(item)
66 result += item.stringValue();
67 }
68
69 return AtomicString::fromValue(value: result);
70}
71
72Item StringJoinFN::evaluateSingleton(const DynamicContext::Ptr &context) const
73{
74 Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context));
75 Q_ASSERT(it);
76 Item current(it->next());
77
78 if(!current) /* Exit early, don't evaluate the separator. */
79 return CommonValues::EmptyString;
80
81 QString result;
82 QString separator;
83 const Item isep(m_operands.at(i: 1)->evaluateSingleton(context));
84
85 if(isep)
86 separator = isep.stringValue();
87
88 while(true)
89 {
90 result += current.stringValue();
91 current = it->next();
92
93 if(!current)
94 break;
95
96 result += separator;
97 }
98
99 return result.isEmpty()
100 ? toItem(atomicValue: CommonValues::EmptyString)
101 : toItem(atomicValue: AtomicString::fromValue(value: result));
102}
103
104Expression::Ptr StringJoinFN::compress(const StaticContext::Ptr &context)
105{
106 if(m_operands.first()->staticType()->cardinality().allowsMany())
107 return FunctionCall::compress(context);
108 else
109 {
110 if(m_operands.first()->is(i: IDEmptySequence))
111 return wrapLiteral(item: CommonValues::EmptyString, context, r: this);
112 else
113 return m_operands.first()->compress(context);
114 }
115}
116
117Item SubstringFN::evaluateSingleton(const DynamicContext::Ptr &context) const
118{
119 Item item(m_operands.first()->evaluateSingleton(context));
120
121 if(!item)
122 return CommonValues::EmptyString;
123
124 const QString str(item.stringValue());
125
126 const xsDouble dblStart = m_operands.at(i: 1)->evaluateSingleton(context).as<Numeric>()
127 ->round()->toDouble();
128 if(qIsNaN(d: dblStart))
129 return CommonValues::EmptyString;
130
131 /* XPath starts from 1, but C++ starts from 0. */
132 xsInteger startingLoc = Double::fromValue(num: dblStart)->round()->toInteger() - 1;
133
134 xsInteger length = 0;
135 if(m_operands.count() == 2)
136 length = str.length() - startingLoc;
137 else
138 {
139 const xsDouble dblLen = m_operands.at(i: 2)->evaluateSingleton(context).as<Numeric>()
140 ->round()->toDouble();
141
142 if(qIsNaN(d: dblLen))
143 return CommonValues::EmptyString;
144
145 length = Double::fromValue(num: dblLen)->round()->toInteger();
146 if(startingLoc > startingLoc + length)
147 return CommonValues::EmptyString;
148 }
149
150 if(startingLoc < 0)
151 {
152 length = length + startingLoc;
153 startingLoc = 0;
154 }
155
156 return AtomicString::fromValue(value: str.mid(position: startingLoc, n: length));
157}
158
159Item StringLengthFN::evaluateSingleton(const DynamicContext::Ptr &context) const
160{
161 const Item item(m_operands.first()->evaluateSingleton(context));
162
163 /* fn:string() is re-implemented "inline" here. */
164 if(item)
165 return Integer::fromValue(num: item.stringValue().length());
166 else
167 return CommonValues::IntegerZero;
168}
169
170NormalizeUnicodeFN::NormalizeUnicodeFN() : m_normForm(QString::NormalizationForm_C)
171{
172}
173
174Item NormalizeSpaceFN::evaluateSingleton(const DynamicContext::Ptr &context) const
175{
176 const Item arg(m_operands.first()->evaluateSingleton(context));
177
178 if(!arg)
179 return CommonValues::EmptyString;
180
181 return toItem(atomicValue: AtomicString::fromValue(value: arg.stringValue().simplified()));
182}
183
184Item NormalizeUnicodeFN::evaluateSingleton(const DynamicContext::Ptr &context) const
185{
186 const Item arg(m_operands.first()->evaluateSingleton(context));
187
188 if(!arg)
189 return CommonValues::EmptyString;
190
191 int normForm;
192
193 /* The second argument has been removed, if we've already determined the form. */
194 if(m_operands.count() == 1)
195 normForm = m_normForm;
196 else
197 {
198 normForm = determineNormalizationForm(context);
199 if(normForm == -1)
200 return toItem(atomicValue: AtomicString::fromValue(value: arg.stringValue()));
201 }
202
203 return AtomicString::fromValue(value: arg.stringValue().normalized(
204 mode: static_cast<QString::NormalizationForm>(normForm)));
205}
206
207Expression::Ptr NormalizeUnicodeFN::compress(const StaticContext::Ptr &context)
208{
209 const Expression::Ptr me(FunctionCall::compress(context));
210 if(me != this)
211 return me;
212
213 Q_ASSERT(m_operands.count() == 1 || m_operands.count() == 2);
214
215 if(m_operands.count() == 1)
216 m_normForm = QString::NormalizationForm_C;
217 else if(m_operands.last()->is(i: IDStringValue))
218 {
219 m_normForm = static_cast<QString::NormalizationForm>(
220 determineNormalizationForm(context: context->dynamicContext()));
221
222 if (int(m_normForm) == -1)
223 return m_operands.first();
224
225 /* Remove the operand since we don't need it anymore. */
226 m_operands.removeLast();
227 }
228
229 return me;
230}
231
232int NormalizeUnicodeFN::determineNormalizationForm(const DynamicContext::Ptr &context) const
233{
234 const QString strRepr(m_operands.last()->evaluateSingleton(context).stringValue().trimmed().toUpper());
235
236 /* TODO. Put these values in a QHash for faster lookup. Keep thread safety in mind. */
237 if(strRepr.isEmpty())
238 return -1;
239 else if(strRepr == QLatin1String("NFC"))
240 return QString::NormalizationForm_C;
241 else if(strRepr == QLatin1String("NFD"))
242 return QString::NormalizationForm_D;
243 else if(strRepr == QLatin1String("NFKC"))
244 return QString::NormalizationForm_KC;
245 else if(strRepr == QLatin1String("NFKD"))
246 return QString::NormalizationForm_KD;
247 else
248 {
249 /* What form is FULLY_NORMALIZED? Is a code path available for that somewhere? */
250 context->error(message: QtXmlPatterns::tr(sourceText: "The normalization form %1 is "
251 "unsupported. The supported forms are "
252 "%2, %3, %4, and %5, and none, i.e. "
253 "the empty string (no normalization).")
254 .arg(a: formatKeyword(keyword: strRepr))
255 .arg(a: formatKeyword(keyword: "NFC"))
256 .arg(a: formatKeyword(keyword: "NFD"))
257 .arg(a: formatKeyword(keyword: "NFKC"))
258 .arg(a: formatKeyword(keyword: "NFKD")),
259 errorCode: ReportContext::FOCH0003,
260 reflection: this);
261 return QString::NormalizationForm_C; /* Silence compiler warning. */
262 }
263}
264
265Item UpperCaseFN::evaluateSingleton(const DynamicContext::Ptr &context) const
266{
267 const Item item(m_operands.first()->evaluateSingleton(context));
268
269 if(!item)
270 return CommonValues::EmptyString;
271
272 return AtomicString::fromValue(value: item.stringValue().toUpper());
273}
274
275Item LowerCaseFN::evaluateSingleton(const DynamicContext::Ptr &context) const
276{
277 const Item item(m_operands.first()->evaluateSingleton(context));
278
279 if(!item)
280 return CommonValues::EmptyString;
281
282 return AtomicString::fromValue(value: item.stringValue().toLower());
283}
284
285Item TranslateFN::evaluateSingleton(const DynamicContext::Ptr &context) const
286{
287 const Item item(m_operands.first()->evaluateSingleton(context));
288
289 if(!item)
290 return CommonValues::EmptyString;
291
292 const QString mapString(m_operands.at(i: 1)->evaluateSingleton(context).stringValue());
293 const QString arg(item.stringValue());
294
295 if(mapString.isEmpty())
296 return AtomicString::fromValue(value: arg);
297
298 const QString transString(m_operands.at(i: 2)->evaluateSingleton(context).stringValue());
299 const int transLen = transString.length();
300 const int argLen = arg.length();
301
302 QString result;
303 result.reserve(asize: argLen);
304 int outI = 0;
305
306 for(int i = 0; i < argLen; ++i)
307 {
308 const QChar argCh(arg.at(i));
309 const int mapPos = mapString.indexOf(c: argCh);
310
311 if(mapPos == -1)
312 {
313 result[outI] = argCh;
314 ++outI;
315 continue;
316 }
317 else if(mapPos >= transLen)
318 continue;
319
320 const QChar transCh(transString.at(i: mapPos));
321
322 if(transCh.isNull())
323 continue;
324
325 result[outI] = transCh;
326 ++outI;
327 }
328
329 result.truncate(pos: outI);
330 return AtomicString::fromValue(value: result);
331}
332
333EncodeString::EncodeString(const QByteArray &excludeChars,
334 const QByteArray &includeChars) : m_excludeChars(excludeChars),
335 m_includeChars(includeChars)
336{
337}
338
339Item EncodeString::evaluateSingleton(const DynamicContext::Ptr &context) const
340{
341 const Item item(m_operands.first()->evaluateSingleton(context));
342
343 if(!item)
344 return CommonValues::EmptyString;
345
346 const QByteArray value = item.stringValue().toUtf8().toPercentEncoding(exclude: m_excludeChars, include: m_includeChars);
347 return AtomicString::fromValue(value: QLatin1String(value));
348}
349
350const char *const EncodeForURIFN::include = "#!*'()";
351
352EncodeForURIFN::EncodeForURIFN() : EncodeString(QByteArray(), QByteArray::fromRawData(include, size: 6))
353{
354}
355
356const char *const IriToURIFN::exclude = "#-_!~*'();?@&=+$,[]/:%";
357
358IriToURIFN::IriToURIFN() : EncodeString(QByteArray::fromRawData(exclude, size: 22), QByteArray())
359{
360}
361
362const char *const EscapeHtmlURIFN::include = "?&[]%";
363const char *const EscapeHtmlURIFN::exclude = " :;=@!./+*()-,#$'";
364
365EscapeHtmlURIFN::EscapeHtmlURIFN() : EncodeString(QByteArray::fromRawData(exclude, size: 17),
366 QByteArray::fromRawData(include, size: 6))
367{
368}
369
370QT_END_NAMESPACE
371

source code of qtxmlpatterns/src/xmlpatterns/functions/qstringvaluefns.cpp