Browse Source

XPath: reworked step processing:
* For ancestor and ancestor-or-self axes, added checks for attribute nodes similar to parent axis.
* For reverse axes, collect and filter nodes in 'natural' (i.e. reversed) order, and only then reverse order while adding to result node set. This is much simpler to implement.
* Fixed memory leak (not destroying TXPathFilterNode.FExpr)

git-svn-id: trunk@15652 -

sergei 15 years ago
parent
commit
ae7aef0861
1 changed files with 52 additions and 43 deletions
  1. 52 43
      packages/fcl-xml/src/xpath.pp

+ 52 - 43
packages/fcl-xml/src/xpath.pp

@@ -1049,6 +1049,7 @@ destructor TXPathFilterNode.Destroy;
 var
   i: Integer;
 begin
+  FExpr.Free;
   for i := 0 to High(FPredicates) do
     FPredicates[i].Free;
   inherited Destroy;
@@ -1121,7 +1122,6 @@ var
   Node, Node2: TDOMNode;
   Attr: TDOMNamedNodeMap;
   i: Integer;
-  TempList: TFPList;
 
   procedure DoNodeTest(Node: TDOMNode);
   begin
@@ -1169,12 +1169,29 @@ var
     end;
   end;
 
+  procedure AddDescendantsReverse(CurNode: TDOMNode);
+  var
+    Child: TDOMNode;
+  begin
+    Child := CurNode.LastChild;
+    while Assigned(Child) do
+    begin
+      AddDescendantsReverse(Child);
+      DoNodeTest(Child);
+      Child := Child.PreviousSibling;
+    end;
+  end;
+
 begin
   ResultNodes := TNodeSet.Create;
   case Axis of
     axisAncestor:
       begin
-        Node := ANode.ParentNode;
+        // TODO: same check needed for XPATH_NAMESPACE_NODE
+        if ANode.nodeType = ATTRIBUTE_NODE then
+          Node := TDOMAttr(ANode).ownerElement
+        else
+          Node := ANode.ParentNode;
         while Assigned(Node) do
         begin
           DoNodeTest(Node);
@@ -1183,11 +1200,17 @@ begin
       end;
     axisAncestorOrSelf:
       begin
-        Node := ANode;
-        repeat
+        DoNodeTest(ANode);
+        // TODO: same check needed for XPATH_NAMESPACE_NODE
+        if ANode.nodeType = ATTRIBUTE_NODE then
+          Node := TDOMAttr(ANode).ownerElement
+        else
+          Node := ANode.ParentNode;
+        while Assigned(Node) do
+        begin
           DoNodeTest(Node);
           Node := Node.ParentNode;
-        until not Assigned(Node);
+        end;
       end;
     axisAttribute:
       begin
@@ -1246,41 +1269,25 @@ begin
         DoNodeTest(ANode.ParentNode);
     axisPreceding:
       begin
-        TempList := TFPList.Create;
-        try
-          Node := ANode;
-          // build list of ancestors
-          while Assigned(Node) do
-          begin
-            TempList.Add(Node);
-            Node := Node.ParentNode;
-          end;
-          // then process it in reverse order
-          for i := TempList.Count-1 downto 1 do
+        Node := ANode;
+        repeat
+          Node2 := Node.PreviousSibling;
+          while Assigned(Node2) do
           begin
-            Node := TDOMNode(TempList[i]);
-            Node2 := Node.FirstChild;
-            while Assigned(Node2) and (Node2 <> TDOMNode(TempList[i-1])) do
-            begin
-              DoNodeTest(Node2);
-              AddDescendants(Node2);
-              Node2 := Node2.NextSibling;
-            end;
+            AddDescendantsReverse(Node2);
+            DoNodeTest(Node2);
+            Node2 := Node2.PreviousSibling;
           end;
-        finally
-          TempList.Free;
-        end;
+          Node := Node.ParentNode;
+        until not Assigned(Node);
       end;
     axisPrecedingSibling:
       begin
-        if Assigned(ANode.ParentNode) then
+        Node := ANode.PreviousSibling;
+        while Assigned(Node) do
         begin
-          Node := ANode.ParentNode.FirstChild;
-          while Assigned(Node) and (Node <> ANode) do
-          begin
-            DoNodeTest(Node);
-            Node := Node.NextSibling;
-          end;
+          DoNodeTest(Node);
+          Node := Node.PreviousSibling;
         end;
       end;
     axisSelf:
@@ -1309,12 +1316,7 @@ begin
     try
       for j := 0 to Nodes.Count - 1 do
       begin
-        // ContextPosition must honor the axis direction
-        if Axis in [axisAncestor, axisAncestorOrSelf,
-          axisPreceding, axisPrecedingSibling] then
-          NewContext.ContextPosition := Nodes.Count - j
-        else
-          NewContext.ContextPosition := j+1;
+        NewContext.ContextPosition := j+1;
         NewContext.ContextNode := TDOMNode(Nodes[j]);
         if not Predicates[i].EvalPredicate(NewContext, AEnvironment) then
           Nodes[j] := nil;
@@ -1342,7 +1344,15 @@ var
     SelectNodes(AContextNode, StepNodes);
     try
       ApplyPredicates(StepNodes, AEnvironment);
-      for i := 0 to StepNodes.Count - 1 do
+      if Axis in [axisAncestor, axisAncestorOrSelf,
+        axisPreceding, axisPrecedingSibling] then
+      for i := StepNodes.Count - 1 downto 0 do
+      begin
+        Node := TDOMNode(StepNodes[i]);
+        if ResultNodeSet.IndexOf(Node) < 0 then
+          ResultNodeSet.Add(Node);
+      end
+      else for i := 0 to StepNodes.Count - 1 do
       begin
         Node := TDOMNode(StepNodes[i]);
         if ResultNodeSet.IndexOf(Node) < 0 then
@@ -2059,10 +2069,9 @@ begin
       Exit;  // allow '/' alone
   end;
 
+  // Continue with parsing of [3] RelativeLocationPath
   repeat
     Result := AddStep(Result, ParseStep);
-
-    // Continue with parsing of [3] RelativeLocationPath
     if CurToken = tkSlashSlash then
     begin
       NextToken;