001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.jxpath.ri.model.beans;
018
019 import org.apache.commons.jxpath.JXPathException;
020 import org.apache.commons.jxpath.ri.model.NodeIterator;
021 import org.apache.commons.jxpath.ri.model.NodePointer;
022
023 /**
024 * Iterates property values of an object pointed at with a {@link PropertyOwnerPointer}.
025 * Examples of such objects are JavaBeans and objects with Dynamic Properties.
026 *
027 * @author Dmitri Plotnikov
028 * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
029 */
030 public class PropertyIterator implements NodeIterator {
031 private boolean empty = false;
032 private boolean reverse;
033 private String name;
034 private int startIndex = 0;
035 private boolean targetReady = false;
036 private int position = 0;
037 private PropertyPointer propertyNodePointer;
038 private int startPropertyIndex;
039
040 private boolean includeStart = false;
041
042 /**
043 * Create a new PropertyIterator.
044 * @param pointer owning pointer
045 * @param name property name
046 * @param reverse iteration order
047 * @param startWith beginning pointer
048 */
049 public PropertyIterator(
050 PropertyOwnerPointer pointer,
051 String name,
052 boolean reverse,
053 NodePointer startWith) {
054 propertyNodePointer =
055 (PropertyPointer) pointer.getPropertyPointer().clone();
056 this.name = name;
057 this.reverse = reverse;
058 this.includeStart = true;
059 if (reverse) {
060 this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
061 this.startIndex = -1;
062 }
063 if (startWith != null) {
064 while (startWith != null
065 && startWith.getImmediateParentPointer() != pointer) {
066 startWith = startWith.getImmediateParentPointer();
067 }
068 if (startWith == null) {
069 throw new JXPathException(
070 "PropertyIerator startWith parameter is "
071 + "not a child of the supplied parent");
072 }
073 this.startPropertyIndex =
074 ((PropertyPointer) startWith).getPropertyIndex();
075 this.startIndex = startWith.getIndex();
076 if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
077 this.startIndex = 0;
078 }
079 this.includeStart = false;
080 if (reverse && startIndex == -1) {
081 this.includeStart = true;
082 }
083 }
084 }
085
086 /**
087 * Get the property pointer.
088 * @return NodePointer
089 */
090 protected NodePointer getPropertyPointer() {
091 return propertyNodePointer;
092 }
093
094 /**
095 * Reset property iteration.
096 */
097 public void reset() {
098 position = 0;
099 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 ex) {
124 // @todo: should this exception be reported in any way?
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 // @todo: should this exception be reported in any way?
322 length = 0;
323 }
324 return length;
325 }
326 }