* Refactored TXPathLocationPathNode.Evaluate(), split that awful 3-level nested procedure into

TStep.SelectNodes, TStep.ApplyPredicates and the remaining part.
* Since predicates contained in a location path are evaluated within separate contexts of their own,
  evaluation of the location path itself does not require a full context (only need context nodes).
  This simplifies things quite a bit.
+ Added support for evaluating filter expressions follwed by location path. Things like
  "id('foo')/bar" work now.

git-svn-id: trunk@13244 -
This commit is contained in:
sergei 2009-06-06 18:51:50 +00:00
parent ba951580a2
commit 26767125f0

View File

@ -240,7 +240,12 @@ type
TNodeTestType = (ntAnyPrincipal, ntName, ntTextNode,
ntCommentNode, ntPINode, ntAnyNode);
TNodeSet = TFPList;
TStep = class
private
procedure SelectNodes(ANode: TDOMNode; out ResultNodes: TNodeSet);
procedure ApplyPredicates(Nodes: TNodeSet; AEnvironment: TXPathEnvironment);
public
NextStep: TStep;
Axis: TAxis;
@ -263,9 +268,6 @@ type
AEnvironment: TXPathEnvironment): TXPathVariable; override;
end;
TNodeSet = TFPList;
{ Exceptions }
EXPathEvaluationError = class(Exception);
@ -1067,32 +1069,23 @@ begin
inherited destroy;
end;
constructor TXPathLocationPathNode.Create(ALeft: TXPathExprNode; AIsAbsolutePath: Boolean);
begin
inherited Create;
FLeft := ALeft;
FIsAbsolutePath := AIsAbsolutePath;
end;
function TXPathLocationPathNode.Evaluate(AContext: TXPathContext;
AEnvironment: TXPathEnvironment): TXPathVariable;
procedure TStep.SelectNodes(ANode: TDOMNode; out ResultNodes: TNodeSet);
var
ResultNodeSet: TNodeSet;
procedure EvaluateStep(AStep: TStep; AContext: TXPathContext);
var
StepNodes: TFPList;
Node, Node2: TDOMNode;
Attr: TDOMNamedNodeMap;
i: Integer;
TempList: TFPList;
procedure DoNodeTest(Node: TDOMNode);
begin
case AStep.NodeTestType of
case NodeTestType of
ntAnyPrincipal:
// !!!: Probably this isn't ready for namespace support yet
if (AStep.Axis <> axisAttribute) and
if (Axis <> axisAttribute) and
(Node.NodeType <> ELEMENT_NODE) then
exit;
ntName:
if Node.NodeName <> AStep.NodeTestString then
if Node.NodeName <> NodeTestString then
exit;
ntTextNode:
if not Node.InheritsFrom(TDOMCharacterData) then
@ -1104,8 +1097,8 @@ var
if Node.NodeType <> PROCESSING_INSTRUCTION_NODE then
exit;
end;
if StepNodes.IndexOf(Node) < 0 then
StepNodes.Add(Node);
if ResultNodes.IndexOf(Node) < 0 then
ResultNodes.Add(Node);
end;
procedure AddDescendants(CurNode: TDOMNode);
@ -1121,22 +1114,12 @@ var
end;
end;
var
Node, Node2: TDOMNode;
Attr: TDOMNamedNodeMap;
i, j: Integer;
NewContext: TXPathContext;
Predicate: TXPathExprNode;
TempList: TFPList;
begin
StepNodes := TFPList.Create;
// !!!: Protect this with an try/finally block
case AStep.Axis of
begin
ResultNodes := TNodeSet.Create;
case Axis of
axisAncestor:
begin
Node := AContext.ContextNode.ParentNode;
Node := ANode.ParentNode;
while Assigned(Node) do
begin
DoNodeTest(Node);
@ -1145,7 +1128,7 @@ var
end;
axisAncestorOrSelf:
begin
Node := AContext.ContextNode;
Node := ANode;
repeat
DoNodeTest(Node);
Node := Node.ParentNode;
@ -1153,14 +1136,14 @@ var
end;
axisAttribute:
begin
Attr := AContext.ContextNode.Attributes;
Attr := ANode.Attributes;
if Assigned(Attr) then
for i := 0 to Attr.Length - 1 do
DoNodeTest(Attr[i]);
end;
axisChild:
begin
Node := AContext.ContextNode.FirstChild;
Node := ANode.FirstChild;
while Assigned(Node) do
begin
DoNodeTest(Node);
@ -1168,15 +1151,15 @@ var
end;
end;
axisDescendant:
AddDescendants(AContext.ContextNode);
AddDescendants(ANode);
axisDescendantOrSelf:
begin
DoNodeTest(AContext.ContextNode);
AddDescendants(AContext.ContextNode);
DoNodeTest(ANode);
AddDescendants(ANode);
end;
axisFollowing:
begin
Node := AContext.ContextNode;
Node := ANode;
repeat
Node2 := Node.NextSibling;
while Assigned(Node2) do
@ -1190,7 +1173,7 @@ var
end;
axisFollowingSibling:
begin
Node := AContext.ContextNode.NextSibling;
Node := ANode.NextSibling;
while Assigned(Node) do
begin
DoNodeTest(Node);
@ -1199,13 +1182,13 @@ var
end;
{axisNamespace: !!!: Not supported yet}
axisParent:
if Assigned(AContext.ContextNode.ParentNode) then
DoNodeTest(AContext.ContextNode);
if Assigned(ANode.ParentNode) then
DoNodeTest(ANode.ParentNode);
axisPreceding:
begin
TempList := TFPList.Create;
try
Node := AContext.ContextNode;
Node := ANode;
// build list of ancestors
while Assigned(Node) do
begin
@ -1230,10 +1213,10 @@ var
end;
axisPrecedingSibling:
begin
if Assigned(AContext.ContextNode.ParentNode) then
if Assigned(ANode.ParentNode) then
begin
Node := AContext.ContextNode.ParentNode.FirstChild;
while Assigned(Node) and (Node <> AContext.ContextNode) do
Node := ANode.ParentNode.FirstChild;
while Assigned(Node) and (Node <> ANode) do
begin
DoNodeTest(Node);
Node := Node.NextSibling;
@ -1241,54 +1224,71 @@ var
end;
end;
axisSelf:
DoNodeTest(AContext.ContextNode);
DoNodeTest(ANode);
end;
end;
{ Filter the nodes of this step using the predicates: The current
node set (StepNodes) is filtered, nodes not passing the filter
are replaced by nil. After one filter has been applied, StepNodes
is packed, and the next filter will be processed.
The final result will then be passed to the next step, or added
to the result of the LocationPath if this is the last step. }
{ Filter the nodes of this step using the predicates: The current
node set is filtered, nodes not passing the filter are replaced
by nil. After one filter has been applied, Nodes is packed, and
the next filter will be processed. }
for i := 0 to High(AStep.Predicates) do
procedure TStep.ApplyPredicates(Nodes: TNodeSet; AEnvironment: TXPathEnvironment);
var
i, j: Integer;
NewContext: TXPathContext;
begin
for i := 0 to High(Predicates) do
begin
NewContext := TXPathContext.Create(nil, 0, StepNodes.Count);
NewContext := TXPathContext.Create(nil, 0, Nodes.Count);
try
Predicate := AStep.Predicates[i];
for j := 0 to StepNodes.Count - 1 do
for j := 0 to Nodes.Count - 1 do
begin
// ContextPosition must honor the axis direction
if AStep.Axis in [axisAncestor, axisAncestorOrSelf,
if Axis in [axisAncestor, axisAncestorOrSelf,
axisPreceding, axisPrecedingSibling] then
NewContext.ContextPosition := StepNodes.Count - j
NewContext.ContextPosition := Nodes.Count - j
else
NewContext.ContextPosition := j+1;
Node := TDOMNode(StepNodes[j]);
NewContext.ContextNode := Node;
if not Predicate.EvalPredicate(NewContext, AEnvironment) then
StepNodes[j] := nil;
NewContext.ContextNode := TDOMNode(Nodes[j]);
if not Predicates[i].EvalPredicate(NewContext, AEnvironment) then
Nodes[j] := nil;
end;
StepNodes.Pack;
Nodes.Pack;
finally
NewContext.Free;
end;
end;
end;
constructor TXPathLocationPathNode.Create(ALeft: TXPathExprNode; AIsAbsolutePath: Boolean);
begin
inherited Create;
FLeft := ALeft;
FIsAbsolutePath := AIsAbsolutePath;
end;
function TXPathLocationPathNode.Evaluate(AContext: TXPathContext;
AEnvironment: TXPathEnvironment): TXPathVariable;
var
ResultNodeSet: TNodeSet;
LeftResult: TXPathVariable;
i: Integer;
procedure EvaluateStep(AStep: TStep; AContextNode: TDOMNode);
var
StepNodes: TFPList;
Node: TDOMNode;
i: Integer;
begin
AStep.SelectNodes(AContextNode, StepNodes);
try
AStep.ApplyPredicates(StepNodes, AEnvironment);
if Assigned(AStep.NextStep) then
begin
NewContext := TXPathContext.Create(nil, 0, StepNodes.Count);
try
for i := 0 to StepNodes.Count - 1 do
begin
NewContext.ContextNode := TDOMNode(StepNodes[i]);
Inc(NewContext.ContextPosition);
EvaluateStep(AStep.NextStep, NewContext);
end;
finally
NewContext.Free;
end;
EvaluateStep(AStep.NextStep, TDOMNode(StepNodes[i]));
end else
begin
// Only add nodes to result if it isn't duplicate
@ -1299,30 +1299,29 @@ var
ResultNodeSet.Add(Node);
end;
end;
finally
StepNodes.Free;
end;
end;
var
NewContext: TXPathContext;
begin
ResultNodeSet := TNodeSet.Create;
try
if FIsAbsolutePath then
if Assigned(FLeft) then
begin
if AContext.ContextNode.NodeType = DOCUMENT_NODE then
NewContext := TXPathContext.Create(AContext.ContextNode, 1, 1)
else
NewContext := TXPathContext.Create(AContext.ContextNode.OwnerDocument,
1, 1);
LeftResult := FLeft.Evaluate(AContext, AEnvironment);
try
EvaluateStep(FFirstStep, NewContext);
with LeftResult.AsNodeSet do
for i := 0 to Count-1 do
EvaluateStep(FFirstStep, TDOMNode(Items[i]));
finally
NewContext.Free;
LeftResult.Release;
end;
end
else if FIsAbsolutePath and (AContext.ContextNode.NodeType <> DOCUMENT_NODE) then
EvaluateStep(FFirstStep, AContext.ContextNode.OwnerDocument)
else
EvaluateStep(FFirstStep, AContext);
EvaluateStep(FFirstStep, AContext.ContextNode);
except
ResultNodeSet.Free;
raise;