txPathExpr.cpp (6817B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "txExpr.h" 7 #include "txNodeSet.h" 8 #include "txNodeSetContext.h" 9 #include "txSingleNodeContext.h" 10 #include "txXMLUtils.h" 11 #include "txXPathTreeWalker.h" 12 13 using mozilla::WrapUnique; 14 15 //------------/ 16 //- PathExpr -/ 17 //------------/ 18 19 /** 20 * Adds the Expr to this PathExpr 21 * @param expr the Expr to add to this PathExpr 22 **/ 23 void PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp) { 24 NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP, 25 "First step has to be relative in PathExpr"); 26 PathExprItem* pxi = mItems.AppendElement(); 27 pxi->expr = WrapUnique(aExpr); 28 pxi->pathOp = aPathOp; 29 } 30 31 //-----------------------------/ 32 //- Virtual methods from Expr -/ 33 //-----------------------------/ 34 35 /** 36 * Evaluates this Expr based on the given context node and processor state 37 * @param context the context node for evaluation of this Expr 38 * @param ps the ContextState containing the stack information needed 39 * for evaluation 40 * @return the result of the evaluation 41 **/ 42 nsresult PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult) { 43 *aResult = nullptr; 44 45 // We need to evaluate the first step with the current context since it 46 // can depend on the context size and position. For example: 47 // key('books', concat('book', position())) 48 RefPtr<txAExprResult> res; 49 nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res)); 50 NS_ENSURE_SUCCESS(rv, rv); 51 52 NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET, 53 NS_ERROR_XSLT_NODESET_EXPECTED); 54 55 RefPtr<txNodeSet> nodes = 56 static_cast<txNodeSet*>(static_cast<txAExprResult*>(res)); 57 if (nodes->isEmpty()) { 58 res.forget(aResult); 59 60 return NS_OK; 61 } 62 res = nullptr; // To allow recycling 63 64 // Evaluate remaining steps 65 uint32_t i, len = mItems.Length(); 66 for (i = 1; i < len; ++i) { 67 PathExprItem& pxi = mItems[i]; 68 RefPtr<txNodeSet> tmpNodes; 69 txNodeSetContext eContext(nodes, aContext); 70 while (eContext.hasNext()) { 71 eContext.next(); 72 73 RefPtr<txNodeSet> resNodes; 74 if (pxi.pathOp == DESCENDANT_OP) { 75 rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes)); 76 NS_ENSURE_SUCCESS(rv, rv); 77 78 rv = evalDescendants(pxi.expr.get(), eContext.getContextNode(), 79 &eContext, resNodes); 80 NS_ENSURE_SUCCESS(rv, rv); 81 } else { 82 RefPtr<txAExprResult> res; 83 rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res)); 84 NS_ENSURE_SUCCESS(rv, rv); 85 86 if (res->getResultType() != txAExprResult::NODESET) { 87 // XXX ErrorReport: report nonnodeset error 88 return NS_ERROR_XSLT_NODESET_EXPECTED; 89 } 90 resNodes = static_cast<txNodeSet*>(static_cast<txAExprResult*>(res)); 91 } 92 93 if (tmpNodes) { 94 if (!resNodes->isEmpty()) { 95 RefPtr<txNodeSet> oldSet; 96 oldSet.swap(tmpNodes); 97 rv = aContext->recycler()->getNonSharedNodeSet( 98 oldSet, getter_AddRefs(tmpNodes)); 99 NS_ENSURE_SUCCESS(rv, rv); 100 101 oldSet.swap(resNodes); 102 rv = aContext->recycler()->getNonSharedNodeSet( 103 oldSet, getter_AddRefs(resNodes)); 104 NS_ENSURE_SUCCESS(rv, rv); 105 106 tmpNodes->addAndTransfer(resNodes); 107 } 108 } else { 109 tmpNodes = resNodes; 110 } 111 } 112 nodes = tmpNodes; 113 if (nodes->isEmpty()) { 114 break; 115 } 116 } 117 118 *aResult = nodes; 119 NS_ADDREF(*aResult); 120 121 return NS_OK; 122 } //-- evaluate 123 124 /** 125 * Selects from the descendants of the context node 126 * all nodes that match the Expr 127 **/ 128 nsresult PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode, 129 txIMatchContext* aContext, 130 txNodeSet* resNodes) { 131 txSingleNodeContext eContext(aNode, aContext); 132 RefPtr<txAExprResult> res; 133 nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res)); 134 NS_ENSURE_SUCCESS(rv, rv); 135 136 if (res->getResultType() != txAExprResult::NODESET) { 137 // XXX ErrorReport: report nonnodeset error 138 return NS_ERROR_XSLT_NODESET_EXPECTED; 139 } 140 141 txNodeSet* oldSet = static_cast<txNodeSet*>(static_cast<txAExprResult*>(res)); 142 RefPtr<txNodeSet> newSet; 143 rv = 144 aContext->recycler()->getNonSharedNodeSet(oldSet, getter_AddRefs(newSet)); 145 NS_ENSURE_SUCCESS(rv, rv); 146 147 resNodes->addAndTransfer(newSet); 148 149 bool filterWS; 150 rv = aContext->isStripSpaceAllowed(aNode, filterWS); 151 NS_ENSURE_SUCCESS(rv, rv); 152 153 txXPathTreeWalker walker(aNode); 154 if (!walker.moveToFirstChild()) { 155 return NS_OK; 156 } 157 158 do { 159 const txXPathNode& node = walker.getCurrentPosition(); 160 if (!(filterWS && txXPathNodeUtils::isText(node) && 161 txXPathNodeUtils::isWhitespace(node))) { 162 rv = evalDescendants(aStep, node, aContext, resNodes); 163 NS_ENSURE_SUCCESS(rv, rv); 164 } 165 } while (walker.moveToNextSibling()); 166 167 return NS_OK; 168 } //-- evalDescendants 169 170 Expr::ExprType PathExpr::getType() { return PATH_EXPR; } 171 172 TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT) 173 174 Expr* PathExpr::getSubExprAt(uint32_t aPos) { 175 return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr; 176 } 177 void PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr) { 178 NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index"); 179 (void)mItems[aPos].expr.release(); 180 mItems[aPos].expr = WrapUnique(aExpr); 181 } 182 183 bool PathExpr::isSensitiveTo(ContextSensitivity aContext) { 184 if (mItems[0].expr->isSensitiveTo(aContext)) { 185 return true; 186 } 187 188 // We're creating a new node/nodeset so we can ignore those bits. 189 Expr::ContextSensitivity context = 190 aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT); 191 if (context == NO_CONTEXT) { 192 return false; 193 } 194 195 uint32_t i, len = mItems.Length(); 196 for (i = 0; i < len; ++i) { 197 NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT), 198 "Step cannot depend on nodeset-context"); 199 if (mItems[i].expr->isSensitiveTo(context)) { 200 return true; 201 } 202 } 203 204 return false; 205 } 206 207 #ifdef TX_TO_STRING 208 void PathExpr::toString(nsAString& dest) { 209 if (!mItems.IsEmpty()) { 210 NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP, 211 "First step should be relative"); 212 mItems[0].expr->toString(dest); 213 } 214 215 uint32_t i, len = mItems.Length(); 216 for (i = 1; i < len; ++i) { 217 switch (mItems[i].pathOp) { 218 case DESCENDANT_OP: 219 dest.AppendLiteral("//"); 220 break; 221 case RELATIVE_OP: 222 dest.Append(char16_t('/')); 223 break; 224 } 225 mItems[i].expr->toString(dest); 226 } 227 } 228 #endif