src/xmlpatterns/functions/qsequencegeneratingfns.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 <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 
       
    57 QT_BEGIN_NAMESPACE
       
    58 
       
    59 using namespace QPatternist;
       
    60 
       
    61 IdFN::IdFN() : m_hasCreatedSorter(false)
       
    62 {
       
    63 }
       
    64 
       
    65 Item 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 IdFN.
       
    73  *
       
    74  * StringSplitter takes an Iterator which delivers strings of this kind:
       
    75  *
       
    76  * "a", "b c", "%invalidNCName", "  ", "d"
       
    77  *
       
    78  * and we deliver instead:
       
    79  *
       
    80  * "a", "b", "c", "d"
       
    81  *
       
    82  * That is, we:
       
    83  * - Remove invalid @c NCName
       
    84  * - Split IDREFs into individual NCNames
       
    85  *
       
    86  * @author Frans Englich <frans.englich@nokia.com>
       
    87  */
       
    88 class StringSplitter : public QAbstractXmlForwardIterator<QString>
       
    89 {
       
    90 public:
       
    91     StringSplitter(const Item::Iterator::Ptr &source);
       
    92     virtual QString next();
       
    93     virtual QString current() const;
       
    94     virtual qint64 position() const;
       
    95 private:
       
    96     QString loadNext();
       
    97     const Item::Iterator::Ptr   m_source;
       
    98     QStack<QString>             m_buffer;
       
    99     QString                     m_current;
       
   100     qint64                      m_position;
       
   101     bool                        m_sourceAtEnd;
       
   102 };
       
   103 
       
   104 StringSplitter::StringSplitter(const Item::Iterator::Ptr &source) : m_source(source)
       
   105                                                                   , m_position(0)
       
   106                                                                   , m_sourceAtEnd(false)
       
   107 {
       
   108     Q_ASSERT(m_source);
       
   109     m_buffer.push(loadNext());
       
   110 }
       
   111 
       
   112 QString StringSplitter::next()
       
   113 {
       
   114     /* We also check m_position, we want to load on our first run. */
       
   115     if(!m_buffer.isEmpty())
       
   116     {
       
   117         ++m_position;
       
   118         m_current = m_buffer.pop();
       
   119         return m_current;
       
   120     }
       
   121     else if(m_sourceAtEnd)
       
   122     {
       
   123         m_current.clear();
       
   124         m_position = -1;
       
   125         return QString();
       
   126     }
       
   127 
       
   128     return loadNext();
       
   129 }
       
   130 
       
   131 QString StringSplitter::loadNext()
       
   132 {
       
   133     const Item sourceNext(m_source->next());
       
   134 
       
   135     if(sourceNext.isNull())
       
   136     {
       
   137         m_sourceAtEnd = true;
       
   138         /* We might have strings in m_buffer, let's empty it. */
       
   139         return next();
       
   140     }
       
   141 
       
   142     const QStringList candidates(sourceNext.stringValue().simplified().split(QLatin1Char(' ')));
       
   143     const int count = candidates.length();
       
   144 
       
   145     for(int i = 0; i < count; ++i)
       
   146     {
       
   147         const QString &at = candidates.at(i);
       
   148 
       
   149         if(QXmlUtils::isNCName(at))
       
   150             m_buffer.push(at);
       
   151     }
       
   152 
       
   153     /* So, now we have populated m_buffer, let's start from the beginning. */
       
   154     return next();
       
   155 }
       
   156 
       
   157 QString StringSplitter::current() const
       
   158 {
       
   159     return m_current;
       
   160 }
       
   161 
       
   162 qint64 StringSplitter::position() const
       
   163 {
       
   164     return m_position;
       
   165 }
       
   166 
       
   167 Item::Iterator::Ptr IdFN::evaluateSequence(const DynamicContext::Ptr &context) const
       
   168 {
       
   169     const Item::Iterator::Ptr idrefs(m_operands.first()->evaluateSequence(context));
       
   170     const Item node(m_operands.last()->evaluateSingleton(context));
       
   171 
       
   172     checkTargetNode(node.asNode(), context, ReportContext::FODC0001);
       
   173 
       
   174     return makeItemMappingIterator<Item,
       
   175                                    QString,
       
   176                                    IdFN::ConstPtr,
       
   177                                    IDContext>(ConstPtr(this),
       
   178                                               StringSplitter::Ptr(new StringSplitter(idrefs)),
       
   179                                               qMakePair(context, node.asNode().model()));
       
   180 }
       
   181 
       
   182 Expression::Ptr IdFN::typeCheck(const StaticContext::Ptr &context,
       
   183                                 const SequenceType::Ptr &reqType)
       
   184 {
       
   185     if(m_hasCreatedSorter)
       
   186         return FunctionCall::typeCheck(context, reqType);
       
   187     else
       
   188     {
       
   189         const Expression::Ptr newMe(new NodeSortExpression(Expression::Ptr(this)));
       
   190         context->wrapExpressionWith(this, newMe);
       
   191         m_hasCreatedSorter = true;
       
   192         return newMe->typeCheck(context, reqType);
       
   193     }
       
   194 }
       
   195 
       
   196 Item::Iterator::Ptr IdrefFN::evaluateSequence(const DynamicContext::Ptr &context) const
       
   197 {
       
   198     const Item::Iterator::Ptr ids(m_operands.first()->evaluateSequence(context));
       
   199 
       
   200     Item mId(ids->next());
       
   201     if(!mId)
       
   202         return CommonValues::emptyIterator;
       
   203 
       
   204     const Item node(m_operands.last()->evaluateSingleton(context));
       
   205     checkTargetNode(node.asNode(), context, ReportContext::FODC0001);
       
   206 
       
   207     return CommonValues::emptyIterator; /* TODO Haven't implemented further. */
       
   208 }
       
   209 
       
   210 Item DocFN::evaluateSingleton(const DynamicContext::Ptr &context) const
       
   211 {
       
   212     const Item itemURI(m_operands.first()->evaluateSingleton(context));
       
   213 
       
   214     if(!itemURI)
       
   215         return Item();
       
   216 
       
   217     /* These two lines were previously in a separate function but are now duplicated
       
   218      * in DocAvailableFN::evaluateEBV() and DocFN::typeCheck(),
       
   219      * as part of a workaround for solaris-cc-64. DocFN::typeCheck() is in qsequencefns.cpp
       
   220      * as part of that workaround. */
       
   221     const QUrl mayRela(AnyURI::toQUrl<ReportContext::FODC0005>(itemURI.stringValue(), context, this));
       
   222     const QUrl uri(context->resolveURI(mayRela, staticBaseURI()));
       
   223 
       
   224     Q_ASSERT(uri.isValid());
       
   225     Q_ASSERT(!uri.isRelative());
       
   226 
       
   227     const Item doc(context->resourceLoader()->openDocument(uri, context));
       
   228 
       
   229     return doc;
       
   230 }
       
   231 
       
   232 SequenceType::Ptr DocFN::staticType() const
       
   233 {
       
   234     if(m_type)
       
   235         return m_type;
       
   236     else
       
   237         return CommonSequenceTypes::ZeroOrOneDocumentNode;
       
   238 }
       
   239 
       
   240 bool DocAvailableFN::evaluateEBV(const DynamicContext::Ptr &context) const
       
   241 {
       
   242     const Item itemURI(m_operands.first()->evaluateSingleton(context));
       
   243 
       
   244     /* 15.5.4 fn:doc reads: "If $uri is the empty sequence, the result is an empty sequence."
       
   245      * Hence, we return false for the empty sequence, because this doesn't hold true:
       
   246      * "If this function returns true, then calling fn:doc($uri) within
       
   247      * the same execution scope must return a document node."(15.5.5 fn:doc-available) */
       
   248     if(!itemURI)
       
   249         return false;
       
   250 
       
   251     /* These two lines are duplicated in DocFN::evaluateSingleton(), as part
       
   252      * of a workaround for solaris-cc-64. */
       
   253     const QUrl mayRela(AnyURI::toQUrl<ReportContext::FODC0005>(itemURI.stringValue(), context, this));
       
   254     const QUrl uri(context->resolveURI(mayRela, staticBaseURI()));
       
   255 
       
   256     Q_ASSERT(!uri.isRelative());
       
   257     return context->resourceLoader()->isDocumentAvailable(uri);
       
   258 }
       
   259 
       
   260 Item::Iterator::Ptr CollectionFN::evaluateSequence(const DynamicContext::Ptr &context) const
       
   261 {
       
   262     // TODO resolve with URI resolve
       
   263     if(m_operands.isEmpty())
       
   264     {
       
   265         // TODO check default collection
       
   266         context->error(QtXmlPatterns::tr("The default collection is undefined"),
       
   267                        ReportContext::FODC0002, this);
       
   268         return CommonValues::emptyIterator;
       
   269     }
       
   270     else
       
   271     {
       
   272         const Item itemURI(m_operands.first()->evaluateSingleton(context));
       
   273 
       
   274         if(itemURI)
       
   275         {
       
   276             const QUrl uri(AnyURI::toQUrl<ReportContext::FODC0004>(itemURI.stringValue(), context, this));
       
   277 
       
   278             // TODO 2. Resolve against static context base URI(store base URI at compile time)
       
   279             context->error(QtXmlPatterns::tr("%1 cannot be retrieved").arg(formatResourcePath(uri)),
       
   280                            ReportContext::FODC0004, this);
       
   281             return CommonValues::emptyIterator;
       
   282         }
       
   283         else
       
   284         {
       
   285             /* This is out default collection currently, */
       
   286             return CommonValues::emptyIterator;
       
   287         }
       
   288     }
       
   289 }
       
   290 
       
   291 QT_END_NAMESPACE