You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by jb...@apache.org on 2013/02/11 02:10:57 UTC
svn commit: r1444644 -
/tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/ForEachSupport.java
Author: jboynes
Date: Mon Feb 11 01:10:57 2013
New Revision: 1444644
URL: http://svn.apache.org/r1444644
Log:
major refactor of ForEachSupport to reduce array allocation and use auto-boxing
also addresses memory issue for begin..end iteration for large values of end
simplify iteration of deferred expressions
Modified:
tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/ForEachSupport.java
Modified: tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/ForEachSupport.java
URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/ForEachSupport.java?rev=1444644&r1=1444643&r2=1444644&view=diff
==============================================================================
--- tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/ForEachSupport.java (original)
+++ tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/ForEachSupport.java Mon Feb 11 01:10:57 2013
@@ -17,22 +17,16 @@
package org.apache.taglibs.standard.tag.common.core;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.StringTokenizer;
+import org.apache.taglibs.standard.resources.Resources;
-import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.jstl.core.IndexedValueExpression;
import javax.servlet.jsp.jstl.core.IteratedExpression;
import javax.servlet.jsp.jstl.core.IteratedValueExpression;
import javax.servlet.jsp.jstl.core.LoopTagSupport;
-
-import org.apache.taglibs.standard.resources.Resources;
+import java.lang.reflect.Array;
+import java.util.*;
/**
* <p>Support for tag handlers for <forEach>, the core iteration
@@ -50,217 +44,50 @@ import org.apache.taglibs.standard.resou
* @see javax.servlet.jsp.jstl.core.LoopTagSupport
*/
public abstract class ForEachSupport extends LoopTagSupport {
+ protected Iterator items; // our 'digested' items
+ protected Object rawItems; // our 'raw' items
- //*********************************************************************
- // Implementation overview
-
- /*
- * This particular handler is essentially a large switching mechanism
- * to support the various types that the <forEach> tag handles. The
- * class is organized around the private ForEachIterator interface,
- * which serves as the basis for relaying information to the iteration
- * implementation we inherit from LoopTagSupport.
- *
- * We expect to receive our 'items' from one of our subclasses
- * (presumably from the rtexprvalue or expression-evaluating libraries).
- * If 'items' is missing, we construct an Integer[] array representing
- * iteration indices, in line with the spec draft. From doStartTag(),
- * we analyze and 'digest' the data we're passed. Then, we simply
- * relay items as necessary to the iteration implementation that
- * we inherit from LoopTagSupport.
- */
-
-
- //*********************************************************************
- // Internal, supporting classes and interfaces
-
- /*
- * Acts as a focal point for converting the various types we support.
- * It would have been ideal to use Iterator here except for one problem:
- * Iterator.hasNext() and Iterator.next() can't throw the JspTagException
- * we want to throw. So instead, we'll encapsulate the hasNext() and
- * next() methods we want to provide inside this local class.
- * (Other implementations are more than welcome to implement hasNext()
- * and next() explicitly, not in terms of a back-end supporting class.
- * For the forEach tag handler, however, this class acts as a convenient
- * organizational mechanism, for we support so many different classes.
- * This encapsulation makes it easier to localize implementations
- * in support of particular types -- e.g., changing the implementation
- * of primitive-array iteration to wrap primitives only on request,
- * instead of in advance, would involve changing only those methods that
- * handle primitive arrays.
- */
-
- protected static interface ForEachIterator {
- public boolean hasNext() throws JspTagException;
-
- public Object next() throws JspTagException;
- }
-
- /*
- * Simple implementation of ForEachIterator that adapts from
- * an Iterator. This is appropriate for cases where hasNext() and
- * next() don't need to throw JspTagException. Such cases are common.core.
- */
-
- protected class SimpleForEachIterator implements ForEachIterator {
- private Iterator i;
-
- public SimpleForEachIterator(Iterator i) {
- this.i = i;
- }
-
- public boolean hasNext() {
- return i.hasNext();
- }
-
- public Object next() {
- return i.next();
- }
- }
-
- protected class DeferredForEachIterator implements ForEachIterator {
-
- private ValueExpression itemsValueExpression;
- private IteratedExpression itemsValueIteratedExpression;
- private int length = -1;
- private int currentIndex = 0;
- private boolean isIndexedValueExpression = false;
- private boolean anIterator = false;
- private Iterator myIterator;
- private boolean anEnumeration = false;
- private Enumeration myEnumeration;
-
- public DeferredForEachIterator(ValueExpression o) throws JspTagException {
- itemsValueExpression = o;
- determineLengthAndType();
- }
-
- public boolean hasNext() throws JspTagException {
- if (isIndexedValueExpression) {
- if (currentIndex < length) {
- return true;
- } else {
- return false;
- }
- } else {
- if (length != -1) {
- //a Collection, Map, or StringTokenizer
- if (currentIndex < length) {
- return true;
- } else {
- return false;
- }
- } else {
- if (anIterator) {
- return myIterator.hasNext();
- } else if (anEnumeration) {
- return myEnumeration.hasMoreElements();
- } else {
- //don't know what this is
- return false;
- }
- }
- }
- }
-
- public Object next() throws JspTagException {
- ValueExpression nextValue = null;
- if (isIndexedValueExpression) {
- nextValue = new IndexedValueExpression(itemsValueExpression, currentIndex);
- currentIndex++;
+ @Override
+ protected void prepare() throws JspTagException {
+ // produce the right sort of ForEachIterator
+ if (rawItems == null) {
+ // if no items were specified, iterate from begin to end
+ items = new ToEndIterator(end);
+ } else if (rawItems instanceof ValueExpression) {
+ deferredExpression = (ValueExpression) rawItems;
+ Object o = deferredExpression.getValue(pageContext.getELContext());
+ Iterator iterator = toIterator(o);
+ if (isIndexed(o)) {
+ items = new IndexedDeferredIterator(iterator, deferredExpression);
} else {
- if (itemsValueIteratedExpression == null) {
- itemsValueIteratedExpression = new IteratedExpression(itemsValueExpression, getDelims());
- }
- nextValue = new IteratedValueExpression(itemsValueIteratedExpression, currentIndex);
- currentIndex++;
- if (length != -1) {
- //a Collection, Map, or StringTokenizer
- //nothing else needed
- } else {
- //need to increment these guys
- if (anIterator) {
- myIterator.next();
- } else if (anEnumeration) {
- myEnumeration.nextElement();
- }
- }
+ items = new IteratedDeferredIterator(iterator, new IteratedExpression(deferredExpression, getDelims()));
}
- return nextValue;
+ } else {
+ items = toIterator(rawItems);
}
+ }
- private void determineLengthAndType() throws JspTagException {
- ELContext myELContext = pageContext.getELContext();
- Object o = itemsValueExpression.getValue(myELContext);
- if (o instanceof Object[]) {
- length = ((Object[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof boolean[]) {
- length = ((boolean[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof byte[]) {
- length = ((byte[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof char[]) {
- length = ((char[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof short[]) {
- length = ((short[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof int[]) {
- length = ((int[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof long[]) {
- length = ((long[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof float[]) {
- length = ((float[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof double[]) {
- length = ((double[]) o).length;
- isIndexedValueExpression = true;
- } else if (o instanceof Collection) {
- length = ((Collection) o).size();
- isIndexedValueExpression = false;
- } else if (o instanceof Iterator) {
- //have to reproduce iterator here so we can determine the size
- isIndexedValueExpression = false;
- anIterator = true;
- myIterator = (Iterator) o;
- } else if (o instanceof Enumeration) {
- isIndexedValueExpression = false;
- anEnumeration = true;
- myEnumeration = (Enumeration) o;
- } else if (o instanceof Map) {
- length = ((Map) o).size();
- isIndexedValueExpression = false;
- //
- //else if (o instanceof ResultSet)
- // items = toForEachIterator((ResultSet) o);
- //
- } else if (o instanceof String) {
- StringTokenizer st = new StringTokenizer((String) o, ",");
- length = st.countTokens();
- isIndexedValueExpression = false;
- } else {
- // unrecognized type
- throw new JspTagException(Resources.getMessage("FOREACH_BAD_ITEMS"));
- }
+ private Iterator toIterator(Object rawItems) throws JspTagException {
+ if (rawItems instanceof Collection) {
+ return ((Collection) rawItems).iterator();
+ } else if (rawItems.getClass().isArray()) {
+ return new ArrayIterator(rawItems);
+ } else if (rawItems instanceof Iterator) {
+ return (Iterator) rawItems;
+ } else if (rawItems instanceof Enumeration) {
+ return new EnumerationIterator((Enumeration) rawItems);
+ } else if (rawItems instanceof Map) {
+ return ((Map) rawItems).entrySet().iterator();
+ } else if (rawItems instanceof String) {
+ return new EnumerationIterator(new StringTokenizer((String) rawItems, ","));
+ } else {
+ throw new JspTagException(Resources.getMessage("FOREACH_BAD_ITEMS"));
}
}
- //*********************************************************************
- // ForEach-specifc state (protected)
-
- protected ForEachIterator items; // our 'digested' items
- protected Object rawItems; // our 'raw' items
-
-
- //*********************************************************************
- // Iteration control methods (based on processed 'items' object)
-
- // (We inherit semantics and Javadoc from LoopTagSupport.)
+ private boolean isIndexed(Object o) {
+ return o.getClass().isArray();
+ }
@Override
protected boolean hasNext() throws JspTagException {
@@ -273,35 +100,6 @@ public abstract class ForEachSupport ext
}
@Override
- protected void prepare() throws JspTagException {
- // produce the right sort of ForEachIterator
- if (rawItems != null) {
- if (rawItems instanceof ValueExpression) {
- deferredExpression = (ValueExpression) rawItems;
- items = toDeferredForEachIterator(deferredExpression);
- } else {
- // extract an iterator over the 'items' we've got
- items = supportedTypeForEachIterator(rawItems);
- }
- } else {
- // no 'items', so use 'begin' and 'end'
- items = beginEndForEachIterator();
- }
-
- /* ResultSet no more supported in <c:forEach>
- // step must be 1 when ResultSet is passed in
- if (rawItems instanceof ResultSet && step != 1)
- throw new JspTagException(Resources.getMessage("FOREACH_STEP_NO_RESULTSET"));
- */
- }
-
-
- //*********************************************************************
- // Tag logic and lifecycle management
-
- // Releases any resources we may have (or inherit)
-
- @Override
public void release() {
super.release();
items = null;
@@ -309,277 +107,125 @@ public abstract class ForEachSupport ext
}
- //*********************************************************************
- // Private generation methods for the ForEachIterators we produce
-
- /* Extracts a ForEachIterator given an object of a supported type. */
- //This should not be called for a deferred ValueExpression
+ /**
+ * Iterator that simply counts up to 'end.'
+ * Unlike the previous implementation this does not attempt to pre-allocate an array
+ * containing all values from 0 to 'end' as that can result in excessive memory allocation
+ * for large values of 'end.'
+ * LoopTagSupport calls next() 'begin' times in order to discard the initial values,
+ * In order to maintain this contract, this implementation always starts at 0.
+ * Future optimization to skip these redundant calls might be possible.
+ */
+ private static class ToEndIterator extends ReadOnlyIterator {
+ private final int end;
+ private int i;
- protected ForEachIterator supportedTypeForEachIterator(Object o)
- throws JspTagException {
+ private ToEndIterator(int end) {
+ this.end = end;
+ }
- /*
- * This is, of necessity, just a big, simple chain, matching in
- * order. Since we are passed on Object because of all the
- * various types we support, we cannot rely on the language's
- * mechanism for resolving overloaded methods. (Method overloading
- * resolves via early binding, so the type of the 'o' reference,
- * not the type of the eventual value that 'o' references, is
- * all that's available.)
- *
- * Currently, we 'match' on the object we have through an
- * if/else chain that picks the first interface (or class match)
- * found for an Object.
- */
-
- ForEachIterator items;
-
- if (o instanceof Object[]) {
- items = toForEachIterator((Object[]) o);
- } else if (o instanceof boolean[]) {
- items = toForEachIterator((boolean[]) o);
- } else if (o instanceof byte[]) {
- items = toForEachIterator((byte[]) o);
- } else if (o instanceof char[]) {
- items = toForEachIterator((char[]) o);
- } else if (o instanceof short[]) {
- items = toForEachIterator((short[]) o);
- } else if (o instanceof int[]) {
- items = toForEachIterator((int[]) o);
- } else if (o instanceof long[]) {
- items = toForEachIterator((long[]) o);
- } else if (o instanceof float[]) {
- items = toForEachIterator((float[]) o);
- } else if (o instanceof double[]) {
- items = toForEachIterator((double[]) o);
- } else if (o instanceof Collection) {
- items = toForEachIterator((Collection) o);
- } else if (o instanceof Iterator) {
- items = toForEachIterator((Iterator) o);
- } else if (o instanceof Enumeration) {
- items = toForEachIterator((Enumeration) o);
- } else if (o instanceof Map) {
- items = toForEachIterator((Map) o);
- }
- /*
- else if (o instanceof ResultSet)
- items = toForEachIterator((ResultSet) o);
- */
- else if (o instanceof String) {
- items = toForEachIterator((String) o);
- } else {
- items = toForEachIterator(o);
+ public boolean hasNext() {
+ return i <= end;
}
- return (items);
+ public Object next() {
+ if (i <= end) {
+ return i++;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
}
- /*
- * Creates a ForEachIterator of Integers from 'begin' to 'end'
- * in support of cases where our tag handler isn't passed an
- * explicit collection over which to iterate.
+ /**
+ * Iterator over an Enumeration.
*/
+ private static class EnumerationIterator extends ReadOnlyIterator {
+ private final Enumeration e;
- private ForEachIterator beginEndForEachIterator() {
- /*
- * To plug into existing support, we need to keep 'begin', 'end',
- * and 'step' as they are. So we'll simply create an Integer[]
- * from 0 to 'end', inclusive, and let the existing implementation
- * handle the subsetting and stepping operations. (Other than
- * localizing the cost of creating this Integer[] to the start
- * of the operation instead of spreading it out over the lifetime
- * of the iteration, this implementation isn't worse than one that
- * created new Integers() as needed from next(). Such an adapter
- * to ForEachIterator could easily be written but, like I said,
- * wouldn't provide much benefit.)
- */
- Integer[] ia = new Integer[end + 1];
- for (int i = 0; i <= end; i++) {
- ia[i] = new Integer(i);
+ private EnumerationIterator(Enumeration e) {
+ this.e = e;
}
- return new SimpleForEachIterator(Arrays.asList(ia).iterator());
- }
-
-
- //*********************************************************************
- // Private conversion methods to handle the various types we support
-
- protected ForEachIterator toDeferredForEachIterator(ValueExpression o) throws JspTagException {
- return new DeferredForEachIterator(o);
- }
-
- // catch-all method whose invocation currently signals a 'matching error'
-
- protected ForEachIterator toForEachIterator(Object o)
- throws JspTagException {
- throw new JspTagException(Resources.getMessage("FOREACH_BAD_ITEMS"));
- }
-
- // returns an iterator over an Object array (via List)
-
- protected ForEachIterator toForEachIterator(Object[] a) {
- return new SimpleForEachIterator(Arrays.asList(a).iterator());
- }
- // returns an iterator over a boolean[] array, wrapping items in Boolean
-
- protected ForEachIterator toForEachIterator(boolean[] a) {
- Boolean[] wrapped = new Boolean[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = a[i] ? Boolean.TRUE : Boolean.FALSE;
+ public boolean hasNext() {
+ return e.hasMoreElements();
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
- }
-
- // returns an iterator over a byte[] array, wrapping items in Byte
- protected ForEachIterator toForEachIterator(byte[] a) {
- Byte[] wrapped = new Byte[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Byte(a[i]);
+ public Object next() {
+ return e.nextElement();
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
}
- // returns an iterator over a char[] array, wrapping items in Character
-
- protected ForEachIterator toForEachIterator(char[] a) {
- Character[] wrapped = new Character[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Character(a[i]);
+ /**
+ * Iterator over an array, including arrays of primitive types.
+ */
+ private static class ArrayIterator extends ReadOnlyIterator {
+ private final Object array;
+ private final int length;
+ private int i = 0;
+
+ private ArrayIterator(Object array) {
+ this.array = array;
+ length = Array.getLength(array);
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
- }
- // returns an iterator over a short[] array, wrapping items in Short
-
- protected ForEachIterator toForEachIterator(short[] a) {
- Short[] wrapped = new Short[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Short(a[i]);
+ public boolean hasNext() {
+ return i < length;
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
- }
-
- // returns an iterator over an int[] array, wrapping items in Integer
- protected ForEachIterator toForEachIterator(int[] a) {
- Integer[] wrapped = new Integer[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Integer(a[i]);
+ public Object next() {
+ try {
+ return Array.get(array, i++);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
}
- // returns an iterator over a long[] array, wrapping items in Long
+ private static class IndexedDeferredIterator extends DeferredIterator {
+ private final ValueExpression itemsValueExpression;
- protected ForEachIterator toForEachIterator(long[] a) {
- Long[] wrapped = new Long[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Long(a[i]);
+ private IndexedDeferredIterator(Iterator iterator, ValueExpression itemsValueExpression) {
+ super(iterator);
+ this.itemsValueExpression = itemsValueExpression;
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
- }
- // returns an iterator over a float[] array, wrapping items in Float
-
- protected ForEachIterator toForEachIterator(float[] a) {
- Float[] wrapped = new Float[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Float(a[i]);
+ public Object next() {
+ iterator.next();
+ return new IndexedValueExpression(itemsValueExpression, currentIndex++);
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
}
- // returns an iterator over a double[] array, wrapping items in Double
+ private static class IteratedDeferredIterator extends DeferredIterator {
+ private final IteratedExpression itemsValueIteratedExpression;
- protected ForEachIterator toForEachIterator(double[] a) {
- Double[] wrapped = new Double[a.length];
- for (int i = 0; i < a.length; i++) {
- wrapped[i] = new Double(a[i]);
+ private IteratedDeferredIterator(Iterator iterator, IteratedExpression itemsValueIteratedExpression) {
+ super(iterator);
+ this.itemsValueIteratedExpression = itemsValueIteratedExpression;
}
- return new SimpleForEachIterator(Arrays.asList(wrapped).iterator());
- }
-
- // retrieves an iterator from a Collection
-
- protected ForEachIterator toForEachIterator(Collection c) {
- return new SimpleForEachIterator(c.iterator());
- }
-
- // simply passes an Iterator through...
-
- protected ForEachIterator toForEachIterator(Iterator i) {
- return new SimpleForEachIterator(i);
- }
-
- // converts an Enumeration to an Iterator via a local adapter
-
- protected ForEachIterator toForEachIterator(Enumeration e) {
-
- // local adapter
- class EnumerationAdapter implements ForEachIterator {
- private Enumeration e;
-
- public EnumerationAdapter(Enumeration e) {
- this.e = e;
- }
-
- public boolean hasNext() {
- return e.hasMoreElements();
- }
- public Object next() {
- return e.nextElement();
- }
+ public Object next() {
+ iterator.next();
+ return new IteratedValueExpression(itemsValueIteratedExpression, currentIndex++);
}
-
- return new EnumerationAdapter(e);
}
- // retrieves an iterator over the Map.Entry items in a Map
+ private abstract static class DeferredIterator extends ReadOnlyIterator {
+ protected final Iterator iterator;
+ protected int currentIndex = 0;
- protected ForEachIterator toForEachIterator(Map m) {
- return new SimpleForEachIterator(m.entrySet().iterator());
- }
-
- /* No more supported in JSTL. See interface Result instead.
- // thinly wraps a ResultSet in an appropriate Iterator
- protected ForEachIterator toForEachIterator(ResultSet rs)
- throws JspTagException {
-
- // local adapter
- class ResultSetAdapter implements ForEachIterator {
- private ResultSet rs;
- public ResultSetAdapter(ResultSet rs) {
- this.rs = rs;
- }
- public boolean hasNext() throws JspTagException {
- try {
- return !(rs.isLast()); // dependent on JDBC 2.0
- } catch (java.sql.SQLException ex) {
- throw new JspTagException(ex.getMessage());
- }
- }
- public Object next() throws JspTagException {
- try {
- rs.next();
- return rs;
- } catch (java.sql.SQLException ex) {
- throw new JspTagException(ex.getMessage());
- }
- }
+ protected DeferredIterator(Iterator iterator) {
+ this.iterator = iterator;
}
- return new ResultSetAdapter(rs);
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
}
- */
- // tokenizes a String as a CSV and returns an iterator over it
-
- protected ForEachIterator toForEachIterator(String s) {
- StringTokenizer st = new StringTokenizer(s, ",");
- return toForEachIterator(st); // convert from Enumeration
+ private abstract static class ReadOnlyIterator implements Iterator {
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
}
-
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org