commit 9abd6d4d8e69b8f32d685f3b532358c5ea6903df Author: Brian Ewins Date: Fri Oct 16 17:28:32 2009 +0100 lazy-list performance patch diff --git a/jaxen/LICENSE.txt b/jaxen/LICENSE.txt old mode 100644 new mode 100755 diff --git a/jaxen/src/java/main/org/jaxen/BaseXPath.java b/jaxen/src/java/main/org/jaxen/BaseXPath.java index 4d54e01..5563f38 100644 --- a/jaxen/src/java/main/org/jaxen/BaseXPath.java +++ b/jaxen/src/java/main/org/jaxen/BaseXPath.java @@ -172,6 +172,8 @@ public class BaseXPath implements XPath, Serializable */ public Object evaluate(Object context) throws JaxenException { + try + { List answer = selectNodes(context); if ( answer != null @@ -191,6 +193,15 @@ public class BaseXPath implements XPath, Serializable } return answer; } + catch (JaxenRuntimeException jre) + { + Throwable cause = jre.getCause(); + if (cause instanceof JaxenException) { + throw (JaxenException) cause; + } + throw jre; + } + } /** Select all nodes that are selected by this XPath * expression. If multiple nodes match, multiple nodes @@ -674,8 +685,19 @@ public class BaseXPath implements XPath, Serializable */ protected List selectNodesForContext(Context context) throws JaxenException { + try + { List list = this.xpath.asList( context ); return list; + } + catch (JaxenRuntimeException jre) + { + Throwable cause = jre.getCause(); + if (cause instanceof JaxenException) { + throw (JaxenException) cause; + } + throw jre; + } } diff --git a/jaxen/src/java/main/org/jaxen/Context.java b/jaxen/src/java/main/org/jaxen/Context.java old mode 100644 new mode 100755 index bc17075..7697281 --- a/jaxen/src/java/main/org/jaxen/Context.java +++ b/jaxen/src/java/main/org/jaxen/Context.java @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.io.Serializable; -import java.util.ArrayList; +import org.jaxen.util.LazyList; import java.util.Collections; import java.util.List; @@ -274,8 +274,7 @@ public class Context implements Serializable { if ( thisNodeSet != null ) { - List dupeNodeSet = new ArrayList( thisNodeSet.size() ); - dupeNodeSet.addAll( thisNodeSet ); + List dupeNodeSet = new LazyList( thisNodeSet.iterator() ); dupe.setNodeSet( dupeNodeSet ); dupe.setPosition(this.position); } diff --git a/jaxen/src/java/main/org/jaxen/JaxenConstants.java b/jaxen/src/java/main/org/jaxen/JaxenConstants.java old mode 100644 new mode 100755 diff --git a/jaxen/src/java/main/org/jaxen/JaxenRuntimeException.java b/jaxen/src/java/main/org/jaxen/JaxenRuntimeException.java old mode 100644 new mode 100755 diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java b/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java index ae6014e..329dae4 100644 --- a/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java +++ b/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.List; import org.jaxen.Context; import org.jaxen.JaxenException; +import org.jaxen.util.LazyList; /** * @deprecated this class will become non-public in the future; @@ -143,9 +144,7 @@ public class DefaultFilterExpr extends DefaultExpr implements FilterExpr, Predic else { List nodeSet = context.getNodeSet(); - ArrayList list = new ArrayList(nodeSet.size()); - list.addAll( nodeSet ); - results = list; + results = new LazyList(nodeSet.iterator()); } if ( results instanceof Boolean ) diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java b/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java old mode 100644 new mode 100755 index 951d722..ef232e1 --- a/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java +++ b/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java @@ -56,6 +56,7 @@ import java.util.List; import org.jaxen.Context; import org.jaxen.ContextSupport; import org.jaxen.JaxenException; +import org.jaxen.util.LazyList; abstract class DefaultLocationPath extends DefaultExpr implements LocationPath { @@ -129,7 +130,7 @@ abstract class DefaultLocationPath extends DefaultExpr implements LocationPath public Object evaluate(Context context) throws JaxenException { List nodeSet = context.getNodeSet(); - List contextNodeSet = new ArrayList(nodeSet); + List contextNodeSet = new LazyList(nodeSet.iterator()); ContextSupport support = context.getContextSupport(); Context stepContext = new Context(support); Iterator stepIter = getSteps().iterator(); @@ -140,11 +141,13 @@ abstract class DefaultLocationPath extends DefaultExpr implements LocationPath contextNodeSet = eachStep.evaluate(stepContext); // now we need to reverse the list if this is a reverse axis if (isReverseAxis(eachStep)) { - Collections.reverse(contextNodeSet); + // todo fixme + Collections.reverse(new ArrayList(contextNodeSet)); } } if (getSteps().size() > 1) { + // todo fixme Collections.sort(contextNodeSet, new NodeComparator(support.getNavigator())); } diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java b/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java old mode 100644 new mode 100755 index 31baf81..5cf946e --- a/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java +++ b/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java @@ -33,18 +33,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jaxen.expr; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.NoSuchElementException; -import org.jaxen.Context; -import org.jaxen.ContextSupport; -import org.jaxen.JaxenException; -import org.jaxen.UnresolvableException; -import org.jaxen.Navigator; import org.jaxen.expr.iter.IterableAxis; import org.jaxen.saxpath.Axis; +import org.jaxen.*; +import org.jaxen.util.FilterIterator; +import org.jaxen.util.LazyList; /** * Expression object that represents any flavor @@ -66,6 +66,7 @@ public class DefaultNameStep extends DefaultStep implements NameStep { * */ private static final long serialVersionUID = 428414912247718390L; + private static final Object PRESENT = "PRESENT"; /** * Our prefix, bound through the current Context. @@ -161,11 +162,13 @@ public class DefaultNameStep extends DefaultStep implements NameStep { if (contextSize == 0) { return Collections.EMPTY_LIST; } - ContextSupport support = context.getContextSupport(); - IterableAxis iterableAxis = getIterableAxis(); + final ContextSupport support = context.getContextSupport(); + final IterableAxis iterableAxis = getIterableAxis(); + final DefaultNameStep namestep = this; boolean namedAccess = (!matchesAnyName && iterableAxis.supportsNamedAccess(support)); // optimize for context size 1 (common case, avoids lots of object creation) + /* todo reinstate optmisations. commenting out for now to simplify diff. if (contextSize == 1) { Object contextNode = contextNodeSet.get(0); if (namedAccess) { @@ -215,94 +218,47 @@ public class DefaultNameStep extends DefaultStep implements NameStep { return getPredicateSet().evaluatePredicates(newNodeSet, support); } } + */ // full case - IdentitySet unique = new IdentitySet(); - List interimSet = new ArrayList(contextSize); - List newNodeSet = new ArrayList(contextSize); + final Iterator contextIterator = contextNodeSet.iterator(); + List newNodeSet; if (namedAccess) { String uri = null; - if (hasPrefix) { + if (hasPrefix) + { uri = support.translateNamespacePrefixToUri(prefix); - if (uri == null) { + if (uri == null) + { throw new UnresolvableException("XPath expression uses unbound namespace prefix " + prefix); } } - for (int i = 0; i < contextSize; ++i) { - Object eachContextNode = contextNodeSet.get(i); - - Iterator axisNodeIter = iterableAxis.namedAccessIterator( - eachContextNode, support, localName, prefix, uri); - if (axisNodeIter == null || !axisNodeIter.hasNext()) { - continue; - } - - while (axisNodeIter.hasNext()) - { - Object eachAxisNode = axisNodeIter.next(); - interimSet.add(eachAxisNode); - } - - // evaluate the predicates - List predicateNodes = getPredicateSet().evaluatePredicates(interimSet, support); - - // ensure only one of each node in the result - Iterator predicateNodeIter = predicateNodes.iterator(); - while (predicateNodeIter.hasNext()) + final String finalUri = uri; + Iterator predicateIterator = new AbstractPredicateIterator(contextIterator, namestep, support) { - Object eachPredicateNode = predicateNodeIter.next(); - if (! unique.contains(eachPredicateNode)) + protected Iterator axisNodeIterator(Object eachContextNode) throws UnsupportedAxisException { - unique.add(eachPredicateNode); - newNodeSet.add(eachPredicateNode); - } + return iterableAxis.namedAccessIterator( + eachContextNode, support, localName, prefix, finalUri); } - interimSet.clear(); - } - + }; + newNodeSet = new LazyList(predicateIterator); } else { - for (int i = 0; i < contextSize; ++i) { - Object eachContextNode = contextNodeSet.get(i); - - Iterator axisNodeIter = axisIterator(eachContextNode, support); - if (axisNodeIter == null || !axisNodeIter.hasNext()) { - continue; - } - - /* See jaxen-106. Might be able to optimize this by doing - * specific matching for individual axes. For instance on namespace axis - * we should only get namespace nodes and on attribute axes we only get - * attribute nodes. Self and parent axes have single members. - * Children, descendant, ancestor, and sibling axes never - * see any attributes or namespaces - */ - - // ensure only unique matching nodes in the result - while (axisNodeIter.hasNext()) { - Object eachAxisNode = axisNodeIter.next(); - - if (matches(eachAxisNode, support)) { - interimSet.add(eachAxisNode); - } - } - - // evaluate the predicates - List predicateNodes = getPredicateSet().evaluatePredicates(interimSet, support); - - // ensure only one of each node in the result - Iterator predicateNodeIter = predicateNodes.iterator(); - while (predicateNodeIter.hasNext()) + Iterator predicateIterator = new AbstractPredicateIterator(contextIterator, namestep, support) { - Object eachPredicateNode = predicateNodeIter.next(); - if (! unique.contains(eachPredicateNode)) + protected Iterator axisNodeIterator(final Object eachContextNode) throws UnsupportedAxisException { - unique.add(eachPredicateNode); - newNodeSet.add(eachPredicateNode); - } + return new FilterIterator(axisIterator(eachContextNode, support)) + { + protected boolean matches(Object item) throws JaxenException + { + return namestep.matches(item, support); } - interimSet.clear(); + }; } + }; + newNodeSet = new LazyList(predicateIterator); } return newNodeSet; @@ -413,10 +369,87 @@ public class DefaultNameStep extends DefaultStep implements NameStep { * * @return a debugging string */ - public String toString() { + public String toString() + { String prefix = getPrefix(); String qName = "".equals(prefix) ? getLocalName() : getPrefix() + ":" + getLocalName(); return "[(DefaultNameStep): " + qName + "]"; } + private static abstract class AbstractPredicateIterator implements Iterator + { + protected Map unique = new IdentityHashMap(); + protected Iterator source = JaxenConstants.EMPTY_ITERATOR; + protected Iterator contextIterator; + protected DefaultNameStep namestep; + protected ContextSupport support; + + protected AbstractPredicateIterator(Iterator contextIterator, DefaultNameStep namestep, ContextSupport support) + { + this.contextIterator = contextIterator; + this.namestep = namestep; + this.support = support; + } + + public Object next() + { + if (hasNext()) + { + return source.next(); + } + + throw new NoSuchElementException(); + } + + public boolean hasNext() + { + try + { + if (source.hasNext()) + { + return true; + } + + while (contextIterator.hasNext()) + { + Object eachContextNode = contextIterator.next(); + Iterator axisNodeIter = axisNodeIterator(eachContextNode); + if (!axisNodeIter.hasNext()) + { + continue; + } + Iterator uniqeIter = new FilterIterator(axisNodeIter) + { + protected boolean matches(Object item) + { + return unique.put(item, PRESENT) == null; + } + }; + if (!uniqeIter.hasNext()) + { + continue; + } + List interimSet = new LazyList(uniqeIter); + List predicateNodes = namestep.getPredicateSet().evaluatePredicates(interimSet, support); + source = predicateNodes.iterator(); + if (source.hasNext()) + { + return true; + } + } + return false; + } + catch (JaxenException e) + { + throw new JaxenRuntimeException(e); + } + } + + protected abstract Iterator axisNodeIterator(final Object eachContextNode) throws UnsupportedAxisException; + + public void remove() + { + throw new UnsupportedOperationException(); + } + } } diff --git a/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java b/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java index 4118ae3..2abe8ac 100644 --- a/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java +++ b/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java @@ -58,6 +58,8 @@ import java.util.List; import org.jaxen.Context; import org.jaxen.ContextSupport; import org.jaxen.JaxenException; +import org.jaxen.util.FilterIterator; +import org.jaxen.util.LazyList; import org.jaxen.function.BooleanFunction; /** @@ -248,31 +250,33 @@ public class PredicateSet implements Serializable return nodes2Filter; } - public List applyPredicate(Predicate predicate, List nodes2Filter, ContextSupport support) + public List applyPredicate(final Predicate predicate, List nodes2Filter, final ContextSupport support) throws JaxenException { final int nodes2FilterSize = nodes2Filter.size(); - List filteredNodes = new ArrayList(nodes2FilterSize); - // Set up a dummy context with a list to hold each node - Context predContext = new Context(support); - List tempList = new ArrayList(1); - predContext.setNodeSet(tempList); // loop through the current nodes to filter and add to the // filtered nodes list if the predicate succeeds - for (int i = 0; i < nodes2FilterSize; ++i) { - Object contextNode = nodes2Filter.get(i); + final Context predContext = new Context(support); + FilterIterator filtered = new FilterIterator(nodes2Filter.iterator()) { + private List tempList = new ArrayList(1); + private int i = 0; + + protected boolean matches(Object contextNode) throws JaxenException + { + i++; tempList.clear(); tempList.add(contextNode); predContext.setNodeSet(tempList); // ???? - predContext.setPosition(i + 1); + predContext.setPosition(i); predContext.setSize(nodes2FilterSize); Object predResult = predicate.evaluate(predContext); - if (predResult instanceof Number) { + if (predResult instanceof Number) + { // Here we assume nodes are in forward or reverse order // as appropriate for axis int proximity = ((Number) predResult).intValue(); - if (proximity == (i + 1)) { - filteredNodes.add(contextNode); + if (proximity == i) { + return true; } } else { @@ -280,11 +284,13 @@ public class PredicateSet implements Serializable BooleanFunction.evaluate(predResult, predContext.getNavigator()); if (includes.booleanValue()) { - filteredNodes.add(contextNode); + return true; } } + return false; } - return filteredNodes; + }; + return new LazyList(filtered); } } diff --git a/jaxen/src/java/main/org/jaxen/util/FilterIterator.java b/jaxen/src/java/main/org/jaxen/util/FilterIterator.java new file mode 100755 index 0000000..a522287 --- /dev/null +++ b/jaxen/src/java/main/org/jaxen/util/FilterIterator.java @@ -0,0 +1,122 @@ +package org.jaxen.util; + +import org.jaxen.JaxenException; +import org.jaxen.JaxenRuntimeException; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/* + * $Header$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright (C) 2000-2005 bob mcwhirter & James Strachan. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions, and the disclaimer that follows + * these conditions in the documentation and/or other materials + * provided with the distribution. + * + * 3. The name "Jaxen" must not be used to endorse or promote products + * derived from this software without prior written permission. For + * written permission, please contact license@jaxen.org. + * + * 4. Products derived from this software may not be called "Jaxen", nor + * may "Jaxen" appear in their name, without prior written permission + * from the Jaxen Project Management (pm@jaxen.org). + * + * In addition, we request (but do not require) that you include in the + * end-user documentation provided with the redistribution and/or in the + * software itself an acknowledgement equivalent to the following: + * "This product includes software developed by the + * Jaxen Project (http://www.jaxen.org/)." + * Alternatively, the acknowledgment may be graphical using the logos + * available at http://www.jaxen.org/ + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * ==================================================================== + * This software consists of voluntary contributions made by many + * individuals on behalf of the Jaxen Project and was originally + * created by bob mcwhirter and + * James Strachan . For more information on the + * Jaxen Project, please see . + * + * $Id$ +*/ + +/** + * Filters another iterator's results. + */ +public abstract class FilterIterator implements Iterator +{ + private Iterator source; + private boolean pushed = false; + private Object pushback; + + public FilterIterator(Iterator source) + { + this.source = source; + } + + public boolean hasNext() + { + if (pushed) { + return true; + } + while (source.hasNext()) { + try + { + pushback = source.next(); + if (matches(pushback)) { + pushed = true; + return true; + } + } + catch (JaxenException e) + { + throw new JaxenRuntimeException(e); + } + } + return false; + } + + protected abstract boolean matches(Object item) throws JaxenException; + + public Object next() + { + if (hasNext()) { + pushed = false; + return pushback; + } + + throw new NoSuchElementException(); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } +} diff --git a/jaxen/src/java/main/org/jaxen/util/LazyList.java b/jaxen/src/java/main/org/jaxen/util/LazyList.java new file mode 100755 index 0000000..0787ba0 --- /dev/null +++ b/jaxen/src/java/main/org/jaxen/util/LazyList.java @@ -0,0 +1,302 @@ +package org.jaxen.util; + +import java.util.List; +import java.util.Iterator; +import java.util.Collection; +import java.util.ListIterator; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +/* + * $Header$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright (C) 2000-2005 bob mcwhirter & James Strachan. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions, and the disclaimer that follows + * these conditions in the documentation and/or other materials + * provided with the distribution. + * + * 3. The name "Jaxen" must not be used to endorse or promote products + * derived from this software without prior written permission. For + * written permission, please contact license@jaxen.org. + * + * 4. Products derived from this software may not be called "Jaxen", nor + * may "Jaxen" appear in their name, without prior written permission + * from the Jaxen Project Management (pm@jaxen.org). + * + * In addition, we request (but do not require) that you include in the + * end-user documentation provided with the redistribution and/or in the + * software itself an acknowledgement equivalent to the following: + * "This product includes software developed by the + * Jaxen Project (http://www.jaxen.org/)." + * Alternatively, the acknowledgment may be graphical using the logos + * available at http://www.jaxen.org/ + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * ==================================================================== + * This software consists of voluntary contributions made by many + * individuals on behalf of the Jaxen Project and was originally + * created by bob mcwhirter and + * James Strachan . For more information on the + * Jaxen Project, please see . + * + * $Id$ +*/ + +/** + * This implementation of List is constructed with an Iterator, and attempts to read as + * little as possible. + */ +public class LazyList implements List +{ + private Iterator source; + private List seen; + + public LazyList(Iterator source) + { + this.source = source; + this.seen = new ArrayList(); + } + + public int size() + { + while (source.hasNext()) { + seen.add(source.next()); + } + return seen.size(); + } + + public boolean isEmpty() + { + return seen.isEmpty() && !source.hasNext(); + } + + public boolean contains(Object item) + { + return indexOf(item) >= 0; + } + + public Iterator iterator() + { + return listIterator(0); + } + + public Object[] toArray() + { + size(); + return seen.toArray(); + } + + public Object[] toArray(Object[] objects) + { + size(); + return seen.toArray(objects); + } + + public boolean containsAll(Collection collection) + { + size(); + return containsAll(collection); + } + + public Object get(int index) + { + while (index >= seen.size()) { + if (!source.hasNext()) { + break; + } + Object nextItem = source.next(); + seen.add(nextItem); + } + return seen.get(index); + } + + + public int indexOf(Object item) + { + + size(); + return seen.indexOf(item); + /* + int index = seen.indexOf(item); + if (index >= 0) { + return index; + } + while (source.hasNext()) { + Object nextItem = source.next(); + seen.add(nextItem); + if (item == nextItem) { + return seen.size() - 1; + } + } + return -1; + */ + } + + public int lastIndexOf(Object item) + { + size(); + return seen.lastIndexOf(item); + } + + public ListIterator listIterator() + { + return listIterator(0); + } + + public ListIterator listIterator(final int min) + { + return new ListIterator() { + int index = min - 1; + + public boolean hasNext() + { + if (index < seen.size() - 1) { + return true; + } else { + return source.hasNext(); + } + } + + public Object next() + { + if (hasNext()) { + index++; + return get(index); + } + throw new NoSuchElementException(); + } + + public boolean hasPrevious() + { + return index > 0; + } + + public Object previous() + { + if (index > 0) { + index--; + return seen.get(index); + } + throw new IndexOutOfBoundsException(); + } + + public int nextIndex() + { + return index - 1; + } + + public int previousIndex() + { + return index + 1; + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + + public void set(Object node) + { + size(); + seen.set(index, node); + } + + public void add(Object node) + { + throw new UnsupportedOperationException(); + } + }; + } + + public List subList(int start, int end) + { + size(); + return seen.subList(start, end); + } + + public boolean add(Object item) + { + size(); + return seen.add(item); + } + + public boolean remove(Object item) + { + size(); + return seen.remove(item); + } + + public boolean addAll(Collection collection) + { + size(); + return seen.addAll(collection); + } + + public boolean addAll(int index, Collection collection) + { + size(); + return seen.addAll(index, collection); + } + + public boolean removeAll(Collection collection) + { + size(); + return seen.removeAll(collection); + } + + public boolean retainAll(Collection collection) + { + size(); + return seen.retainAll(collection); + } + + public void clear() + { + size(); + seen.clear(); + } + + public Object set(int index, Object item) + { + size(); + return seen.set(index, item); + } + + public void add(int index, Object item) + { + size(); + seen.add(index, item); + } + + public Object remove(int index) + { + size(); + return seen.remove(index); + } +} diff --git a/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java b/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java index f66627f..787e8ec 100644 --- a/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java +++ b/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java @@ -267,7 +267,7 @@ public class DocumentNavigator extends org.jaxen.DefaultNavigator } else if (isNamespace(o)) { parent = ((XPathNamespace)o).getElement(); } - return (parent != null ? new SingleObjectIterator(parent) : null); + return (parent != null ? new SingleObjectIterator(parent) : JaxenConstants.EMPTY_ITERATOR); } public Object getParentNode(Object o) {