dom/xslt/xpath/txPathExpr.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/xslt/xpath/txPathExpr.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,253 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "txExpr.h"
    1.10 +#include "txNodeSet.h"
    1.11 +#include "txNodeSetContext.h"
    1.12 +#include "txSingleNodeContext.h"
    1.13 +#include "txXMLUtils.h"
    1.14 +#include "txXPathTreeWalker.h"
    1.15 +
    1.16 +  //------------/
    1.17 + //- PathExpr -/
    1.18 +//------------/
    1.19 +
    1.20 +/**
    1.21 + * Adds the Expr to this PathExpr
    1.22 + * @param expr the Expr to add to this PathExpr
    1.23 +**/
    1.24 +nsresult
    1.25 +PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp)
    1.26 +{
    1.27 +    NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP,
    1.28 +                 "First step has to be relative in PathExpr");
    1.29 +    PathExprItem* pxi = mItems.AppendElement();
    1.30 +    if (!pxi) {
    1.31 +        return NS_ERROR_OUT_OF_MEMORY;
    1.32 +    }
    1.33 +    pxi->expr = aExpr;
    1.34 +    pxi->pathOp = aPathOp;
    1.35 +
    1.36 +    return NS_OK;
    1.37 +}
    1.38 +
    1.39 +    //-----------------------------/
    1.40 +  //- Virtual methods from Expr -/
    1.41 +//-----------------------------/
    1.42 +
    1.43 +/**
    1.44 + * Evaluates this Expr based on the given context node and processor state
    1.45 + * @param context the context node for evaluation of this Expr
    1.46 + * @param ps the ContextState containing the stack information needed
    1.47 + * for evaluation
    1.48 + * @return the result of the evaluation
    1.49 +**/
    1.50 +nsresult
    1.51 +PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
    1.52 +{
    1.53 +    *aResult = nullptr;
    1.54 +
    1.55 +    // We need to evaluate the first step with the current context since it
    1.56 +    // can depend on the context size and position. For example:
    1.57 +    // key('books', concat('book', position()))
    1.58 +    nsRefPtr<txAExprResult> res;
    1.59 +    nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res));
    1.60 +    NS_ENSURE_SUCCESS(rv, rv);
    1.61 +
    1.62 +    NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
    1.63 +                   NS_ERROR_XSLT_NODESET_EXPECTED);
    1.64 +
    1.65 +    nsRefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
    1.66 +                                           (static_cast<txAExprResult*>
    1.67 +                                                       (res));
    1.68 +    if (nodes->isEmpty()) {
    1.69 +        res.swap(*aResult);
    1.70 +
    1.71 +        return NS_OK;
    1.72 +    }
    1.73 +    res = nullptr; // To allow recycling
    1.74 +
    1.75 +    // Evaluate remaining steps
    1.76 +    uint32_t i, len = mItems.Length();
    1.77 +    for (i = 1; i < len; ++i) {
    1.78 +        PathExprItem& pxi = mItems[i];
    1.79 +        nsRefPtr<txNodeSet> tmpNodes;
    1.80 +        txNodeSetContext eContext(nodes, aContext);
    1.81 +        while (eContext.hasNext()) {
    1.82 +            eContext.next();
    1.83 +
    1.84 +            nsRefPtr<txNodeSet> resNodes;
    1.85 +            if (pxi.pathOp == DESCENDANT_OP) {
    1.86 +                rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes));
    1.87 +                NS_ENSURE_SUCCESS(rv, rv);
    1.88 +
    1.89 +                rv = evalDescendants(pxi.expr, eContext.getContextNode(),
    1.90 +                                     &eContext, resNodes);
    1.91 +                NS_ENSURE_SUCCESS(rv, rv);
    1.92 +            }
    1.93 +            else {
    1.94 +                nsRefPtr<txAExprResult> res;
    1.95 +                rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res));
    1.96 +                NS_ENSURE_SUCCESS(rv, rv);
    1.97 +
    1.98 +                if (res->getResultType() != txAExprResult::NODESET) {
    1.99 +                    //XXX ErrorReport: report nonnodeset error
   1.100 +                    return NS_ERROR_XSLT_NODESET_EXPECTED;
   1.101 +                }
   1.102 +                resNodes = static_cast<txNodeSet*>
   1.103 +                                      (static_cast<txAExprResult*>
   1.104 +                                                  (res));
   1.105 +            }
   1.106 +
   1.107 +            if (tmpNodes) {
   1.108 +                if (!resNodes->isEmpty()) {
   1.109 +                    nsRefPtr<txNodeSet> oldSet;
   1.110 +                    oldSet.swap(tmpNodes);
   1.111 +                    rv = aContext->recycler()->
   1.112 +                        getNonSharedNodeSet(oldSet, getter_AddRefs(tmpNodes));
   1.113 +                    NS_ENSURE_SUCCESS(rv, rv);
   1.114 +
   1.115 +                    oldSet.swap(resNodes);
   1.116 +                    rv = aContext->recycler()->
   1.117 +                        getNonSharedNodeSet(oldSet, getter_AddRefs(resNodes));
   1.118 +                    NS_ENSURE_SUCCESS(rv, rv);
   1.119 +
   1.120 +                    tmpNodes->addAndTransfer(resNodes);
   1.121 +                }
   1.122 +            }
   1.123 +            else {
   1.124 +                tmpNodes = resNodes;
   1.125 +            }
   1.126 +        }
   1.127 +        nodes = tmpNodes;
   1.128 +        if (nodes->isEmpty()) {
   1.129 +            break;
   1.130 +        }
   1.131 +    }
   1.132 +
   1.133 +    *aResult = nodes;
   1.134 +    NS_ADDREF(*aResult);
   1.135 +    
   1.136 +    return NS_OK;
   1.137 +} //-- evaluate
   1.138 +
   1.139 +/**
   1.140 + * Selects from the descendants of the context node
   1.141 + * all nodes that match the Expr
   1.142 +**/
   1.143 +nsresult
   1.144 +PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
   1.145 +                          txIMatchContext* aContext, txNodeSet* resNodes)
   1.146 +{
   1.147 +    txSingleNodeContext eContext(aNode, aContext);
   1.148 +    nsRefPtr<txAExprResult> res;
   1.149 +    nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res));
   1.150 +    NS_ENSURE_SUCCESS(rv, rv);
   1.151 +
   1.152 +    if (res->getResultType() != txAExprResult::NODESET) {
   1.153 +        //XXX ErrorReport: report nonnodeset error
   1.154 +        return NS_ERROR_XSLT_NODESET_EXPECTED;
   1.155 +    }
   1.156 +
   1.157 +    txNodeSet* oldSet = static_cast<txNodeSet*>
   1.158 +                                   (static_cast<txAExprResult*>(res));
   1.159 +    nsRefPtr<txNodeSet> newSet;
   1.160 +    rv = aContext->recycler()->getNonSharedNodeSet(oldSet,
   1.161 +                                                   getter_AddRefs(newSet));
   1.162 +    NS_ENSURE_SUCCESS(rv, rv);
   1.163 +
   1.164 +    resNodes->addAndTransfer(newSet);
   1.165 +
   1.166 +    bool filterWS = aContext->isStripSpaceAllowed(aNode);
   1.167 +
   1.168 +    txXPathTreeWalker walker(aNode);
   1.169 +    if (!walker.moveToFirstChild()) {
   1.170 +        return NS_OK;
   1.171 +    }
   1.172 +
   1.173 +    do {
   1.174 +        const txXPathNode& node = walker.getCurrentPosition();
   1.175 +        if (!(filterWS && txXPathNodeUtils::isText(node) &&
   1.176 +              txXPathNodeUtils::isWhitespace(node))) {
   1.177 +            rv = evalDescendants(aStep, node, aContext, resNodes);
   1.178 +            NS_ENSURE_SUCCESS(rv, rv);
   1.179 +        }
   1.180 +    } while (walker.moveToNextSibling());
   1.181 +
   1.182 +    return NS_OK;
   1.183 +} //-- evalDescendants
   1.184 +
   1.185 +Expr::ExprType
   1.186 +PathExpr::getType()
   1.187 +{
   1.188 +  return PATH_EXPR;
   1.189 +}
   1.190 +
   1.191 +TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT)
   1.192 +
   1.193 +Expr*
   1.194 +PathExpr::getSubExprAt(uint32_t aPos)
   1.195 +{
   1.196 +    return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr;
   1.197 +}
   1.198 +void
   1.199 +PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr)
   1.200 +{
   1.201 +    NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index");
   1.202 +    mItems[aPos].expr.forget();
   1.203 +    mItems[aPos].expr = aExpr;
   1.204 +}
   1.205 +
   1.206 +
   1.207 +bool
   1.208 +PathExpr::isSensitiveTo(ContextSensitivity aContext)
   1.209 +{
   1.210 +    if (mItems[0].expr->isSensitiveTo(aContext)) {
   1.211 +        return true;
   1.212 +    }
   1.213 +
   1.214 +    // We're creating a new node/nodeset so we can ignore those bits.
   1.215 +    Expr::ContextSensitivity context =
   1.216 +        aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
   1.217 +    if (context == NO_CONTEXT) {
   1.218 +        return false;
   1.219 +    }
   1.220 +
   1.221 +    uint32_t i, len = mItems.Length();
   1.222 +    for (i = 0; i < len; ++i) {
   1.223 +        NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT),
   1.224 +                     "Step cannot depend on nodeset-context");
   1.225 +        if (mItems[i].expr->isSensitiveTo(context)) {
   1.226 +            return true;
   1.227 +        }
   1.228 +    }
   1.229 +
   1.230 +    return false;
   1.231 +}
   1.232 +
   1.233 +#ifdef TX_TO_STRING
   1.234 +void
   1.235 +PathExpr::toString(nsAString& dest)
   1.236 +{
   1.237 +    if (!mItems.IsEmpty()) {
   1.238 +        NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP,
   1.239 +                     "First step should be relative");
   1.240 +        mItems[0].expr->toString(dest);
   1.241 +    }
   1.242 +    
   1.243 +    uint32_t i, len = mItems.Length();
   1.244 +    for (i = 1; i < len; ++i) {
   1.245 +        switch (mItems[i].pathOp) {
   1.246 +            case DESCENDANT_OP:
   1.247 +                dest.AppendLiteral("//");
   1.248 +                break;
   1.249 +            case RELATIVE_OP:
   1.250 +                dest.Append(char16_t('/'));
   1.251 +                break;
   1.252 +        }
   1.253 +        mItems[i].expr->toString(dest);
   1.254 +    }
   1.255 +}
   1.256 +#endif

mercurial