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.beans;
19  
20  import org.apache.commons.jxpath.JXPathException;
21  import org.apache.commons.jxpath.ri.model.NodeIterator;
22  import org.apache.commons.jxpath.ri.model.NodePointer;
23  
24  /**
25   * Iterates property values of an object pointed at with a {@link PropertyOwnerPointer}. Examples of such objects are JavaBeans and objects with Dynamic
26   * Properties.
27   */
28  public class PropertyIterator implements NodeIterator {
29  
30      private boolean empty;
31      private final boolean reverse;
32      private final String name;
33      private int startIndex;
34      private boolean targetReady;
35      private int position;
36      private final PropertyPointer propertyNodePointer;
37      private int startPropertyIndex;
38      private boolean includeStart;
39  
40      /**
41       * Constructs a new PropertyIterator.
42       *
43       * @param pointer   owning pointer
44       * @param name      property name
45       * @param reverse   iteration order
46       * @param startWith beginning pointer
47       */
48      public PropertyIterator(final PropertyOwnerPointer pointer, final String name, final boolean reverse, NodePointer startWith) {
49          propertyNodePointer = (PropertyPointer) pointer.getPropertyPointer().clone();
50          this.name = name;
51          this.reverse = reverse;
52          this.includeStart = true;
53          if (reverse) {
54              this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
55              this.startIndex = -1;
56          }
57          if (startWith != null) {
58              while (startWith != null && startWith.getImmediateParentPointer() != pointer) {
59                  startWith = startWith.getImmediateParentPointer();
60              }
61              if (startWith == null) {
62                  throw new JXPathException("PropertyIerator startWith parameter is " + "not a child of the supplied parent");
63              }
64              this.startPropertyIndex = ((PropertyPointer) startWith).getPropertyIndex();
65              this.startIndex = startWith.getIndex();
66              if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
67                  this.startIndex = 0;
68              }
69              this.includeStart = false;
70              if (reverse && startIndex == -1) {
71                  this.includeStart = true;
72              }
73          }
74      }
75  
76      /**
77       * Computes length for the current pointer - ignores any exceptions.
78       *
79       * @return length
80       */
81      private int getLength() {
82          int length;
83          try {
84              length = propertyNodePointer.getLength(); // TBD: cache length
85          } catch (final Throwable t) {
86              propertyNodePointer.handle(t);
87              length = 0;
88          }
89          return length;
90      }
91  
92      @Override
93      public NodePointer getNodePointer() {
94          if (position == 0) {
95              if (name != null) {
96                  if (!targetReady) {
97                      prepareForIndividualProperty(name);
98                  }
99                  // If there is no such property - return null
100                 if (empty) {
101                     return null;
102                 }
103             } else {
104                 if (!setPosition(1)) {
105                     return null;
106                 }
107                 reset();
108             }
109         }
110         try {
111             return propertyNodePointer.getValuePointer();
112         } catch (final Throwable t) {
113             propertyNodePointer.handle(t);
114             final NullPropertyPointer npp = new NullPropertyPointer(propertyNodePointer.getImmediateParentPointer());
115             npp.setPropertyName(propertyNodePointer.getPropertyName());
116             npp.setIndex(propertyNodePointer.getIndex());
117             return npp.getValuePointer();
118         }
119     }
120 
121     @Override
122     public int getPosition() {
123         return position;
124     }
125 
126     /**
127      * Gets the property pointer.
128      *
129      * @return NodePointer
130      */
131     protected NodePointer getPropertyPointer() {
132         return propertyNodePointer;
133     }
134 
135     /**
136      * Prepare for an individual property.
137      *
138      * @param name property name
139      */
140     protected void prepareForIndividualProperty(final String name) {
141         targetReady = true;
142         empty = true;
143         final String[] names = propertyNodePointer.getPropertyNames();
144         if (!reverse) {
145             if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
146                 startPropertyIndex = 0;
147             }
148             if (startIndex == NodePointer.WHOLE_COLLECTION) {
149                 startIndex = 0;
150             }
151             for (int i = startPropertyIndex; i < names.length; i++) {
152                 if (names[i].equals(name)) {
153                     propertyNodePointer.setPropertyIndex(i);
154                     if (i != startPropertyIndex) {
155                         startIndex = 0;
156                         includeStart = true;
157                     }
158                     empty = false;
159                     break;
160                 }
161             }
162         } else {
163             if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
164                 startPropertyIndex = names.length - 1;
165             }
166             if (startIndex == NodePointer.WHOLE_COLLECTION) {
167                 startIndex = -1;
168             }
169             for (int i = startPropertyIndex; i >= 0; i--) {
170                 if (names[i].equals(name)) {
171                     propertyNodePointer.setPropertyIndex(i);
172                     if (i != startPropertyIndex) {
173                         startIndex = -1;
174                         includeStart = true;
175                     }
176                     empty = false;
177                     break;
178                 }
179             }
180         }
181     }
182 
183     /**
184      * Reset property iteration.
185      */
186     public void reset() {
187         position = 0;
188         targetReady = false;
189     }
190 
191     @Override
192     public boolean setPosition(final int position) {
193         return name == null ? setPositionAllProperties(position) : setPositionIndividualProperty(position);
194     }
195 
196     /**
197      * Sets position for all properties
198      *
199      * @param position int position
200      * @return whether this was a valid position
201      */
202     private boolean setPositionAllProperties(final int position) {
203         this.position = position;
204         if (position < 1) {
205             return false;
206         }
207         int offset;
208         final int count = propertyNodePointer.getPropertyCount();
209         if (!reverse) {
210             int index = 1;
211             for (int i = startPropertyIndex; i < count; i++) {
212                 propertyNodePointer.setPropertyIndex(i);
213                 int length = getLength();
214                 if (i == startPropertyIndex) {
215                     length -= startIndex;
216                     if (!includeStart) {
217                         length--;
218                     }
219                     offset = startIndex + position - index;
220                     if (!includeStart) {
221                         offset++;
222                     }
223                 } else {
224                     offset = position - index;
225                 }
226                 if (index <= position && position < index + length) {
227                     propertyNodePointer.setIndex(offset);
228                     return true;
229                 }
230                 index += length;
231             }
232         } else {
233             int index = 1;
234             int start = startPropertyIndex;
235             if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
236                 start = count - 1;
237             }
238             for (int i = start; i >= 0; i--) {
239                 propertyNodePointer.setPropertyIndex(i);
240                 int length = getLength();
241                 if (i == startPropertyIndex) {
242                     int end = startIndex;
243                     if (end == -1) {
244                         end = length - 1;
245                     }
246                     length = end + 1;
247                     offset = end - position + 1;
248                     if (!includeStart) {
249                         offset--;
250                         length--;
251                     }
252                 } else {
253                     offset = length - (position - index) - 1;
254                 }
255                 if (index <= position && position < index + length) {
256                     propertyNodePointer.setIndex(offset);
257                     return true;
258                 }
259                 index += length;
260             }
261         }
262         return false;
263     }
264 
265     /**
266      * Sets position for an individual property.
267      *
268      * @param position int position
269      * @return whether this was a valid position
270      */
271     private boolean setPositionIndividualProperty(final int position) {
272         this.position = position;
273         if (position < 1) {
274             return false;
275         }
276         if (!targetReady) {
277             prepareForIndividualProperty(name);
278         }
279         if (empty) {
280             return false;
281         }
282         final int length = getLength();
283         int index;
284         if (!reverse) {
285             index = position + startIndex;
286             if (!includeStart) {
287                 index++;
288             }
289             if (index > length) {
290                 return false;
291             }
292         } else {
293             int end = startIndex;
294             if (end == -1) {
295                 end = length - 1;
296             }
297             index = end - position + 2;
298             if (!includeStart) {
299                 index--;
300             }
301             if (index < 1) {
302                 return false;
303             }
304         }
305         propertyNodePointer.setIndex(index - 1);
306         return true;
307     }
308 }