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 &lt;forEach&gt;, 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