    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.ri.axes;
    17
    18  import java.util.Stack;
    19
    20  import org.apache.commons.jxpath.Pointer;
    21  import org.apache.commons.jxpath.ri.Compiler;
    22  import org.apache.commons.jxpath.ri.EvalContext;
    23  import org.apache.commons.jxpath.ri.compiler.NodeTest;
    24  import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
    25  import org.apache.commons.jxpath.ri.model.NodeIterator;
    26  import org.apache.commons.jxpath.ri.model.NodePointer;
    27
    28  /***
    29   * An EvalContext that walks the "descendant::" and "descendant-or-self::"
    30   * axes.
    31   *
    32   * @author Dmitri Plotnikov
    33   * @version $Revision: 1.16 $ $Date: 2004/02/29 14:17:38 $
    34   */
    35  public class DescendantContext extends EvalContext {
    36      private NodeTest nodeTest;
    37      private boolean setStarted = false;
    38      private Stack stack;
    39      private NodePointer currentNodePointer;
    40      private boolean includeSelf;
    41      private static final NodeTest ELEMENT_NODE_TEST =
    42              new NodeTypeTest(Compiler.NODE_TYPE_NODE);
    43
    44      public DescendantContext(
    45              EvalContext parentContext,
    46              boolean includeSelf,
    47              NodeTest nodeTest)
    48      {
    49          super(parentContext);
    50          this.includeSelf = includeSelf;
    51          this.nodeTest = nodeTest;
    52      }
    53
    54      public boolean isChildOrderingRequired() {
    55          return true;
    56      }
    57
    58      public NodePointer getCurrentNodePointer() {
    59          if (position == 0) {
    60              if (!setPosition(1)) {
    61                  return null;
    62              }
    63          }
    64          return currentNodePointer;
    65      }
    66
    67      public void reset() {
    68          super.reset();
    69          setStarted = false;
    70      }
    71
    72      public boolean setPosition(int position) {
    73          if (position < this.position) {
    74              reset();
    75          }
    76
    77          while (this.position < position) {
    78              if (!nextNode()) {
    79                  return false;
    80              }
    81          }
    82          return true;
    83      }
    84
    85      public boolean nextNode() {
    86          if (!setStarted) {
    87              setStarted = true;
    88              stack = new Stack();
    89              currentNodePointer = parentContext.getCurrentNodePointer();
    90              if (currentNodePointer != null) {
    91                  if (!currentNodePointer.isLeaf()) {
    92                      stack.push(
    93                          currentNodePointer.childIterator(
    94                              ELEMENT_NODE_TEST,
    95                              false,
    96                              null));
    97                  }
    98                  if (includeSelf) {
    99                      if (currentNodePointer.testNode(nodeTest)) {
    100                         position++;
    101                         return true;
    102                     }
    103                 }
    104             }
    105         }
    106
    107         while (!stack.isEmpty()) {
    108             NodeIterator it = (NodeIterator) stack.peek();
    109             if (it.setPosition(it.getPosition() + 1)) {
    110                 currentNodePointer = it.getNodePointer();
    111                 if (!isRecursive()) {
    112                     if (!currentNodePointer.isLeaf()) {
    113                         stack.push(
    114                             currentNodePointer.childIterator(
    115                                 ELEMENT_NODE_TEST,
    116                                 false,
    117                                 null));
    118                     }
    119                     if (currentNodePointer.testNode(nodeTest)) {
    120                         position++;
    121                         return true;
    122                     }
    123                 }
    124             }
    125             else {
    126                 // We get here only if the name test failed
    127                 // and the iterator ended
    128                 stack.pop();
    129             }
    130         }
    131         return false;
    132     }
    133
    134     /***
    135      * Checks if we are reentering a bean we have already seen and if so
    136      * returns true to prevent infinite recursion.
    137      */
    138     private boolean isRecursive() {
    139         Object node = currentNodePointer.getNode();
    140         for (int i = stack.size() - 1; --i >= 0;) {
    141             NodeIterator it = (NodeIterator) stack.get(i);
    142             Pointer pointer = it.getNodePointer();
    143             if (pointer != null && pointer.getNode() == node) {
    144                 return true;
    145             }
    146         }
    147         return false;
    148     }
    149 }