src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QtCore/QFile>
       
    43 #include <QtCore/QTextCodec>
       
    44 #include <QtCore/QTimer>
       
    45 #include <QtCore/QXmlStreamReader>
       
    46 
       
    47 #include <QtNetwork/QNetworkRequest>
       
    48 
       
    49 #include "qatomicstring_p.h"
       
    50 #include "qautoptr_p.h"
       
    51 #include "qcommonsequencetypes_p.h"
       
    52 
       
    53 #include "qacceltreeresourceloader_p.h"
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 using namespace QPatternist;
       
    58 
       
    59 AccelTreeResourceLoader::AccelTreeResourceLoader(const NamePool::Ptr &np,
       
    60                                                  const NetworkAccessDelegator::Ptr &manager,
       
    61                                                  AccelTreeBuilder<true>::Features features)
       
    62     : m_namePool(np)
       
    63     , m_networkAccessDelegator(manager)
       
    64     , m_features(features)
       
    65 {
       
    66     Q_ASSERT(m_namePool);
       
    67     Q_ASSERT(m_networkAccessDelegator);
       
    68 }
       
    69 
       
    70 bool AccelTreeResourceLoader::retrieveDocument(const QUrl &uri,
       
    71                                                const ReportContext::Ptr &context)
       
    72 {
       
    73     Q_ASSERT(uri.isValid());
       
    74     AccelTreeBuilder<true> builder(uri, uri, m_namePool, context.data(), m_features);
       
    75 
       
    76     const AutoPtr<QNetworkReply> reply(load(uri, m_networkAccessDelegator, context));
       
    77 
       
    78     if(!reply)
       
    79         return false;
       
    80 
       
    81     bool success = false;
       
    82     success = streamToReceiver(reply.data(), &builder, m_namePool, context, uri);
       
    83 
       
    84     m_loadedDocuments.insert(uri, builder.builtDocument());
       
    85     return success;
       
    86 }
       
    87 
       
    88 bool AccelTreeResourceLoader::retrieveDocument(QIODevice *source, const QUrl &documentUri, const ReportContext::Ptr &context)
       
    89 {
       
    90     Q_ASSERT(source);
       
    91     Q_ASSERT(source->isReadable());
       
    92     Q_ASSERT(documentUri.isValid());
       
    93 
       
    94     AccelTreeBuilder<true> builder(documentUri, documentUri, m_namePool, context.data(), m_features);
       
    95 
       
    96     bool success = false;
       
    97     success = streamToReceiver(source, &builder, m_namePool, context, documentUri);
       
    98 
       
    99     m_loadedDocuments.insert(documentUri, builder.builtDocument());
       
   100 
       
   101     return success;
       
   102 }
       
   103 
       
   104 QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri,
       
   105                                              const NetworkAccessDelegator::Ptr &networkDelegator,
       
   106                                              const ReportContext::Ptr &context, ErrorHandling errorHandling)
       
   107 {
       
   108     return load(uri,
       
   109                 networkDelegator->managerFor(uri),
       
   110                 context, errorHandling);
       
   111 }
       
   112 
       
   113 QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri,
       
   114                                              QNetworkAccessManager *const networkManager,
       
   115                                              const ReportContext::Ptr &context, ErrorHandling errorHandling)
       
   116 
       
   117 {
       
   118     Q_ASSERT(networkManager);
       
   119     Q_ASSERT(uri.isValid());
       
   120 
       
   121     NetworkLoop networkLoop;
       
   122 
       
   123     QNetworkRequest request(uri);
       
   124     QNetworkReply *const reply = networkManager->get(request);
       
   125     networkLoop.connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(error(QNetworkReply::NetworkError)));
       
   126     networkLoop.connect(reply, SIGNAL(finished()), SLOT(finished()));
       
   127 
       
   128     if(networkLoop.exec(QEventLoop::ExcludeUserInputEvents))
       
   129     {
       
   130         const QString errorMessage(escape(reply->errorString()));
       
   131 
       
   132         /* Note, we delete reply before we exit this function with error(). */
       
   133         delete reply;
       
   134 
       
   135         const QSourceLocation location(uri);
       
   136 
       
   137         if(context && (errorHandling == FailOnError))
       
   138             context->error(errorMessage, ReportContext::FODC0002, location);
       
   139 
       
   140         return 0;
       
   141     }
       
   142     else
       
   143         return reply;
       
   144 }
       
   145 
       
   146 bool AccelTreeResourceLoader::streamToReceiver(QIODevice *const dev,
       
   147                                                AccelTreeBuilder<true> *const receiver,
       
   148                                                const NamePool::Ptr &np,
       
   149                                                const ReportContext::Ptr &context,
       
   150                                                const QUrl &uri)
       
   151 {
       
   152     Q_ASSERT(dev);
       
   153     Q_ASSERT(receiver);
       
   154     Q_ASSERT(np);
       
   155 
       
   156     QXmlStreamReader reader(dev);
       
   157 
       
   158     /* Optimize: change NamePool to take QStringRef such that we don't have to call toString() below. That
       
   159      * will save us a gazillion of temporary QStrings. */
       
   160 
       
   161     while(!reader.atEnd())
       
   162     {
       
   163         reader.readNext();
       
   164 
       
   165         switch(reader.tokenType())
       
   166         {
       
   167             case QXmlStreamReader::StartElement:
       
   168             {
       
   169                 /* Send the name. */
       
   170                 receiver->startElement(np->allocateQName(reader.namespaceUri().toString(), reader.name().toString(),
       
   171                                                          reader.prefix().toString()), reader.lineNumber(), reader.columnNumber());
       
   172 
       
   173                 /* Send namespace declarations. */
       
   174                 const QXmlStreamNamespaceDeclarations &nss = reader.namespaceDeclarations();
       
   175 
       
   176                 /* The far most common case, is for it to be empty. */
       
   177                 if(!nss.isEmpty())
       
   178                 {
       
   179                     const int len = nss.size();
       
   180 
       
   181                     for(int i = 0; i < len; ++i)
       
   182                     {
       
   183                         const QXmlStreamNamespaceDeclaration &ns = nss.at(i);
       
   184                         receiver->namespaceBinding(np->allocateBinding(ns.prefix().toString(), ns.namespaceUri().toString()));
       
   185                     }
       
   186                 }
       
   187 
       
   188                 /* Send attributes. */
       
   189                 const QXmlStreamAttributes &attrs = reader.attributes();
       
   190                 const int len = attrs.size();
       
   191 
       
   192                 for(int i = 0; i < len; ++i)
       
   193                 {
       
   194                     const QXmlStreamAttribute &attr = attrs.at(i);
       
   195 
       
   196                     receiver->attribute(np->allocateQName(attr.namespaceUri().toString(), attr.name().toString(),
       
   197                                                           attr.prefix().toString()),
       
   198                                         attr.value());
       
   199                 }
       
   200 
       
   201                 continue;
       
   202             }
       
   203             case QXmlStreamReader::EndElement:
       
   204             {
       
   205                 receiver->endElement();
       
   206                 continue;
       
   207             }
       
   208             case QXmlStreamReader::Characters:
       
   209             {
       
   210                 if(reader.isWhitespace())
       
   211                     receiver->whitespaceOnly(reader.text());
       
   212                 else
       
   213                     receiver->characters(reader.text());
       
   214 
       
   215                 continue;
       
   216             }
       
   217             case QXmlStreamReader::Comment:
       
   218             {
       
   219                 receiver->comment(reader.text().toString());
       
   220                 continue;
       
   221             }
       
   222             case QXmlStreamReader::ProcessingInstruction:
       
   223             {
       
   224                 receiver->processingInstruction(np->allocateQName(QString(), reader.processingInstructionTarget().toString()),
       
   225                                                 reader.processingInstructionData().toString());
       
   226                 continue;
       
   227             }
       
   228             case QXmlStreamReader::StartDocument:
       
   229             {
       
   230                 receiver->startDocument();
       
   231                 continue;
       
   232             }
       
   233             case QXmlStreamReader::EndDocument:
       
   234             {
       
   235                 receiver->endDocument();
       
   236                 continue;
       
   237             }
       
   238             case QXmlStreamReader::EntityReference:
       
   239             /* Fallthrough. */
       
   240             case QXmlStreamReader::DTD:
       
   241             {
       
   242                 /* We just ignore any DTD and entity references. */
       
   243                 continue;
       
   244             }
       
   245             case QXmlStreamReader::Invalid:
       
   246             {
       
   247                 if(context)
       
   248                     context->error(escape(reader.errorString()), ReportContext::FODC0002, QSourceLocation(uri, reader.lineNumber(), reader.columnNumber()));
       
   249 
       
   250                 return false;
       
   251             }
       
   252             case QXmlStreamReader::NoToken:
       
   253             {
       
   254                 Q_ASSERT_X(false, Q_FUNC_INFO,
       
   255                            "This token is never expected to be received.");
       
   256                 return false;
       
   257             }
       
   258         }
       
   259     }
       
   260 
       
   261     return true;
       
   262 }
       
   263 
       
   264 Item AccelTreeResourceLoader::openDocument(const QUrl &uri,
       
   265                                            const ReportContext::Ptr &context)
       
   266 {
       
   267     const AccelTree::Ptr doc(m_loadedDocuments.value(uri));
       
   268 
       
   269     if(doc)
       
   270         return doc->root(QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
       
   271     else
       
   272     {
       
   273         if(retrieveDocument(uri, context))
       
   274             return m_loadedDocuments.value(uri)->root(QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
       
   275         else
       
   276             return Item();
       
   277     }
       
   278 }
       
   279 
       
   280 Item AccelTreeResourceLoader::openDocument(QIODevice *source, const QUrl &documentUri,
       
   281                                            const ReportContext::Ptr &context)
       
   282 {
       
   283     const AccelTree::Ptr doc(m_loadedDocuments.value(documentUri));
       
   284 
       
   285     if(doc)
       
   286         return doc->root(QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
       
   287     else
       
   288     {
       
   289         if(retrieveDocument(source, documentUri, context))
       
   290             return m_loadedDocuments.value(documentUri)->root(QXmlNodeModelIndex()); /* Pass in dummy object. We know AccelTree doesn't use it. */
       
   291         else
       
   292             return Item();
       
   293     }
       
   294 }
       
   295 
       
   296 SequenceType::Ptr AccelTreeResourceLoader::announceDocument(const QUrl &uri, const Usage)
       
   297 {
       
   298     // TODO deal with the usage thingy
       
   299     Q_ASSERT(uri.isValid());
       
   300     Q_ASSERT(!uri.isRelative());
       
   301     Q_UNUSED(uri); /* Needed when compiling in release mode. */
       
   302 
       
   303     return CommonSequenceTypes::ZeroOrOneDocumentNode;
       
   304 }
       
   305 
       
   306 bool AccelTreeResourceLoader::isDocumentAvailable(const QUrl &uri)
       
   307 {
       
   308     return retrieveDocument(uri, ReportContext::Ptr());
       
   309 }
       
   310 
       
   311 static inline uint qHash(const QPair<QUrl, QString> &desc)
       
   312 {
       
   313     /* Probably a lousy hash. */
       
   314     return qHash(desc.first) + qHash(desc.second);
       
   315 }
       
   316 
       
   317 bool AccelTreeResourceLoader::retrieveUnparsedText(const QUrl &uri,
       
   318                                                    const QString &encoding,
       
   319                                                    const ReportContext::Ptr &context,
       
   320                                                    const SourceLocationReflection *const where)
       
   321 {
       
   322     const AutoPtr<QNetworkReply> reply(load(uri, m_networkAccessDelegator, context));
       
   323 
       
   324     if(!reply)
       
   325         return false;
       
   326 
       
   327     const QTextCodec * codec;
       
   328     if(encoding.isEmpty())
       
   329     {
       
   330         /* XSL Transformations (XSLT) Version 2.0 16.2 Reading Text Files:
       
   331          *
       
   332          * "if the media type of the resource is text/xml or application/xml
       
   333          * (see [RFC2376]), or if it matches the conventions text/\*+xml or
       
   334          * application/\*+xml (see [RFC3023] and/or its successors), then the
       
   335          * encoding is recognized as specified in [XML 1.0]"
       
   336          */
       
   337         codec = QTextCodec::codecForMib(106);
       
   338     }
       
   339     else
       
   340     {
       
   341         codec = QTextCodec::codecForName(encoding.toLatin1());
       
   342         if(codec && context)
       
   343         {
       
   344             context->error(QtXmlPatterns::tr("%1 is an unsupported encoding.").arg(formatURI(encoding)),
       
   345                            ReportContext::XTDE1190,
       
   346                            where);
       
   347         }
       
   348         else
       
   349             return false;
       
   350     }
       
   351 
       
   352     QTextCodec::ConverterState converterState;
       
   353     const QByteArray inData(reply->readAll());
       
   354     const QString result(codec->toUnicode(inData.constData(), inData.length(), &converterState));
       
   355 
       
   356     if(converterState.invalidChars)
       
   357     {
       
   358         if(context)
       
   359         {
       
   360             context->error(QtXmlPatterns::tr("%1 contains octets which are disallowed in "
       
   361                                              "the requested encoding %2.").arg(formatURI(uri),
       
   362                                                                                formatURI(encoding)),
       
   363                            ReportContext::XTDE1190,
       
   364                            where);
       
   365         }
       
   366         else
       
   367             return false;
       
   368     }
       
   369 
       
   370     const int len = result.length();
       
   371     /* This code is a candidate for threading. Divide and conqueror. */
       
   372     for(int i = 0; i < len; ++i)
       
   373     {
       
   374         if(!QXmlUtils::isChar(result.at(i)))
       
   375         {
       
   376             if(context)
       
   377             {
       
   378                 context->error(QtXmlPatterns::tr("The codepoint %1, occurring in %2 using encoding %3, "
       
   379                                                  "is an invalid XML character.").arg(formatData(result.at(i)),
       
   380                                                                                      formatURI(uri),
       
   381                                                                                      formatURI(encoding)),
       
   382                                ReportContext::XTDE1190,
       
   383                                where);
       
   384             }
       
   385             else
       
   386                 return false;
       
   387         }
       
   388     }
       
   389 
       
   390     m_unparsedTexts.insert(qMakePair(uri, encoding), result);
       
   391     return true;
       
   392 }
       
   393 
       
   394 bool AccelTreeResourceLoader::isUnparsedTextAvailable(const QUrl &uri,
       
   395                                                       const QString &encoding)
       
   396 {
       
   397     return retrieveUnparsedText(uri, encoding, ReportContext::Ptr(), 0);
       
   398 }
       
   399 
       
   400 Item AccelTreeResourceLoader::openUnparsedText(const QUrl &uri,
       
   401                                                const QString &encoding,
       
   402                                                const ReportContext::Ptr &context,
       
   403                                                const SourceLocationReflection *const where)
       
   404 {
       
   405     const QString &text = m_unparsedTexts.value(qMakePair(uri, encoding));
       
   406 
       
   407     if(text.isNull())
       
   408     {
       
   409         if(retrieveUnparsedText(uri, encoding, context, where))
       
   410             return openUnparsedText(uri, encoding, context, where);
       
   411         else
       
   412             return Item();
       
   413     }
       
   414     else
       
   415         return AtomicString::fromValue(text);
       
   416 }
       
   417 
       
   418 QSet<QUrl> AccelTreeResourceLoader::deviceURIs() const
       
   419 {
       
   420      QHash<QUrl, AccelTree::Ptr>::const_iterator it(m_loadedDocuments.constBegin());
       
   421      const QHash<QUrl, AccelTree::Ptr>::const_iterator end(m_loadedDocuments.constEnd());
       
   422      QSet<QUrl> retval;
       
   423 
       
   424      while (it != end)
       
   425      {
       
   426          if(it.key().toString().startsWith(QLatin1String("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:")))
       
   427              retval.insert(it.key());
       
   428 
       
   429          ++it;
       
   430      }
       
   431 
       
   432      return retval;
       
   433 }
       
   434 
       
   435 void AccelTreeResourceLoader::clear(const QUrl &uri)
       
   436 {
       
   437     m_loadedDocuments.remove(uri);
       
   438 }
       
   439 
       
   440 QT_END_NAMESPACE
       
   441