001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.ri.model.beans;
017:
018: import org.apache.commons.jxpath.JXPathException;
019: import org.apache.commons.jxpath.ri.model.NodeIterator;
020: import org.apache.commons.jxpath.ri.model.NodePointer;
021:
022: /**
023: * Iterates property values of an object pointed at with a PropertyOwnerPointer.
024: * Examples of such objects are JavaBeans and objects with Dynamic Properties.
025: *
026: * @author Dmitri Plotnikov
027: * @version $Revision: 1.13 $ $Date: 2004/03/25 03:49:50 $
028: */
029: public class PropertyIterator implements NodeIterator {
030: private boolean empty = false;
031: private boolean reverse;
032: private String name;
033: private int startIndex = 0;
034: private boolean targetReady = false;
035: private int position = 0;
036: private PropertyPointer propertyNodePointer;
037: private int startPropertyIndex;
038:
039: private boolean ready = false;
040: private boolean includeStart = false;
041:
042: public PropertyIterator(PropertyOwnerPointer pointer, String name,
043: boolean reverse, NodePointer startWith) {
044: propertyNodePointer = (PropertyPointer) pointer
045: .getPropertyPointer().clone();
046: this .name = name;
047: this .reverse = reverse;
048: this .includeStart = true;
049: if (reverse) {
050: this .startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
051: this .startIndex = -1;
052: }
053: if (startWith != null) {
054: while (startWith != null
055: && startWith.getImmediateParentPointer() != pointer) {
056: startWith = startWith.getImmediateParentPointer();
057: }
058: if (startWith == null) {
059: throw new JXPathException(
060: "PropertyIerator startWith parameter is "
061: + "not a child of the supplied parent");
062: }
063: this .startPropertyIndex = ((PropertyPointer) startWith)
064: .getPropertyIndex();
065: this .startIndex = startWith.getIndex();
066: if (this .startIndex == NodePointer.WHOLE_COLLECTION) {
067: this .startIndex = 0;
068: }
069: this .includeStart = false;
070: if (reverse && startIndex == -1) {
071: this .includeStart = true;
072: }
073: }
074: }
075:
076: protected NodePointer getPropertyPointer() {
077: return propertyNodePointer;
078: }
079:
080: public void reset() {
081: position = 0;
082: targetReady = false;
083: }
084:
085: public NodePointer getNodePointer() {
086: if (position == 0) {
087: if (name != null) {
088: if (!targetReady) {
089: prepareForIndividualProperty(name);
090: }
091: // If there is no such property - return null
092: if (empty) {
093: return null;
094: }
095: } else {
096: if (!setPosition(1)) {
097: return null;
098: }
099: reset();
100: }
101: }
102: try {
103: return propertyNodePointer.getValuePointer();
104: } catch (Throwable ex) {
105: // @todo: should this exception be reported in any way?
106: NullPropertyPointer npp = new NullPropertyPointer(
107: propertyNodePointer.getImmediateParentPointer());
108: npp.setPropertyName(propertyNodePointer.getPropertyName());
109: npp.setIndex(propertyNodePointer.getIndex());
110: return npp.getValuePointer();
111: }
112: }
113:
114: public int getPosition() {
115: return position;
116: }
117:
118: public boolean setPosition(int position) {
119: if (name != null) {
120: return setPositionIndividualProperty(position);
121: } else {
122: return setPositionAllProperties(position);
123: }
124: }
125:
126: private boolean setPositionIndividualProperty(int position) {
127: this .position = position;
128: if (position < 1) {
129: return false;
130: }
131:
132: if (!targetReady) {
133: prepareForIndividualProperty(name);
134: }
135:
136: if (empty) {
137: return false;
138: }
139:
140: int length = getLength();
141: int index;
142: if (!reverse) {
143: index = position + startIndex;
144: if (!includeStart) {
145: index++;
146: }
147: if (index > length) {
148: return false;
149: }
150: } else {
151: int end = startIndex;
152: if (end == -1) {
153: end = length - 1;
154: }
155: index = end - position + 2;
156: if (!includeStart) {
157: index--;
158: }
159: if (index < 1) {
160: return false;
161: }
162: }
163: propertyNodePointer.setIndex(index - 1);
164: return true;
165: }
166:
167: private boolean setPositionAllProperties(int position) {
168: this .position = position;
169: if (position < 1) {
170: return false;
171: }
172:
173: int offset;
174: int count = propertyNodePointer.getPropertyCount();
175: if (!reverse) {
176: int index = 1;
177: for (int i = startPropertyIndex; i < count; i++) {
178: propertyNodePointer.setPropertyIndex(i);
179: int length = getLength();
180: if (i == startPropertyIndex) {
181: length -= startIndex;
182: if (!includeStart) {
183: length--;
184: }
185: offset = startIndex + position - index;
186: if (!includeStart) {
187: offset++;
188: }
189: } else {
190: offset = position - index;
191: }
192: if (index <= position && position < index + length) {
193: propertyNodePointer.setIndex(offset);
194: return true;
195: }
196: index += length;
197: }
198: } else {
199: int index = 1;
200: int start = startPropertyIndex;
201: if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
202: start = count - 1;
203: }
204: for (int i = start; i >= 0; i--) {
205: propertyNodePointer.setPropertyIndex(i);
206: int length = getLength();
207: if (i == startPropertyIndex) {
208: int end = startIndex;
209: if (end == -1) {
210: end = length - 1;
211: }
212: length = end + 1;
213: offset = end - position + 1;
214: if (!includeStart) {
215: offset--;
216: length--;
217: }
218: } else {
219: offset = length - (position - index) - 1;
220: }
221:
222: if (index <= position && position < index + length) {
223: propertyNodePointer.setIndex(offset);
224: return true;
225: }
226: index += length;
227: }
228: }
229: return false;
230: }
231:
232: protected void prepareForIndividualProperty(String name) {
233: targetReady = true;
234: empty = true;
235:
236: String names[] = propertyNodePointer.getPropertyNames();
237: if (!reverse) {
238: if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
239: startPropertyIndex = 0;
240: }
241: if (startIndex == NodePointer.WHOLE_COLLECTION) {
242: startIndex = 0;
243: }
244: for (int i = startPropertyIndex; i < names.length; i++) {
245: if (names[i].equals(name)) {
246: propertyNodePointer.setPropertyIndex(i);
247: if (i != startPropertyIndex) {
248: startIndex = 0;
249: includeStart = true;
250: }
251: empty = false;
252: break;
253: }
254: }
255: } else {
256: if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
257: startPropertyIndex = names.length - 1;
258: }
259: if (startIndex == NodePointer.WHOLE_COLLECTION) {
260: startIndex = -1;
261: }
262: for (int i = startPropertyIndex; i >= 0; i--) {
263: if (names[i].equals(name)) {
264: propertyNodePointer.setPropertyIndex(i);
265: if (i != startPropertyIndex) {
266: startIndex = -1;
267: includeStart = true;
268: }
269: empty = false;
270: break;
271: }
272: }
273: }
274: }
275:
276: /**
277: * Computes length for the current pointer - ignores any exceptions
278: */
279: private int getLength() {
280: int length;
281: try {
282: length = propertyNodePointer.getLength(); // TBD: cache length
283: } catch (Throwable t) {
284: // @todo: should this exception be reported in any way?
285: length = 0;
286: }
287: return length;
288: }
289: }
|