2343737b53c95d0c7ccc29fccbadb9dd2e0569d0
[profile/ivi/qtxmlpatterns.git] / src / xmlpatterns / functions / qstringvaluefns.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qabstractfloat_p.h"
43 #include "qatomicstring_p.h"
44 #include "qcommonsequencetypes_p.h"
45 #include "qcommonvalues_p.h"
46 #include "qinteger_p.h"
47 #include "qliteral_p.h"
48 #include "qpatternistlocale_p.h"
49 #include "qschemanumeric_p.h"
50
51 #include "qstringvaluefns_p.h"
52
53 QT_BEGIN_NAMESPACE
54
55 using namespace QPatternist;
56
57 Item ConcatFN::evaluateSingleton(const DynamicContext::Ptr &context) const
58 {
59     const Expression::List::const_iterator end(m_operands.constEnd());
60     Expression::List::const_iterator it(m_operands.constBegin());
61     QString result;
62
63     for(; it != end; ++it)
64     {
65         Item item((*it)->evaluateSingleton(context));
66
67         if(item)
68             result += item.stringValue();
69     }
70
71     return AtomicString::fromValue(result);
72 }
73
74 Item StringJoinFN::evaluateSingleton(const DynamicContext::Ptr &context) const
75 {
76     Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context));
77     Q_ASSERT(it);
78     Item current(it->next());
79
80     if(!current) /* Exit early, don't evaluate the separator. */
81         return CommonValues::EmptyString;
82
83     QString result;
84     QString separator;
85     const Item isep(m_operands.at(1)->evaluateSingleton(context));
86
87     if(isep)
88         separator = isep.stringValue();
89
90     while(true)
91     {
92         result += current.stringValue();
93         current = it->next();
94
95         if(!current)
96             break;
97
98         result += separator;
99     }
100
101     return result.isEmpty()
102             ? toItem(CommonValues::EmptyString)
103             : toItem(AtomicString::fromValue(result));
104 }
105
106 Expression::Ptr StringJoinFN::compress(const StaticContext::Ptr &context)
107 {
108     if(m_operands.first()->staticType()->cardinality().allowsMany())
109         return FunctionCall::compress(context);
110     else
111     {
112         if(m_operands.first()->is(IDEmptySequence))
113             return wrapLiteral(CommonValues::EmptyString, context, this);
114         else
115             return m_operands.first()->compress(context);
116     }
117 }
118
119 Item SubstringFN::evaluateSingleton(const DynamicContext::Ptr &context) const
120 {
121     Item item(m_operands.first()->evaluateSingleton(context));
122
123     if(!item)
124         return CommonValues::EmptyString;
125
126     const QString str(item.stringValue());
127
128     const xsDouble dblStart = m_operands.at(1)->evaluateSingleton(context).as<Numeric>()
129                                         ->round()->toDouble();
130     if(qIsNaN(dblStart))
131         return CommonValues::EmptyString;
132
133     /* XPath starts from 1, but C++ starts from 0. */
134     xsInteger startingLoc = Double::fromValue(dblStart)->round()->toInteger() - 1;
135
136     xsInteger length = 0;
137     if(m_operands.count() == 2)
138         length = str.length() - startingLoc;
139     else
140     {
141         const xsDouble dblLen = m_operands.at(2)->evaluateSingleton(context).as<Numeric>()
142                 ->round()->toDouble();
143
144         if(qIsNaN(dblLen))
145             return CommonValues::EmptyString;
146
147         length = Double::fromValue(dblLen)->round()->toInteger();
148         if(startingLoc > startingLoc + length)
149             return CommonValues::EmptyString;
150     }
151
152     if(startingLoc < 0)
153     {
154         length = length + startingLoc;
155         startingLoc = 0;
156     }
157
158     return AtomicString::fromValue(str.mid(startingLoc, length));
159 }
160
161 Item StringLengthFN::evaluateSingleton(const DynamicContext::Ptr &context) const
162 {
163     const Item item(m_operands.first()->evaluateSingleton(context));
164
165     /* fn:string() is re-implemented "inline" here. */
166     if(item)
167         return Integer::fromValue(item.stringValue().length());
168     else
169         return CommonValues::IntegerZero;
170 }
171
172 NormalizeUnicodeFN::NormalizeUnicodeFN() : m_normForm(QString::NormalizationForm_C)
173 {
174 }
175
176 Item NormalizeSpaceFN::evaluateSingleton(const DynamicContext::Ptr &context) const
177 {
178     const Item arg(m_operands.first()->evaluateSingleton(context));
179
180     if(!arg)
181         return CommonValues::EmptyString;
182
183     return toItem(AtomicString::fromValue(arg.stringValue().simplified()));
184 }
185
186 Item NormalizeUnicodeFN::evaluateSingleton(const DynamicContext::Ptr &context) const
187 {
188     const Item arg(m_operands.first()->evaluateSingleton(context));
189
190     if(!arg)
191         return CommonValues::EmptyString;
192
193     int normForm;
194
195     /* The second argument has been removed, if we've already determined the form. */
196     if(m_operands.count() == 1)
197         normForm = m_normForm;
198     else
199     {
200         normForm = determineNormalizationForm(context);
201         if(normForm == -1)
202             return toItem(AtomicString::fromValue(arg.stringValue()));
203     }
204
205     return AtomicString::fromValue(arg.stringValue().normalized(
206             static_cast<QString::NormalizationForm>(normForm)));
207 }
208
209 Expression::Ptr NormalizeUnicodeFN::compress(const StaticContext::Ptr &context)
210 {
211     const Expression::Ptr me(FunctionCall::compress(context));
212     if(me != this)
213         return me;
214
215     Q_ASSERT(m_operands.count() == 1 || m_operands.count() == 2);
216
217     if(m_operands.count() == 1)
218         m_normForm = QString::NormalizationForm_C;
219     else if(m_operands.last()->is(IDStringValue))
220     {
221         m_normForm = static_cast<QString::NormalizationForm>(
222                 determineNormalizationForm(context->dynamicContext()));
223
224         if(m_normForm == -1)
225             return m_operands.first();
226
227         /* Remove the operand since we don't need it anymore. */
228         m_operands.removeLast();
229     }
230
231     return me;
232 }
233
234 int NormalizeUnicodeFN::determineNormalizationForm(const DynamicContext::Ptr &context) const
235 {
236     const QString strRepr(m_operands.last()->evaluateSingleton(context).stringValue().trimmed().toUpper());
237
238     /* TODO. Put these values in a QHash for faster lookup. Keep thread safety in mind. */
239     if(strRepr.isEmpty())
240         return -1;
241     else if(strRepr == QLatin1String("NFC"))
242         return QString::NormalizationForm_C;
243     else if(strRepr == QLatin1String("NFD"))
244         return QString::NormalizationForm_D;
245     else if(strRepr == QLatin1String("NFKC"))
246         return QString::NormalizationForm_KC;
247     else if(strRepr == QLatin1String("NFKD"))
248         return QString::NormalizationForm_KD;
249     else
250     {
251         /* What form is FULLY_NORMALIZED? Is a code path available for that somewhere? */
252         context->error(QtXmlPatterns::tr("The normalization form %1 is "
253                                          "unsupported. The supported forms are "
254                                          "%2, %3, %4, and %5, and none, i.e. "
255                                          "the empty string (no normalization).")
256                                         .arg(formatKeyword(strRepr))
257                                         .arg(formatKeyword("NFC"))
258                                         .arg(formatKeyword("NFD"))
259                                         .arg(formatKeyword("NFKC"))
260                                         .arg(formatKeyword("NFKD")),
261                                    ReportContext::FOCH0003,
262                                    this);
263         return QString::NormalizationForm_C; /* Silence compiler warning. */
264     }
265 }
266
267 Item UpperCaseFN::evaluateSingleton(const DynamicContext::Ptr &context) const
268 {
269     const Item item(m_operands.first()->evaluateSingleton(context));
270
271     if(!item)
272         return CommonValues::EmptyString;
273
274     return AtomicString::fromValue(item.stringValue().toUpper());
275 }
276
277 Item LowerCaseFN::evaluateSingleton(const DynamicContext::Ptr &context) const
278 {
279     const Item item(m_operands.first()->evaluateSingleton(context));
280
281     if(!item)
282         return CommonValues::EmptyString;
283
284     return AtomicString::fromValue(item.stringValue().toLower());
285 }
286
287 Item TranslateFN::evaluateSingleton(const DynamicContext::Ptr &context) const
288 {
289     const Item item(m_operands.first()->evaluateSingleton(context));
290
291     if(!item)
292         return CommonValues::EmptyString;
293
294     const QString mapString(m_operands.at(1)->evaluateSingleton(context).stringValue());
295     const QString arg(item.stringValue());
296
297     if(mapString.isEmpty())
298         return AtomicString::fromValue(arg);
299
300     const QString transString(m_operands.at(2)->evaluateSingleton(context).stringValue());
301     const int transLen = transString.length();
302     const int argLen = arg.length();
303
304     QString result;
305     result.reserve(argLen);
306     int outI = 0;
307
308     for(int i = 0; i < argLen; ++i)
309     {
310         const QChar argCh(arg.at(i));
311         const int mapPos = mapString.indexOf(argCh);
312
313         if(mapPos == -1)
314         {
315             result[outI] = argCh;
316             ++outI;
317             continue;
318         }
319         else if(mapPos >= transLen)
320             continue;
321
322         const QChar transCh(transString.at(mapPos));
323
324         if(transCh.isNull())
325             continue;
326
327         result[outI] = transCh;
328         ++outI;
329     }
330
331     result.truncate(outI);
332     return AtomicString::fromValue(result);
333 }
334
335 EncodeString::EncodeString(const QByteArray &excludeChars,
336                            const QByteArray &includeChars) : m_excludeChars(excludeChars),
337                                                              m_includeChars(includeChars)
338 {
339 }
340
341 Item EncodeString::evaluateSingleton(const DynamicContext::Ptr &context) const
342 {
343     const Item item(m_operands.first()->evaluateSingleton(context));
344
345     if(!item)
346         return CommonValues::EmptyString;
347
348     const QByteArray value = item.stringValue().toUtf8().toPercentEncoding(m_excludeChars, m_includeChars);
349     return AtomicString::fromValue(QLatin1String(value));
350 }
351
352 const char *const EncodeForURIFN::include = "#!*'()";
353
354 EncodeForURIFN::EncodeForURIFN() : EncodeString(QByteArray(), QByteArray::fromRawData(include, 6))
355 {
356 }
357
358 const char *const IriToURIFN::exclude = "#-_!~*'();?@&=+$,[]/:%";
359
360 IriToURIFN::IriToURIFN() : EncodeString(QByteArray::fromRawData(exclude, 22), QByteArray())
361 {
362 }
363
364 const char *const EscapeHtmlURIFN::include = "?&[]%";
365 const char *const EscapeHtmlURIFN::exclude = " :;=@!./+*()-,#$'";
366
367 EscapeHtmlURIFN::EscapeHtmlURIFN() : EncodeString(QByteArray::fromRawData(exclude, 17),
368                                                   QByteArray::fromRawData(include, 6))
369 {
370 }
371
372 QT_END_NAMESPACE