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 <QtCore/QFile>
41#include <QtCore/QTextCodec>
42#include <QtCore/QTimer>
43#include <QtCore/QXmlStreamReader>
44
45#include <QtNetwork/QNetworkRequest>
46
47#include "qatomicstring_p.h"
48#include "qautoptr_p.h"
49#include "qcommonsequencetypes_p.h"
50
51#include "qacceltreeresourceloader_p.h"
52
53QT_BEGIN_NAMESPACE
54
55using namespace QPatternist;
56
57AccelTreeResourceLoader::AccelTreeResourceLoader(const NamePool::Ptr &np,
58 const NetworkAccessDelegator::Ptr &manager,
59 AccelTreeBuilder<true>::Features features)
60 : m_namePool(np)
61 , m_networkAccessDelegator(manager)
62 , m_features(features)
63{
64 Q_ASSERT(m_namePool);
65 Q_ASSERT(m_networkAccessDelegator);
66}
67
68bool AccelTreeResourceLoader::retrieveDocument(const QUrl &uri,
69 const ReportContext::Ptr &context)
70{
71 Q_ASSERT(uri.isValid());
72 AccelTreeBuilder<true> builder(uri, uri, m_namePool, context.data(), m_features);
73
74 const AutoPtr<QNetworkReply> reply(load(uri, networkDelegator: m_networkAccessDelegator, context));
75
76 if(!reply)
77 return false;
78
79 bool success = false;
80 success = streamToReceiver(dev: reply.data(), receiver: &builder, np: m_namePool, context, uri);
81
82 m_loadedDocuments.insert(akey: uri, avalue: builder.builtDocument());
83 return success;
84}
85
86bool AccelTreeResourceLoader::retrieveDocument(QIODevice *source, const QUrl &documentUri, const ReportContext::Ptr &context)
87{
88 Q_ASSERT(source);
89 Q_ASSERT(source->isReadable());
90 Q_ASSERT(documentUri.isValid());
91
92 AccelTreeBuilder<true> builder(documentUri, documentUri, m_namePool, context.data(), m_features);
93
94 bool success = false;
95 success = streamToReceiver(dev: source, receiver: &builder, np: m_namePool, context, uri: documentUri);
96
97 m_loadedDocuments.insert(akey: documentUri, avalue: builder.builtDocument());
98
99 return success;
100}
101
102QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri,
103 const NetworkAccessDelegator::Ptr &networkDelegator,
104 const ReportContext::Ptr &context, ErrorHandling errorHandling)
105{
106 return load(uri,
107 networkManager: networkDelegator->managerFor(uri),
108 context, handling: errorHandling);
109}
110
111QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri,
112 QNetworkAccessManager *const networkManager,
113 const ReportContext::Ptr &context, ErrorHandling errorHandling)
114
115{
116 Q_ASSERT(networkManager);
117 Q_ASSERT(uri.isValid());
118
119 const bool ftpSchemeUsed = (uri.scheme() == QStringLiteral("ftp"));
120 // QNAM doesn't have support for SynchronousRequestAttribute in its ftp backend.
121 QEventLoop ftpNetworkLoop;
122 QNetworkRequest request(uri);
123 if (!ftpSchemeUsed)
124 request.setAttribute(code: QNetworkRequest::SynchronousRequestAttribute, value: true);
125 QNetworkReply *const reply = networkManager->get(request);
126 if (ftpSchemeUsed) {
127 ftpNetworkLoop.connect(asender: reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(quit()));
128 ftpNetworkLoop.connect(asender: reply, SIGNAL(finished()), SLOT(quit()));
129 ftpNetworkLoop.exec(flags: QEventLoop::ExcludeUserInputEvents);
130 }
131
132 if (reply->error() != QNetworkReply::NoError) {
133 const QString errorMessage(escape(input: reply->errorString()));
134
135 /* Note, we delete reply before we exit this function with error(). */
136 delete reply;
137
138 const QSourceLocation location(uri);
139
140 if(context && (errorHandling == FailOnError))
141 context->error(message: errorMessage, errorCode: ReportContext::FODC0002, sourceLocation: location);
142
143 return 0;
144 } else
145 return reply;
146}
147
148bool AccelTreeResourceLoader::streamToReceiver(QIODevice *const dev,
149 AccelTreeBuilder<true> *const receiver,
150 const NamePool::Ptr &np,
151 const ReportContext::Ptr &context,
152 const QUrl &uri)
153{
154 Q_ASSERT(dev);
155 Q_ASSERT(receiver);
156 Q_ASSERT(np);
157
158 QXmlStreamReader reader(dev);
159
160 /* Optimize: change NamePool to take QStringRef such that we don't have to call toString() below. That
161 * will save us a gazillion of temporary QStrings. */
162
163 while(!reader.atEnd())
164 {
165 reader.readNext();
166
167 switch(reader.tokenType())
168 {
169 case QXmlStreamReader::StartElement:
170 {
171 /* Send the name. */
172 receiver->startElement(name: np->allocateQName(uri: reader.namespaceUri().toString(), localName: reader.name().toString(),
173 prefix: reader.prefix().toString()), line: reader.lineNumber(), column: reader.columnNumber());
174
175 /* Send namespace declarations. */
176 const QXmlStreamNamespaceDeclarations &nss = reader.namespaceDeclarations();
177
178 /* The far most common case, is for it to be empty. */
179 if(!nss.isEmpty())
180 {
181 const int len = nss.size();
182
183 for(int i = 0; i < len; ++i)
184 {
185 const QXmlStreamNamespaceDeclaration &ns = nss.at(i);
186 receiver->namespaceBinding(nb: np->allocateBinding(prefix: ns.prefix().toString(), uri: ns.namespaceUri().toString()));
187 }
188 }
189
190 /* Send attributes. */
191 const QXmlStreamAttributes &attrs = reader.attributes();
192 const int len = attrs.size();
193
194 for(int i = 0; i < len; ++i)
195 {
196 const QXmlStreamAttribute &attr = attrs.at(i);
197
198 receiver->attribute(name: np->allocateQName(uri: attr.namespaceUri().toString(), localName: attr.name().toString(),
199 prefix: attr.prefix().toString()),
200 value: attr.value());
201 }
202
203 continue;
204 }
205 case QXmlStreamReader::EndElement:
206 {
207 receiver->endElement();
208 continue;
209 }
210 case QXmlStreamReader::Characters:
211 {
212 if(reader.isWhitespace())
213 receiver->whitespaceOnly(ch: reader.text());
214 else
215 receiver->characters(ch: reader.text());
216
217 continue;
218 }
219 case QXmlStreamReader::Comment:
220 {
221 receiver->comment(content: reader.text().toString());
222 continue;
223 }
224 case QXmlStreamReader::ProcessingInstruction:
225 {
226 receiver->processingInstruction(target: np->allocateQName(uri: QString(), localName: reader.processingInstructionTarget().toString()),
227 data: reader.processingInstructionData().toString());
228 continue;
229 }
230 case QXmlStreamReader::StartDocument:
231 {
232 receiver->startDocument();
233 continue;
234 }
235 case QXmlStreamReader::EndDocument:
236 {
237 receiver->endDocument();
238 continue;
239 }
240 case QXmlStreamReader::EntityReference:
241 case QXmlStreamReader::DTD:
242 {
243 /* We just ignore any DTD and entity references. */
244 continue;
245 }
246 case QXmlStreamReader::Invalid:
247 {
248 if(context)
249 context->error(message: escape(input: reader.errorString()), errorCode: ReportContext::FODC0002, sourceLocation: QSourceLocation(uri, reader.lineNumber(), reader.columnNumber()));
250
251 return false;
252 }
253 case QXmlStreamReader::NoToken:
254 {
255 Q_ASSERT_X(false, Q_FUNC_INFO,
256 "This token is never expected to be received.");
257 return false;
258 }
259 }
260 }
261
262 return true;
263}
264
265Item AccelTreeResourceLoader::openDocument(const QUrl &uri,
266 const ReportContext::Ptr &context)
267{
268 const AccelTree::Ptr doc(m_loadedDocuments.value(akey: uri));
269
270 if(doc)
271 return doc->root(n: QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
272 else
273 {
274 if(retrieveDocument(uri, context))
275 return m_loadedDocuments.value(akey: uri)->root(n: QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
276 else
277 return Item();
278 }
279}
280
281Item AccelTreeResourceLoader::openDocument(QIODevice *source, const QUrl &documentUri,
282 const ReportContext::Ptr &context)
283{
284 const AccelTree::Ptr doc(m_loadedDocuments.value(akey: documentUri));
285
286 if(doc)
287 return doc->root(n: QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
288 else
289 {
290 if(retrieveDocument(source, documentUri, context))
291 return m_loadedDocuments.value(akey: documentUri)->root(n: QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
292 else
293 return Item();
294 }
295}
296
297SequenceType::Ptr AccelTreeResourceLoader::announceDocument(const QUrl &uri, const Usage)
298{
299 // TODO deal with the usage thingy
300 Q_ASSERT(uri.isValid());
301 Q_ASSERT(!uri.isRelative());
302 Q_UNUSED(uri); /* Needed when compiling in release mode. */
303
304 return CommonSequenceTypes::ZeroOrOneDocumentNode;
305}
306
307bool AccelTreeResourceLoader::isDocumentAvailable(const QUrl &uri)
308{
309 return retrieveDocument(uri, context: ReportContext::Ptr());
310}
311
312bool AccelTreeResourceLoader::retrieveUnparsedText(const QUrl &uri,
313 const QString &encoding,
314 const ReportContext::Ptr &context,
315 const SourceLocationReflection *const where)
316{
317 const AutoPtr<QNetworkReply> reply(load(uri, networkDelegator: m_networkAccessDelegator, context));
318
319 if(!reply)
320 return false;
321
322 const QTextCodec * codec;
323 if(encoding.isEmpty())
324 {
325 /* XSL Transformations (XSLT) Version 2.0 16.2 Reading Text Files:
326 *
327 * "if the media type of the resource is text/xml or application/xml
328 * (see [RFC2376]), or if it matches the conventions text/\*+xml or
329 * application/\*+xml (see [RFC3023] and/or its successors), then the
330 * encoding is recognized as specified in [XML 1.0]"
331 */
332 codec = QTextCodec::codecForMib(mib: 106);
333 }
334 else
335 {
336 codec = QTextCodec::codecForName(name: encoding.toLatin1());
337 if(codec && context)
338 {
339 context->error(message: QtXmlPatterns::tr(sourceText: "%1 is an unsupported encoding.").arg(a: formatURI(uri: encoding)),
340 errorCode: ReportContext::XTDE1190,
341 reflection: where);
342 }
343 else
344 return false;
345 }
346
347 QTextCodec::ConverterState converterState;
348 const QByteArray inData(reply->readAll());
349 const QString result(codec->toUnicode(in: inData.constData(), length: inData.length(), state: &converterState));
350
351 if(converterState.invalidChars)
352 {
353 if(context)
354 {
355 context->error(message: QtXmlPatterns::tr(sourceText: "%1 contains octets which are disallowed in "
356 "the requested encoding %2.").arg(args: formatURI(uri),
357 args: formatURI(uri: encoding)),
358 errorCode: ReportContext::XTDE1190,
359 reflection: where);
360 }
361 else
362 return false;
363 }
364
365 const int len = result.length();
366 /* This code is a candidate for threading. Divide and conqueror. */
367 for(int i = 0; i < len; ++i)
368 {
369 if(!QXmlUtils::isChar(c: result.at(i)))
370 {
371 if(context)
372 {
373 context->error(message: QtXmlPatterns::tr(sourceText: "The codepoint %1, occurring in %2 using encoding %3, "
374 "is an invalid XML character.").arg(args: formatData(data: result.at(i)),
375 args: formatURI(uri),
376 args: formatURI(uri: encoding)),
377 errorCode: ReportContext::XTDE1190,
378 reflection: where);
379 }
380 else
381 return false;
382 }
383 }
384
385 m_unparsedTexts.insert(akey: qMakePair(x: uri, y: encoding), avalue: result);
386 return true;
387}
388
389bool AccelTreeResourceLoader::isUnparsedTextAvailable(const QUrl &uri,
390 const QString &encoding)
391{
392 return retrieveUnparsedText(uri, encoding, context: ReportContext::Ptr(), where: 0);
393}
394
395Item AccelTreeResourceLoader::openUnparsedText(const QUrl &uri,
396 const QString &encoding,
397 const ReportContext::Ptr &context,
398 const SourceLocationReflection *const where)
399{
400 const QString &text = m_unparsedTexts.value(akey: qMakePair(x: uri, y: encoding));
401
402 if(text.isNull())
403 {
404 if(retrieveUnparsedText(uri, encoding, context, where))
405 return openUnparsedText(uri, encoding, context, where);
406 else
407 return Item();
408 }
409 else
410 return AtomicString::fromValue(value: text);
411}
412
413QSet<QUrl> AccelTreeResourceLoader::deviceURIs() const
414{
415 QHash<QUrl, AccelTree::Ptr>::const_iterator it(m_loadedDocuments.constBegin());
416 const QHash<QUrl, AccelTree::Ptr>::const_iterator end(m_loadedDocuments.constEnd());
417 QSet<QUrl> retval;
418
419 while (it != end)
420 {
421 if(it.key().toString().startsWith(s: QLatin1String("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:")))
422 retval.insert(value: it.key());
423
424 ++it;
425 }
426
427 return retval;
428}
429
430void AccelTreeResourceLoader::clear(const QUrl &uri)
431{
432 m_loadedDocuments.remove(akey: uri);
433}
434
435QT_END_NAMESPACE
436
437

source code of qtxmlpatterns/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp