View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath.ri.model.jdom;
19  
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.apache.commons.jxpath.ri.compiler.NodeTest;
24  import org.apache.commons.jxpath.ri.model.NodeIterator;
25  import org.apache.commons.jxpath.ri.model.NodePointer;
26  import org.jdom.Document;
27  import org.jdom.Element;
28  
29  /**
30   * An iterator of children of a JDOM Node.
31   */
32  public class JDOMNodeIterator implements NodeIterator {
33  
34      private final NodePointer parent;
35      private final NodeTest nodeTest;
36      private final boolean reverse;
37      private int position;
38      private int index;
39      private List children;
40      private Object child;
41  
42      /**
43       * Constructs a new JDOMNodeIterator.
44       *
45       * @param parent    pointer
46       * @param nodeTest  test
47       * @param reverse   whether to iterate in reverse
48       * @param startWith starting pointer
49       */
50      public JDOMNodeIterator(final NodePointer parent, final NodeTest nodeTest, final boolean reverse, final NodePointer startWith) {
51          this.parent = parent;
52          if (startWith != null) {
53              this.child = startWith.getNode();
54          }
55          // TBD: optimize me for different node tests
56          final Object node = parent.getNode();
57          if (node instanceof Document) {
58              this.children = ((Document) node).getContent();
59          } else if (node instanceof Element) {
60              this.children = ((Element) node).getContent();
61          } else {
62              this.children = Collections.EMPTY_LIST;
63          }
64          this.nodeTest = nodeTest;
65          this.reverse = reverse;
66      }
67  
68      @Override
69      public NodePointer getNodePointer() {
70          if (child == null) {
71              if (!setPosition(1)) {
72                  return null;
73              }
74              position = 0;
75          }
76          return new JDOMNodePointer(parent, child);
77      }
78  
79      @Override
80      public int getPosition() {
81          return position;
82      }
83  
84      /**
85       * Iterate to next pointer.
86       *
87       * @return whether valid
88       */
89      private boolean next() {
90          position++;
91          if (!reverse) {
92              if (position == 1) {
93                  index = 0;
94                  if (child != null) {
95                      index = children.indexOf(child) + 1;
96                  }
97              } else {
98                  index++;
99              }
100             for (; index < children.size(); index++) {
101                 child = children.get(index);
102                 if (testChild()) {
103                     return true;
104                 }
105             }
106             return false;
107         }
108         if (position == 1) {
109             index = children.size() - 1;
110             if (child != null) {
111                 index = children.indexOf(child) - 1;
112             }
113         } else {
114             index--;
115         }
116         for (; index >= 0; index--) {
117             child = children.get(index);
118             if (testChild()) {
119                 return true;
120             }
121         }
122         return false;
123     }
124 
125     /**
126      * This is actually never invoked during the normal evaluation of xpaths - an iterator is always going forward, never backwards. So, this is implemented
127      * only for completeness and perhaps for those who use these iterators outside of XPath evaluation.
128      *
129      * @return boolean
130      */
131     private boolean previous() {
132         position--;
133         if (!reverse) {
134             while (--index >= 0) {
135                 child = children.get(index);
136                 if (testChild()) {
137                     return true;
138                 }
139             }
140         } else {
141             for (; index < children.size(); index++) {
142                 child = children.get(index);
143                 if (testChild()) {
144                     return true;
145                 }
146             }
147         }
148         return false;
149     }
150 
151     @Override
152     public boolean setPosition(final int position) {
153         while (this.position < position) {
154             if (!next()) {
155                 return false;
156             }
157         }
158         while (this.position > position) {
159             if (!previous()) {
160                 return false;
161             }
162         }
163         return true;
164     }
165 
166     /**
167      * Test a child node.
168      *
169      * @return whether test passes.
170      */
171     private boolean testChild() {
172         return JDOMNodePointer.testNode(parent, child, nodeTest);
173     }
174 }