/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jxpath.ri.model.beans;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.ri.model.NodeIterator;
import org.apache.commons.jxpath.ri.model.NodePointer;
/**
* Iterates property values of an object pointed at with a {@link PropertyOwnerPointer}.
* Examples of such objects are JavaBeans and objects with Dynamic Properties.
*
* @author Dmitri Plotnikov
* @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
*/
public class PropertyIterator implements NodeIterator {
private boolean empty = false;
private boolean reverse;
private String name;
private int startIndex = 0;
private boolean targetReady = false;
private int position = 0;
private PropertyPointer propertyNodePointer;
private int startPropertyIndex;
private boolean includeStart = false;
/**
* Create a new PropertyIterator.
* @param pointer owning pointer
* @param name property name
* @param reverse iteration order
* @param startWith beginning pointer
*/
public PropertyIterator(
PropertyOwnerPointer pointer,
String name,
boolean reverse,
NodePointer startWith) {
propertyNodePointer =
(PropertyPointer) pointer.getPropertyPointer().clone();
this.name = name;
this.reverse = reverse;
this.includeStart = true;
if (reverse) {
this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
this.startIndex = -1;
}
if (startWith != null) {
while (startWith != null
&& startWith.getImmediateParentPointer() != pointer) {
startWith = startWith.getImmediateParentPointer();
}
if (startWith == null) {
throw new JXPathException(
"PropertyIerator startWith parameter is "
+ "not a child of the supplied parent");
}
this.startPropertyIndex =
((PropertyPointer) startWith).getPropertyIndex();
this.startIndex = startWith.getIndex();
if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
this.startIndex = 0;
}
this.includeStart = false;
if (reverse && startIndex == -1) {
this.includeStart = true;
}
}
}
/**
* Get the property pointer.
* @return NodePointer
*/
protected NodePointer getPropertyPointer() {
return propertyNodePointer;
}
/**
* Reset property iteration.
*/
public void reset() {
position = 0;
targetReady = false;
}
public NodePointer getNodePointer() {
if (position == 0) {
if (name != null) {
if (!targetReady) {
prepareForIndividualProperty(name);
}
// If there is no such property - return null
if (empty) {
return null;
}
}
else {
if (!setPosition(1)) {
return null;
}
reset();
}
}
try {
return propertyNodePointer.getValuePointer();
}
catch (Throwable ex) {
// @todo: should this exception be reported in any way?
NullPropertyPointer npp =
new NullPropertyPointer(
propertyNodePointer.getImmediateParentPointer());
npp.setPropertyName(propertyNodePointer.getPropertyName());
npp.setIndex(propertyNodePointer.getIndex());
return npp.getValuePointer();
}
}
public int getPosition() {
return position;
}
public boolean setPosition(int position) {
return name == null ? setPositionAllProperties(position) : setPositionIndividualProperty(position);
}
/**
* Set position for an individual property.
* @param position int position
* @return whether this was a valid position
*/
private boolean setPositionIndividualProperty(int position) {
this.position = position;
if (position < 1) {
return false;
}
if (!targetReady) {
prepareForIndividualProperty(name);
}
if (empty) {
return false;
}
int length = getLength();
int index;
if (!reverse) {
index = position + startIndex;
if (!includeStart) {
index++;
}
if (index > length) {
return false;
}
}
else {
int end = startIndex;
if (end == -1) {
end = length - 1;
}
index = end - position + 2;
if (!includeStart) {
index--;
}
if (index < 1) {
return false;
}
}
propertyNodePointer.setIndex(index - 1);
return true;
}
/**
* Set position for all properties
* @param position int position
* @return whether this was a valid position
*/
private boolean setPositionAllProperties(int position) {
this.position = position;
if (position < 1) {
return false;
}
int offset;
int count = propertyNodePointer.getPropertyCount();
if (!reverse) {
int index = 1;
for (int i = startPropertyIndex; i < count; i++) {
propertyNodePointer.setPropertyIndex(i);
int length = getLength();
if (i == startPropertyIndex) {
length -= startIndex;
if (!includeStart) {
length--;
}
offset = startIndex + position - index;
if (!includeStart) {
offset++;
}
}
else {
offset = position - index;
}
if (index <= position && position < index + length) {
propertyNodePointer.setIndex(offset);
return true;
}
index += length;
}
}
else {
int index = 1;
int start = startPropertyIndex;
if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
start = count - 1;
}
for (int i = start; i >= 0; i--) {
propertyNodePointer.setPropertyIndex(i);
int length = getLength();
if (i == startPropertyIndex) {
int end = startIndex;
if (end == -1) {
end = length - 1;
}
length = end + 1;
offset = end - position + 1;
if (!includeStart) {
offset--;
length--;
}
}
else {
offset = length - (position - index) - 1;
}
if (index <= position && position < index + length) {
propertyNodePointer.setIndex(offset);
return true;
}
index += length;
}
}
return false;
}
/**
* Prepare for an individual property.
* @param name property name
*/
protected void prepareForIndividualProperty(String name) {
targetReady = true;
empty = true;
String[] names = propertyNodePointer.getPropertyNames();
if (!reverse) {
if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
startPropertyIndex = 0;
}
if (startIndex == NodePointer.WHOLE_COLLECTION) {
startIndex = 0;
}
for (int i = startPropertyIndex; i < names.length; i++) {
if (names[i].equals(name)) {
propertyNodePointer.setPropertyIndex(i);
if (i != startPropertyIndex) {
startIndex = 0;
includeStart = true;
}
empty = false;
break;
}
}
}
else {
if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
startPropertyIndex = names.length - 1;
}
if (startIndex == NodePointer.WHOLE_COLLECTION) {
startIndex = -1;
}
for (int i = startPropertyIndex; i >= 0; i--) {
if (names[i].equals(name)) {
propertyNodePointer.setPropertyIndex(i);
if (i != startPropertyIndex) {
startIndex = -1;
includeStart = true;
}
empty = false;
break;
}
}
}
}
/**
* Computes length for the current pointer - ignores any exceptions.
* @return length
*/
private int getLength() {
int length;
try {
length = propertyNodePointer.getLength(); // TBD: cache length
}
catch (Throwable t) {
// @todo: should this exception be reported in any way?
length = 0;
}
return length;
}
}
|