зеркало из https://github.com/mozilla/pjs.git
Make PathExpr::matches more clever and remove specialcasing of absolute path expressions
b=97107 r=peterv sr=jst
This commit is contained in:
Родитель
6b95ff030d
Коммит
4dda9dee83
|
@ -727,7 +727,7 @@ public:
|
|||
//-- Path Operators
|
||||
//-- RELATIVE_OP is the default
|
||||
//-- LF, changed from static const short to enum
|
||||
enum _PathOperator { ANCESTOR_OP=1, PARENT_OP, RELATIVE_OP} ;
|
||||
enum PathOperator { RELATIVE_OP, DESCENDANT_OP };
|
||||
|
||||
/**
|
||||
* Creates a new PathExpr
|
||||
|
@ -743,7 +743,7 @@ public:
|
|||
* Adds the Expr to this PathExpr
|
||||
* @param expr the Expr to add to this PathExpr
|
||||
**/
|
||||
void addExpr(Expr* expr, short ancestryOp);
|
||||
void addExpr(Expr* expr, PathOperator pathOp);
|
||||
|
||||
/**
|
||||
* Virtual methods from Expr
|
||||
|
@ -755,11 +755,9 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
virtual MBool isAbsolute();
|
||||
|
||||
struct PathExprItem {
|
||||
Expr* expr;
|
||||
short ancestryOp;
|
||||
PathOperator pathOp;
|
||||
};
|
||||
|
||||
List expressions;
|
||||
|
@ -779,13 +777,15 @@ private:
|
|||
/**
|
||||
* This class represents a RootExpr, which only matches the Document node
|
||||
**/
|
||||
class RootExpr : public PathExpr {
|
||||
class RootExpr : public Expr {
|
||||
|
||||
public:
|
||||
|
||||
//------------------/
|
||||
//- Public Methods -/
|
||||
//------------------/
|
||||
/**
|
||||
* Creates a new RootExpr
|
||||
* @param aSerialize should this RootExpr be serialized
|
||||
*/
|
||||
RootExpr(MBool aSerialize);
|
||||
|
||||
/**
|
||||
* Virtual methods from Expr
|
||||
|
@ -795,6 +795,10 @@ public:
|
|||
virtual double getDefaultPriority(Node* node, Node* context, ContextState* cs);
|
||||
virtual void toString(String& dest);
|
||||
|
||||
private:
|
||||
// When a RootExpr is used in a PathExpr it shouldn't be serialized
|
||||
MBool mSerialize;
|
||||
|
||||
}; //-- RootExpr
|
||||
|
||||
/**
|
||||
|
|
|
@ -646,7 +646,7 @@ Expr* ExprParser::createPathExpr(ExprLexer& lexer) {
|
|||
if (tok->type == Token::PARENT_OP) {
|
||||
lexer.nextToken();
|
||||
if (!isLocationStepToken(lexer.peek()))
|
||||
return new RootExpr;
|
||||
return new RootExpr(MB_TRUE);
|
||||
|
||||
lexer.pushBack();
|
||||
}
|
||||
|
@ -669,22 +669,33 @@ Expr* ExprParser::createPathExpr(ExprLexer& lexer) {
|
|||
tok->type != Token::ANCESTOR_OP)
|
||||
return expr;
|
||||
}
|
||||
else {
|
||||
expr = new RootExpr(MB_FALSE);
|
||||
if (!expr) {
|
||||
// XXX ErrorReport: out of memory
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//we have a pathexpression containing several steps
|
||||
// We have a PathExpr containing several steps
|
||||
PathExpr* pathExpr = new PathExpr();
|
||||
if (expr)
|
||||
pathExpr->addExpr(expr, PathExpr::RELATIVE_OP);
|
||||
if (!pathExpr) {
|
||||
// XXX ErrorReport: out of memory
|
||||
delete expr;
|
||||
return 0;
|
||||
}
|
||||
pathExpr->addExpr(expr, PathExpr::RELATIVE_OP);
|
||||
|
||||
// this is ugly
|
||||
while (1) {
|
||||
short ancestryOp;
|
||||
PathExpr::PathOperator pathOp;
|
||||
tok = lexer.nextToken();
|
||||
switch (tok->type) {
|
||||
case Token::ANCESTOR_OP :
|
||||
ancestryOp = PathExpr::ANCESTOR_OP;
|
||||
pathOp = PathExpr::DESCENDANT_OP;
|
||||
break;
|
||||
case Token::PARENT_OP :
|
||||
ancestryOp = PathExpr::PARENT_OP;
|
||||
pathOp = PathExpr::RELATIVE_OP;
|
||||
break;
|
||||
default:
|
||||
lexer.pushBack();
|
||||
|
@ -697,7 +708,7 @@ Expr* ExprParser::createPathExpr(ExprLexer& lexer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
pathExpr->addExpr(expr, ancestryOp);
|
||||
pathExpr->addExpr(expr, pathOp);
|
||||
}
|
||||
|
||||
return pathExpr;
|
||||
|
|
|
@ -66,27 +66,23 @@ PathExpr::~PathExpr()
|
|||
* Adds the Expr to this PathExpr
|
||||
* @param expr the Expr to add to this PathExpr
|
||||
**/
|
||||
void PathExpr::addExpr(Expr* expr, short ancestryOp)
|
||||
void PathExpr::addExpr(Expr* expr, PathOperator pathOp)
|
||||
{
|
||||
NS_ASSERTION(expressions.getLength() > 0 || pathOp == RELATIVE_OP,
|
||||
"First step has to be relative in PathExpr");
|
||||
if (expr) {
|
||||
PathExprItem* pxi = new PathExprItem;
|
||||
if (!pxi) {
|
||||
// XXX ErrorReport: out of memory
|
||||
NS_ASSERTION(0, "out of memory");
|
||||
return;
|
||||
}
|
||||
pxi->expr = expr;
|
||||
pxi->ancestryOp = ancestryOp;
|
||||
pxi->pathOp = pathOp;
|
||||
expressions.add(pxi);
|
||||
}
|
||||
} //-- addPattenExpr
|
||||
|
||||
MBool PathExpr::isAbsolute()
|
||||
{
|
||||
if (expressions.getLength() > 0) {
|
||||
ListIterator* iter = expressions.iterator();
|
||||
PathExprItem* pxi = (PathExprItem*)iter->next();
|
||||
delete iter;
|
||||
return (pxi->ancestryOp != RELATIVE_OP);
|
||||
}
|
||||
return MB_FALSE;
|
||||
} //-- isAbsolute
|
||||
|
||||
//-----------------------------/
|
||||
//- Virtual methods from Expr -/
|
||||
//-----------------------------/
|
||||
|
@ -106,23 +102,23 @@ ExprResult* PathExpr::evaluate(Node* context, ContextState* cs)
|
|||
return new NodeSet(0);
|
||||
|
||||
NodeSet* nodes = new NodeSet();
|
||||
if (!nodes) {
|
||||
// XXX ErrorReport: out of memory
|
||||
NS_ASSERTION(0, "out of memory");
|
||||
return 0;
|
||||
}
|
||||
nodes->add(context);
|
||||
|
||||
if (isAbsolute() && (context->getNodeType() != Node::DOCUMENT_NODE))
|
||||
nodes->add(context->getOwnerDocument());
|
||||
else
|
||||
nodes->add(context);
|
||||
ListIterator iter(&expressions);
|
||||
PathExprItem* pxi;
|
||||
|
||||
ListIterator* iter = expressions.iterator();
|
||||
|
||||
while (iter->hasNext()) {
|
||||
PathExprItem* pxi = (PathExprItem*)iter->next();
|
||||
while (pxi = (PathExprItem*)iter.next()) {
|
||||
NodeSet* tmpNodes = 0;
|
||||
cs->getNodeSetStack()->push(nodes);
|
||||
for (int i = 0; i < nodes->size(); i++) {
|
||||
Node* node = nodes->get(i);
|
||||
|
||||
NodeSet* resNodes;
|
||||
if (pxi->ancestryOp == ANCESTOR_OP) {
|
||||
if (pxi->pathOp == DESCENDANT_OP) {
|
||||
resNodes = new NodeSet;
|
||||
evalDescendants(pxi->expr, node, cs, resNodes);
|
||||
}
|
||||
|
@ -144,12 +140,10 @@ ExprResult* PathExpr::evaluate(Node* context, ContextState* cs)
|
|||
tmpNodes = resNodes;
|
||||
|
||||
}
|
||||
delete (NodeSet*) cs->getNodeSetStack()->pop();
|
||||
delete nodes;
|
||||
nodes = tmpNodes;
|
||||
if (!nodes || (nodes->size() == 0)) break;
|
||||
}
|
||||
delete iter;
|
||||
|
||||
return nodes;
|
||||
} //-- evaluate
|
||||
|
||||
|
@ -188,7 +182,7 @@ double PathExpr::getDefaultPriority(Node* node, Node* context,
|
|||
ContextState* cs)
|
||||
{
|
||||
int size = expressions.getLength();
|
||||
if (size > 1 || isAbsolute())
|
||||
if (size > 1)
|
||||
return 0.5;
|
||||
|
||||
return ((PathExprItem*)expressions.get(0))->
|
||||
|
@ -201,133 +195,69 @@ double PathExpr::getDefaultPriority(Node* node, Node* context,
|
|||
**/
|
||||
MBool PathExpr::matches(Node* node, Node* context, ContextState* cs)
|
||||
{
|
||||
/*
|
||||
* The idea is to split up a path into blocks separated by descendant
|
||||
* operators. For example "foo/bar//baz/bop//ying/yang" is split up into
|
||||
* three blocks. The "ying/yang" block is handled by the first while-loop
|
||||
* and the "foo/bar" and "baz/bop" blocks are handled by the second
|
||||
* while-loop.
|
||||
* A block is considered matched when we find a list of ancestors that
|
||||
* match the block. If there are more than one list of ancestors that
|
||||
* match a block we only need to find the one furthermost down in the
|
||||
* tree.
|
||||
*/
|
||||
|
||||
if (!node || (expressions.getLength() == 0))
|
||||
return MB_FALSE;
|
||||
|
||||
//-- for performance reasons, I've duplicated some code
|
||||
//-- here. If we only have one expression, there is no
|
||||
//-- reason to create NodeSets and go through the
|
||||
//-- while loop below. This resulted in a decent
|
||||
//-- performance gain in XSL:P, so I'm doing it here also,
|
||||
//-- even though I have no real code in place to test the
|
||||
//-- performance of transformiix.
|
||||
ListIterator iter(&expressions);
|
||||
iter.resetToEnd();
|
||||
|
||||
if (expressions.getLength() == 1) {
|
||||
PathExprItem* pxi = (PathExprItem*)expressions.get(0);
|
||||
switch(pxi->ancestryOp) {
|
||||
case ANCESTOR_OP:
|
||||
{
|
||||
Node* ancestor = node;
|
||||
while ((ancestor = ancestor->getXPathParent())) {
|
||||
if (pxi->expr->matches(node, ancestor, cs))
|
||||
return MB_TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PARENT_OP:
|
||||
{
|
||||
Node* parent = node->getXPathParent();
|
||||
if (parent) {
|
||||
//-- make sure node is Document node
|
||||
if (parent->getNodeType() == Node::DOCUMENT_NODE)
|
||||
return pxi->expr->matches(node, parent, cs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return pxi->expr->matches(node, context, cs);
|
||||
}
|
||||
PathExprItem* pxi;
|
||||
PathOperator pathOp = RELATIVE_OP;
|
||||
|
||||
while (pathOp == RELATIVE_OP) {
|
||||
|
||||
return MB_FALSE;
|
||||
}
|
||||
pxi = (PathExprItem*)iter.previous();
|
||||
if (!pxi)
|
||||
return MB_TRUE; // We've stepped through the entire list
|
||||
|
||||
//-- if we reach here we have subpaths...
|
||||
NodeSet nodes(3);
|
||||
NodeSet tmpNodes(3);
|
||||
|
||||
nodes.add(node);
|
||||
|
||||
ListIterator* iter = expressions.iterator();
|
||||
iter->resetToEnd();
|
||||
|
||||
while (iter->hasPrevious()) {
|
||||
|
||||
PathExprItem* pxi = (PathExprItem*)iter->previous();
|
||||
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Node* tnode = nodes.get(i);
|
||||
|
||||
//-- select node's parent or ancestors
|
||||
switch (pxi->ancestryOp) {
|
||||
case ANCESTOR_OP:
|
||||
{
|
||||
Node* parent = tnode;
|
||||
while ((parent = parent->getXPathParent())) {
|
||||
if (pxi->expr->matches(tnode, parent, cs))
|
||||
tmpNodes.add(parent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PARENT_OP:
|
||||
{
|
||||
Node* parent = tnode->getXPathParent();
|
||||
if (parent) {
|
||||
//-- make sure we have a document node if necessary
|
||||
if (!iter->hasPrevious())
|
||||
if (parent->getNodeType() != Node::DOCUMENT_NODE)
|
||||
break;
|
||||
if (pxi->expr->matches(tnode, parent, cs))
|
||||
tmpNodes.add(parent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (!iter->hasPrevious()) {
|
||||
/*
|
||||
// PREVIOUS // result = pxi->expr->matches(tnode, context, cs);
|
||||
// result was being overwritten if there was more than one
|
||||
// node in nodes during the final iteration (Marina)
|
||||
|
||||
result = result || pxi->expr->matches(tnode, context, cs)
|
||||
*/
|
||||
|
||||
//-- Just return true if we match here
|
||||
if (pxi->expr->matches(tnode, context, cs)) {
|
||||
delete iter;
|
||||
return MB_TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//-- error in expression, will we ever see this?
|
||||
delete iter;
|
||||
return MB_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} //-- for
|
||||
|
||||
if (tmpNodes.size() == 0) {
|
||||
delete iter;
|
||||
if (!node || !pxi->expr->matches(node, 0, cs))
|
||||
return MB_FALSE;
|
||||
|
||||
node = node->getXPathParent();
|
||||
pathOp = pxi->pathOp;
|
||||
}
|
||||
|
||||
// We have at least one DESCENDANT_OP
|
||||
|
||||
Node* blockStart = node;
|
||||
ListIterator blockIter(iter);
|
||||
|
||||
while ((pxi = (PathExprItem*)iter.previous())) {
|
||||
|
||||
if (!node)
|
||||
return MB_FALSE; // There are more steps in the current block
|
||||
// then ancestors of the tested node
|
||||
|
||||
if (!pxi->expr->matches(node, 0, cs)) {
|
||||
// Didn't match. We restart at beginning of block using a new
|
||||
// start node
|
||||
iter = blockIter;
|
||||
blockStart = blockStart->getXPathParent();
|
||||
node = blockStart;
|
||||
}
|
||||
else {
|
||||
node = node->getXPathParent();
|
||||
if (pxi->pathOp == DESCENDANT_OP) {
|
||||
// We've matched an entire block. Set new start iter and start node
|
||||
blockIter = iter;
|
||||
blockStart = node;
|
||||
}
|
||||
}
|
||||
|
||||
nodes.clear();
|
||||
tmpNodes.copyInto(nodes);
|
||||
tmpNodes.clear();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
|
||||
if ( this->isAbsolute()) {
|
||||
Node* doc = 0;
|
||||
if (node->getNodeType() == Node::DOCUMENT_NODE)
|
||||
doc = node;
|
||||
else
|
||||
doc = node->getOwnerDocument();
|
||||
return (MBool)nodes.contains(doc);
|
||||
}
|
||||
|
||||
return (MBool)(nodes.size() > 0);
|
||||
|
||||
return MB_TRUE;
|
||||
|
||||
} //-- matches
|
||||
|
||||
|
@ -342,22 +272,24 @@ MBool PathExpr::matches(Node* node, Node* context, ContextState* cs)
|
|||
**/
|
||||
void PathExpr::toString(String& dest)
|
||||
{
|
||||
ListIterator* iter = expressions.iterator();
|
||||
while (iter->hasNext()) {
|
||||
//-- set operator
|
||||
PathExprItem* pxi = (PathExprItem*)iter->next();
|
||||
switch (pxi->ancestryOp) {
|
||||
case ANCESTOR_OP:
|
||||
ListIterator iter(&expressions);
|
||||
|
||||
PathExprItem* pxi = (PathExprItem*)iter.next();
|
||||
if (pxi) {
|
||||
NS_ASSERTION(pxi->pathOp == RELATIVE_OP,
|
||||
"First step should be relative");
|
||||
pxi->expr->toString(dest);
|
||||
}
|
||||
|
||||
while (pxi = (PathExprItem*)iter.next()) {
|
||||
switch (pxi->pathOp) {
|
||||
case DESCENDANT_OP:
|
||||
dest.append("//");
|
||||
break;
|
||||
case PARENT_OP:
|
||||
case RELATIVE_OP:
|
||||
dest.append('/');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pxi->expr->toString(dest);
|
||||
}
|
||||
delete iter;
|
||||
} //-- toString
|
||||
|
||||
|
|
|
@ -25,6 +25,14 @@
|
|||
|
||||
#include "Expr.h"
|
||||
|
||||
/**
|
||||
* Creates a new RootExpr
|
||||
* @param aSerialize should this RootExpr be serialized
|
||||
*/
|
||||
RootExpr::RootExpr(MBool aSerialize) {
|
||||
mSerialize = aSerialize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates this Expr based on the given context node and processor state
|
||||
* @param context the context node for evaluation of this Expr
|
||||
|
@ -34,8 +42,20 @@
|
|||
**/
|
||||
ExprResult* RootExpr::evaluate(Node* context, ContextState* cs) {
|
||||
NodeSet* nodeSet = new NodeSet();
|
||||
if ( !context ) return nodeSet;
|
||||
nodeSet->add(context->getOwnerDocument());
|
||||
if (!nodeSet) {
|
||||
// XXX ErrorReport: out of memory
|
||||
NS_ASSERTION(0, "out of memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!context)
|
||||
return nodeSet;
|
||||
|
||||
if (context->getNodeType() != Node::DOCUMENT_NODE)
|
||||
nodeSet->add(context->getOwnerDocument());
|
||||
else
|
||||
nodeSet->add(context);
|
||||
|
||||
return nodeSet;
|
||||
} //-- evaluate
|
||||
|
||||
|
@ -52,10 +72,7 @@ double RootExpr::getDefaultPriority(Node* node, Node* context, ContextState* cs)
|
|||
* the given context
|
||||
**/
|
||||
MBool RootExpr::matches(Node* node, Node* context, ContextState* cs) {
|
||||
if ( node ) {
|
||||
return (MBool) (node->getNodeType() == Node::DOCUMENT_NODE);
|
||||
}
|
||||
return MB_FALSE;
|
||||
return node && (node->getNodeType() == Node::DOCUMENT_NODE);
|
||||
} //-- matches
|
||||
|
||||
/**
|
||||
|
@ -67,6 +84,6 @@ MBool RootExpr::matches(Node* node, Node* context, ContextState* cs) {
|
|||
* @return the String representation of this Expr.
|
||||
**/
|
||||
void RootExpr::toString(String& dest) {
|
||||
dest.append('/');
|
||||
if (mSerialize)
|
||||
dest.append('/');
|
||||
} //-- toString
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче