michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: Implementation of an XPath LocationStep michael@0: */ michael@0: michael@0: #include "txExpr.h" michael@0: #include "txIXPathContext.h" michael@0: #include "txNodeSet.h" michael@0: #include "txXPathTreeWalker.h" michael@0: michael@0: //-----------------------------/ michael@0: //- Virtual methods from Expr -/ michael@0: //-----------------------------/ michael@0: michael@0: /** michael@0: * Evaluates this Expr based on the given context node and processor state michael@0: * @param context the context node for evaluation of this Expr michael@0: * @param ps the ProcessorState containing the stack information needed michael@0: * for evaluation michael@0: * @return the result of the evaluation michael@0: * @see Expr michael@0: **/ michael@0: nsresult michael@0: LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult) michael@0: { michael@0: NS_ASSERTION(aContext, "internal error"); michael@0: *aResult = nullptr; michael@0: michael@0: nsRefPtr nodes; michael@0: nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: txXPathTreeWalker walker(aContext->getContextNode()); michael@0: michael@0: switch (mAxisIdentifier) { michael@0: case ANCESTOR_AXIS: michael@0: { michael@0: if (!walker.moveToParent()) { michael@0: break; michael@0: } michael@0: // do not break here michael@0: } michael@0: case ANCESTOR_OR_SELF_AXIS: michael@0: { michael@0: nodes->setReverse(); michael@0: michael@0: do { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: } while (walker.moveToParent()); michael@0: michael@0: break; michael@0: } michael@0: case ATTRIBUTE_AXIS: michael@0: { michael@0: if (!walker.moveToFirstAttribute()) { michael@0: break; michael@0: } michael@0: michael@0: do { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: } while (walker.moveToNextAttribute()); michael@0: break; michael@0: } michael@0: case DESCENDANT_OR_SELF_AXIS: michael@0: { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: // do not break here michael@0: } michael@0: case DESCENDANT_AXIS: michael@0: { michael@0: fromDescendants(walker.getCurrentPosition(), aContext, nodes); michael@0: break; michael@0: } michael@0: case FOLLOWING_AXIS: michael@0: { michael@0: if (txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) { michael@0: walker.moveToParent(); michael@0: fromDescendants(walker.getCurrentPosition(), aContext, nodes); michael@0: } michael@0: bool cont = true; michael@0: while (!walker.moveToNextSibling()) { michael@0: if (!walker.moveToParent()) { michael@0: cont = false; michael@0: break; michael@0: } michael@0: } michael@0: while (cont) { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: michael@0: fromDescendants(walker.getCurrentPosition(), aContext, nodes); michael@0: michael@0: while (!walker.moveToNextSibling()) { michael@0: if (!walker.moveToParent()) { michael@0: cont = false; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case FOLLOWING_SIBLING_AXIS: michael@0: { michael@0: while (walker.moveToNextSibling()) { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case NAMESPACE_AXIS: //-- not yet implemented michael@0: #if 0 michael@0: // XXX DEBUG OUTPUT michael@0: cout << "namespace axis not yet implemented"<matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: break; michael@0: } michael@0: case PRECEDING_AXIS: michael@0: { michael@0: nodes->setReverse(); michael@0: michael@0: bool cont = true; michael@0: while (!walker.moveToPreviousSibling()) { michael@0: if (!walker.moveToParent()) { michael@0: cont = false; michael@0: break; michael@0: } michael@0: } michael@0: while (cont) { michael@0: fromDescendantsRev(walker.getCurrentPosition(), aContext, nodes); michael@0: michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: michael@0: while (!walker.moveToPreviousSibling()) { michael@0: if (!walker.moveToParent()) { michael@0: cont = false; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case PRECEDING_SIBLING_AXIS: michael@0: { michael@0: nodes->setReverse(); michael@0: michael@0: while (walker.moveToPreviousSibling()) { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case SELF_AXIS: michael@0: { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: break; michael@0: } michael@0: default: // Children Axis michael@0: { michael@0: if (!walker.moveToFirstChild()) { michael@0: break; michael@0: } michael@0: michael@0: do { michael@0: if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) { michael@0: nodes->append(walker.getCurrentPosition()); michael@0: } michael@0: } while (walker.moveToNextSibling()); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Apply predicates michael@0: if (!isEmpty()) { michael@0: rv = evaluatePredicates(nodes, aContext); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: nodes->unsetReverse(); michael@0: michael@0: NS_ADDREF(*aResult = nodes); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void LocationStep::fromDescendants(const txXPathNode& aNode, michael@0: txIMatchContext* aCs, michael@0: txNodeSet* aNodes) michael@0: { michael@0: txXPathTreeWalker walker(aNode); michael@0: if (!walker.moveToFirstChild()) { michael@0: return; michael@0: } michael@0: michael@0: do { michael@0: const txXPathNode& child = walker.getCurrentPosition(); michael@0: if (mNodeTest->matches(child, aCs)) { michael@0: aNodes->append(child); michael@0: } michael@0: fromDescendants(child, aCs, aNodes); michael@0: } while (walker.moveToNextSibling()); michael@0: } michael@0: michael@0: void LocationStep::fromDescendantsRev(const txXPathNode& aNode, michael@0: txIMatchContext* aCs, michael@0: txNodeSet* aNodes) michael@0: { michael@0: txXPathTreeWalker walker(aNode); michael@0: if (!walker.moveToLastChild()) { michael@0: return; michael@0: } michael@0: michael@0: do { michael@0: const txXPathNode& child = walker.getCurrentPosition(); michael@0: fromDescendantsRev(child, aCs, aNodes); michael@0: michael@0: if (mNodeTest->matches(child, aCs)) { michael@0: aNodes->append(child); michael@0: } michael@0: michael@0: } while (walker.moveToPreviousSibling()); michael@0: } michael@0: michael@0: Expr::ExprType michael@0: LocationStep::getType() michael@0: { michael@0: return LOCATIONSTEP_EXPR; michael@0: } michael@0: michael@0: michael@0: TX_IMPL_EXPR_STUBS_BASE(LocationStep, NODESET_RESULT) michael@0: michael@0: Expr* michael@0: LocationStep::getSubExprAt(uint32_t aPos) michael@0: { michael@0: return PredicateList::getSubExprAt(aPos); michael@0: } michael@0: michael@0: void michael@0: LocationStep::setSubExprAt(uint32_t aPos, Expr* aExpr) michael@0: { michael@0: PredicateList::setSubExprAt(aPos, aExpr); michael@0: } michael@0: michael@0: bool michael@0: LocationStep::isSensitiveTo(ContextSensitivity aContext) michael@0: { michael@0: return (aContext & NODE_CONTEXT) || michael@0: mNodeTest->isSensitiveTo(aContext) || michael@0: PredicateList::isSensitiveTo(aContext); michael@0: } michael@0: michael@0: #ifdef TX_TO_STRING michael@0: void michael@0: LocationStep::toString(nsAString& str) michael@0: { michael@0: switch (mAxisIdentifier) { michael@0: case ANCESTOR_AXIS : michael@0: str.AppendLiteral("ancestor::"); michael@0: break; michael@0: case ANCESTOR_OR_SELF_AXIS : michael@0: str.AppendLiteral("ancestor-or-self::"); michael@0: break; michael@0: case ATTRIBUTE_AXIS: michael@0: str.Append(char16_t('@')); michael@0: break; michael@0: case DESCENDANT_AXIS: michael@0: str.AppendLiteral("descendant::"); michael@0: break; michael@0: case DESCENDANT_OR_SELF_AXIS: michael@0: str.AppendLiteral("descendant-or-self::"); michael@0: break; michael@0: case FOLLOWING_AXIS : michael@0: str.AppendLiteral("following::"); michael@0: break; michael@0: case FOLLOWING_SIBLING_AXIS: michael@0: str.AppendLiteral("following-sibling::"); michael@0: break; michael@0: case NAMESPACE_AXIS: michael@0: str.AppendLiteral("namespace::"); michael@0: break; michael@0: case PARENT_AXIS : michael@0: str.AppendLiteral("parent::"); michael@0: break; michael@0: case PRECEDING_AXIS : michael@0: str.AppendLiteral("preceding::"); michael@0: break; michael@0: case PRECEDING_SIBLING_AXIS : michael@0: str.AppendLiteral("preceding-sibling::"); michael@0: break; michael@0: case SELF_AXIS : michael@0: str.AppendLiteral("self::"); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: NS_ASSERTION(mNodeTest, "mNodeTest is null, that's verboten"); michael@0: mNodeTest->toString(str); michael@0: michael@0: PredicateList::toString(str); michael@0: } michael@0: #endif