View Javadoc

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