Make PathExpr::matches more clever and remove specialcasing of absolute path expressions

b=97107 r=peterv sr=jst
This commit is contained in:
sicking%bigfoot.com 2001-09-14 11:49:50 +00:00
Родитель 6b95ff030d
Коммит 4dda9dee83
4 изменённых файлов: 145 добавлений и 181 удалений

Просмотреть файл

@ -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