source: trunk/src/xmlpatterns/functions/qsequencegeneratingfns.cpp@ 5

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 9.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QStack>
43#include <QStringList>
44
45#include "qanyuri_p.h"
46#include "qboolean_p.h"
47#include "qcommonsequencetypes_p.h"
48#include "qcommonvalues_p.h"
49#include "qemptysequence_p.h"
50#include "qitemmappingiterator_p.h"
51#include "qnodesort_p.h"
52#include "qpatternistlocale_p.h"
53#include "private/qxmlutils_p.h"
54
55#include "qsequencegeneratingfns_p.h"
56
57QT_BEGIN_NAMESPACE
58
59using namespace QPatternist;
60
61IdFN::IdFN() : m_hasCreatedSorter(false)
62{
63}
64
65Item IdFN::mapToItem(const QString &id,
66 const IDContext &context) const
67{
68 return context.second->elementById(context.first->namePool()->allocateQName(QString(), id));
69}
70
71/**
72 * @short Helper class for StringSplitter
73 *
74 * Needed by the QAbstractXmlForwardIterator sub-class.
75 *
76 * @relates StringSplitter
77 */
78template<>
79bool qIsForwardIteratorEnd(const QString &unit)
80{
81 return unit.isNull();
82}
83
84/**
85 * @short Helper class for IdFN.
86 *
87 * StringSplitter takes an Iterator which delivers strings of this kind:
88 *
89 * "a", "b c", "%invalidNCName", " ", "d"
90 *
91 * and we deliver instead:
92 *
93 * "a", "b", "c", "d"
94 *
95 * That is, we:
96 * - Remove invalid @c NCName
97 * - Split IDREFs into individual NCNames
98 *
99 * @author Frans Englich
100 */
101class StringSplitter : public QAbstractXmlForwardIterator<QString>
102{
103public:
104 StringSplitter(const Item::Iterator::Ptr &source);
105 virtual QString next();
106 virtual QString current() const;
107 virtual qint64 position() const;
108private:
109 QString loadNext();
110 const Item::Iterator::Ptr m_source;
111 QStack<QString> m_buffer;
112 QString m_current;
113 qint64 m_position;
114 bool m_sourceAtEnd;
115};
116
117StringSplitter::StringSplitter(const Item::Iterator::Ptr &source) : m_source(source)
118 , m_position(0)
119 , m_sourceAtEnd(false)
120{
121 Q_ASSERT(m_source);
122 m_buffer.push(loadNext());
123}
124
125QString StringSplitter::next()
126{
127 /* We also check m_position, we want to load on our first run. */
128 if(!m_buffer.isEmpty())
129 {
130 ++m_position;
131 m_current = m_buffer.pop();
132 return m_current;
133 }
134 else if(m_sourceAtEnd)
135 {
136 m_current.clear();
137 m_position = -1;
138 return QString();
139 }
140
141 return loadNext();
142}
143
144QString StringSplitter::loadNext()
145{
146 const Item sourceNext(m_source->next());
147
148 if(sourceNext.isNull())
149 {
150 m_sourceAtEnd = true;
151 /* We might have strings in m_buffer, let's empty it. */
152 return next();
153 }
154
155 const QStringList candidates(sourceNext.stringValue().simplified().split(QLatin1Char(' ')));
156 const int count = candidates.length();
157
158 for(int i = 0; i < count; ++i)
159 {
160 const QString &at = candidates.at(i);
161
162 if(QXmlUtils::isNCName(at))
163 m_buffer.push(at);
164 }
165
166 /* So, now we have populated m_buffer, let's start from the beginning. */
167 return next();
168}
169
170QString StringSplitter::current() const
171{
172 return m_current;
173}
174
175qint64 StringSplitter::position() const
176{
177 return m_position;
178}
179
180Item::Iterator::Ptr IdFN::evaluateSequence(const DynamicContext::Ptr &context) const
181{
182 const Item::Iterator::Ptr idrefs(m_operands.first()->evaluateSequence(context));
183 const Item node(m_operands.last()->evaluateSingleton(context));
184
185 checkTargetNode(node.asNode(), context, ReportContext::FODC0001);
186
187 return makeItemMappingIterator<Item,
188 QString,
189 IdFN::ConstPtr,
190 IDContext>(ConstPtr(this),
191 StringSplitter::Ptr(new StringSplitter(idrefs)),
192 qMakePair(context, node.asNode().model()));
193}
194
195Expression::Ptr IdFN::typeCheck(const StaticContext::Ptr &context,
196 const SequenceType::Ptr &reqType)
197{
198 if(m_hasCreatedSorter)
199 return FunctionCall::typeCheck(context, reqType);
200 else
201 {
202 const Expression::Ptr newMe(new NodeSortExpression(Expression::Ptr(this)));
203 context->wrapExpressionWith(this, newMe);
204 m_hasCreatedSorter = true;
205 return newMe->typeCheck(context, reqType);
206 }
207}
208
209Item::Iterator::Ptr IdrefFN::evaluateSequence(const DynamicContext::Ptr &context) const
210{
211 const Item::Iterator::Ptr ids(m_operands.first()->evaluateSequence(context));
212
213 Item mId(ids->next());
214 if(!mId)
215 return CommonValues::emptyIterator;
216
217 const Item node(m_operands.last()->evaluateSingleton(context));
218 checkTargetNode(node.asNode(), context, ReportContext::FODC0001);
219
220 return CommonValues::emptyIterator; /* TODO Haven't implemented further. */
221}
222
223Item DocFN::evaluateSingleton(const DynamicContext::Ptr &context) const
224{
225 const Item itemURI(m_operands.first()->evaluateSingleton(context));
226
227 if(!itemURI)
228 return Item();
229
230 /* These two lines were previously in a separate function but are now duplicated
231 * in DocAvailableFN::evaluateEBV() and DocFN::typeCheck(),
232 * as part of a workaround for solaris-cc-64. DocFN::typeCheck() is in qsequencefns.cpp
233 * as part of that workaround. */
234 const QUrl mayRela(AnyURI::toQUrl<ReportContext::FODC0005>(itemURI.stringValue(), context, this));
235 const QUrl uri(context->resolveURI(mayRela, staticBaseURI()));
236
237 Q_ASSERT(uri.isValid());
238 Q_ASSERT(!uri.isRelative());
239
240 const Item doc(context->resourceLoader()->openDocument(uri, context));
241
242 return doc;
243}
244
245SequenceType::Ptr DocFN::staticType() const
246{
247 if(m_type)
248 return m_type;
249 else
250 return CommonSequenceTypes::ZeroOrOneDocumentNode;
251}
252
253bool DocAvailableFN::evaluateEBV(const DynamicContext::Ptr &context) const
254{
255 const Item itemURI(m_operands.first()->evaluateSingleton(context));
256
257 /* 15.5.4 fn:doc reads: "If $uri is the empty sequence, the result is an empty sequence."
258 * Hence, we return false for the empty sequence, because this doesn't hold true:
259 * "If this function returns true, then calling fn:doc($uri) within
260 * the same execution scope must return a document node."(15.5.5 fn:doc-available) */
261 if(!itemURI)
262 return false;
263
264 /* These two lines are duplicated in DocFN::evaluateSingleton(), as part
265 * of a workaround for solaris-cc-64. */
266 const QUrl mayRela(AnyURI::toQUrl<ReportContext::FODC0005>(itemURI.stringValue(), context, this));
267 const QUrl uri(context->resolveURI(mayRela, staticBaseURI()));
268
269 Q_ASSERT(!uri.isRelative());
270 return context->resourceLoader()->isDocumentAvailable(uri);
271}
272
273Item::Iterator::Ptr CollectionFN::evaluateSequence(const DynamicContext::Ptr &context) const
274{
275 // TODO resolve with URI resolve
276 if(m_operands.isEmpty())
277 {
278 // TODO check default collection
279 context->error(QtXmlPatterns::tr("The default collection is undefined"),
280 ReportContext::FODC0002, this);
281 return CommonValues::emptyIterator;
282 }
283 else
284 {
285 const Item itemURI(m_operands.first()->evaluateSingleton(context));
286
287 if(itemURI)
288 {
289 const QUrl uri(AnyURI::toQUrl<ReportContext::FODC0004>(itemURI.stringValue(), context, this));
290
291 // TODO 2. Resolve against static context base URI(store base URI at compile time)
292 context->error(QtXmlPatterns::tr("%1 cannot be retrieved").arg(formatResourcePath(uri)),
293 ReportContext::FODC0004, this);
294 return CommonValues::emptyIterator;
295 }
296 else
297 {
298 /* This is out default collection currently, */
299 return CommonValues::emptyIterator;
300 }
301 }
302}
303
304QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.