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