001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Portions Copyright Apache Software Foundation.
007: *
008: * The contents of this file are subject to the terms of either the GNU
009: * General Public License Version 2 only ("GPL") or the Common Development
010: * and Distribution License("CDDL") (collectively, the "License"). You
011: * may not use this file except in compliance with the License. You can obtain
012: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
013: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
014: * language governing permissions and limitations under the License.
015: *
016: * When distributing the software, include this License Header Notice in each
017: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
018: * Sun designates this particular file as subject to the "Classpath" exception
019: * as provided by Sun in the GPL Version 2 section of the License file that
020: * accompanied this code. If applicable, add the following below the License
021: * Header, with the fields enclosed by brackets [] replaced by your own
022: * identifying information: "Portions Copyrighted [year]
023: * [name of copyright owner]"
024: *
025: * Contributor(s):
026: *
027: * If you wish your version of this file to be governed by only the CDDL or
028: * only the GPL Version 2, indicate your decision by adding "[Contributor]
029: * elects to include this software in this distribution under the [CDDL or GPL
030: * Version 2] license." If you don't indicate a single choice of license, a
031: * recipient has the option to distribute your version of this file under
032: * either the CDDL, the GPL Version 2 or to extend the choice of license to
033: * its licensees as provided above. However, if you add GPL Version 2 code
034: * and therefore, elected the GPL Version 2 license, then the option applies
035: * only if the new code is made subject to such option by the copyright
036: * holder.
037: */
038:
039: package org.apache.taglibs.standard.tag.common.core;
040:
041: import java.util.ArrayList;
042: import java.util.Arrays;
043: import java.util.Collection;
044: import java.util.Enumeration;
045: import java.util.Iterator;
046: import java.util.Map;
047: import java.util.StringTokenizer;
048:
049: import javax.el.ValueExpression;
050:
051: import javax.servlet.jsp.JspTagException;
052: import javax.servlet.jsp.jstl.core.LoopTagSupport;
053:
054: import org.apache.taglibs.standard.resources.Resources;
055:
056: /**
057: * <p>Support for tag handlers for <forEach>, the core iteration
058: * tag in JSTL 1.0. This class extends LoopTagSupport and provides
059: * ForEach-specific functionality. The rtexprvalue library and the
060: * expression-evaluating library each have handlers that extend this
061: * class.</p>
062: *
063: * <p>Localized here is the logic for handling the veritable smorgasbord
064: * of types supported by <forEach>, including arrays,
065: * Collections, and others. To see how the actual iteration is controlled,
066: * review the javax.servlet.jsp.jstl.core.LoopTagSupport class instead.
067: * </p>
068: *
069: * @see javax.servlet.jsp.jstl.core.LoopTagSupport
070: * @author Shawn Bayern
071: */
072:
073: public abstract class ForEachSupport extends LoopTagSupport {
074:
075: //*********************************************************************
076: // Implementation overview
077:
078: /*
079: * This particular handler is essentially a large switching mechanism
080: * to support the various types that the <forEach> tag handles. The
081: * class is organized around the private ForEachIterator interface,
082: * which serves as the basis for relaying information to the iteration
083: * implementation we inherit from LoopTagSupport.
084: *
085: * We expect to receive our 'items' from one of our subclasses
086: * (presumably from the rtexprvalue or expression-evaluating libraries).
087: * If 'items' is missing, we construct an Integer[] array representing
088: * iteration indices, in line with the spec draft. From doStartTag(),
089: * we analyze and 'digest' the data we're passed. Then, we simply
090: * relay items as necessary to the iteration implementation that
091: * we inherit from LoopTagSupport.
092: */
093:
094: //*********************************************************************
095: // Internal, supporting classes and interfaces
096: /*
097: * Acts as a focal point for converting the various types we support.
098: * It would have been ideal to use Iterator here except for one problem:
099: * Iterator.hasNext() and Iterator.next() can't throw the JspTagException
100: * we want to throw. So instead, we'll encapsulate the hasNext() and
101: * next() methods we want to provide inside this local class.
102: * (Other implementations are more than welcome to implement hasNext()
103: * and next() explicitly, not in terms of a back-end supporting class.
104: * For the forEach tag handler, however, this class acts as a convenient
105: * organizational mechanism, for we support so many different classes.
106: * This encapsulation makes it easier to localize implementations
107: * in support of particular types -- e.g., changing the implementation
108: * of primitive-array iteration to wrap primitives only on request,
109: * instead of in advance, would involve changing only those methods that
110: * handle primitive arrays.
111: */
112: protected static interface ForEachIterator {
113: public boolean hasNext() throws JspTagException;
114:
115: public Object next() throws JspTagException;
116: }
117:
118: /*
119: * Simple implementation of ForEachIterator that adapts from
120: * an Iterator. This is appropriate for cases where hasNext() and
121: * next() don't need to throw JspTagException. Such cases are common.core.
122: */
123: protected class SimpleForEachIterator implements ForEachIterator {
124: private Iterator i;
125:
126: public SimpleForEachIterator(Iterator i) {
127: this .i = i;
128: }
129:
130: public boolean hasNext() {
131: return i.hasNext();
132: }
133:
134: public Object next() {
135: return i.next();
136: }
137: }
138:
139: //*********************************************************************
140: // ForEach-specifc state (protected)
141:
142: protected ForEachIterator items; // our 'digested' items
143: protected Object rawItems; // our 'raw' items
144:
145: //*********************************************************************
146: // Iteration control methods (based on processed 'items' object)
147:
148: // (We inherit semantics and Javadoc from LoopTagSupport.)
149:
150: protected boolean hasNext() throws JspTagException {
151: return items.hasNext();
152: }
153:
154: protected Object next() throws JspTagException {
155: return items.next();
156: }
157:
158: protected void prepare() throws JspTagException {
159: // produce the right sort of ForEachIterator
160: if (rawItems != null) {
161: // If this is a deferred expression, make a note and get
162: // the 'items' instance.
163: if (rawItems instanceof ValueExpression) {
164: deferredExpression = (ValueExpression) rawItems;
165: rawItems = deferredExpression.getValue(pageContext
166: .getELContext());
167: if (rawItems == null) {
168: // Simulate an empty list
169: rawItems = new ArrayList();
170: }
171: }
172: // extract an iterator over the 'items' we've got
173: items = supportedTypeForEachIterator(rawItems);
174: } else {
175: // no 'items', so use 'begin' and 'end'
176: items = beginEndForEachIterator();
177: }
178:
179: /* ResultSet no more supported in <c:forEach>
180: // step must be 1 when ResultSet is passed in
181: if (rawItems instanceof ResultSet && step != 1)
182: throw new JspTagException(
183: Resources.getMessage("FOREACH_STEP_NO_RESULTSET"));
184: */
185: }
186:
187: //*********************************************************************
188: // Tag logic and lifecycle management
189:
190: // Releases any resources we may have (or inherit)
191: public void release() {
192: super .release();
193: items = null;
194: rawItems = null;
195: deferredExpression = null;
196: }
197:
198: //*********************************************************************
199: // Private generation methods for the ForEachIterators we produce
200:
201: /* Extracts a ForEachIterator given an object of a supported type. */
202: protected ForEachIterator supportedTypeForEachIterator(Object o)
203: throws JspTagException {
204:
205: /*
206: * This is, of necessity, just a big, simple chain, matching in
207: * order. Since we are passed on Object because of all the
208: * various types we support, we cannot rely on the language's
209: * mechanism for resolving overloaded methods. (Method overloading
210: * resolves via early binding, so the type of the 'o' reference,
211: * not the type of the eventual value that 'o' references, is
212: * all that's available.)
213: *
214: * Currently, we 'match' on the object we have through an
215: * if/else chain that picks the first interface (or class match)
216: * found for an Object.
217: */
218:
219: ForEachIterator items;
220:
221: if (o instanceof Object[])
222: items = toForEachIterator((Object[]) o);
223: else if (o instanceof boolean[])
224: items = toForEachIterator((boolean[]) o);
225: else if (o instanceof byte[])
226: items = toForEachIterator((byte[]) o);
227: else if (o instanceof char[])
228: items = toForEachIterator((char[]) o);
229: else if (o instanceof short[])
230: items = toForEachIterator((short[]) o);
231: else if (o instanceof int[])
232: items = toForEachIterator((int[]) o);
233: else if (o instanceof long[])
234: items = toForEachIterator((long[]) o);
235: else if (o instanceof float[])
236: items = toForEachIterator((float[]) o);
237: else if (o instanceof double[])
238: items = toForEachIterator((double[]) o);
239: else if (o instanceof Collection)
240: items = toForEachIterator((Collection) o);
241: else if (o instanceof Iterator)
242: items = toForEachIterator((Iterator) o);
243: else if (o instanceof Enumeration)
244: items = toForEachIterator((Enumeration) o);
245: else if (o instanceof Map)
246: items = toForEachIterator((Map) o);
247: /*
248: else if (o instanceof ResultSet)
249: items = toForEachIterator((ResultSet) o);
250: */
251: else if (o instanceof String)
252: items = toForEachIterator((String) o);
253: else
254: items = toForEachIterator(o);
255:
256: return (items);
257: }
258:
259: /*
260: * Creates a ForEachIterator of Integers from 'begin' to 'end'
261: * in support of cases where our tag handler isn't passed an
262: * explicit collection over which to iterate.
263: */
264: private ForEachIterator beginEndForEachIterator() {
265: /*
266: * To plug into existing support, we need to keep 'begin', 'end',
267: * and 'step' as they are. So we'll simply create an Integer[]
268: * from 0 to 'end', inclusive, and let the existing implementation
269: * handle the subsetting and stepping operations. (Other than
270: * localizing the cost of creating this Integer[] to the start
271: * of the operation instead of spreading it out over the lifetime
272: * of the iteration, this implementation isn't worse than one that
273: * created new Integers() as needed from next(). Such an adapter
274: * to ForEachIterator could easily be written but, like I said,
275: * wouldn't provide much benefit.)
276: */
277: Integer[] ia = new Integer[end + 1];
278: for (int i = 0; i <= end; i++)
279: ia[i] = Integer.valueOf(i);
280: return new SimpleForEachIterator(Arrays.asList(ia).iterator());
281: }
282:
283: //*********************************************************************
284: // Private conversion methods to handle the various types we support
285:
286: // catch-all method whose invocation currently signals a 'matching error'
287: protected ForEachIterator toForEachIterator(Object o)
288: throws JspTagException {
289: throw new JspTagException(Resources
290: .getMessage("FOREACH_BAD_ITEMS"));
291: }
292:
293: // returns an iterator over an Object array (via List)
294: protected ForEachIterator toForEachIterator(Object[] a) {
295: return new SimpleForEachIterator(Arrays.asList(a).iterator());
296: }
297:
298: // returns an iterator over a boolean[] array, wrapping items in Boolean
299: protected ForEachIterator toForEachIterator(boolean[] a) {
300: Boolean[] wrapped = new Boolean[a.length];
301: for (int i = 0; i < a.length; i++)
302: wrapped[i] = Boolean.valueOf(a[i]);
303: return new SimpleForEachIterator(Arrays.asList(wrapped)
304: .iterator());
305: }
306:
307: // returns an iterator over a byte[] array, wrapping items in Byte
308: protected ForEachIterator toForEachIterator(byte[] a) {
309: Byte[] wrapped = new Byte[a.length];
310: for (int i = 0; i < a.length; i++)
311: wrapped[i] = Byte.valueOf(a[i]);
312: return new SimpleForEachIterator(Arrays.asList(wrapped)
313: .iterator());
314: }
315:
316: // returns an iterator over a char[] array, wrapping items in Character
317: protected ForEachIterator toForEachIterator(char[] a) {
318: Character[] wrapped = new Character[a.length];
319: for (int i = 0; i < a.length; i++)
320: wrapped[i] = Character.valueOf(a[i]);
321: return new SimpleForEachIterator(Arrays.asList(wrapped)
322: .iterator());
323: }
324:
325: // returns an iterator over a short[] array, wrapping items in Short
326: protected ForEachIterator toForEachIterator(short[] a) {
327: Short[] wrapped = new Short[a.length];
328: for (int i = 0; i < a.length; i++)
329: wrapped[i] = Short.valueOf(a[i]);
330: return new SimpleForEachIterator(Arrays.asList(wrapped)
331: .iterator());
332: }
333:
334: // returns an iterator over an int[] array, wrapping items in Integer
335: protected ForEachIterator toForEachIterator(int[] a) {
336: Integer[] wrapped = new Integer[a.length];
337: for (int i = 0; i < a.length; i++)
338: wrapped[i] = Integer.valueOf(a[i]);
339: return new SimpleForEachIterator(Arrays.asList(wrapped)
340: .iterator());
341: }
342:
343: // returns an iterator over a long[] array, wrapping items in Long
344: protected ForEachIterator toForEachIterator(long[] a) {
345: Long[] wrapped = new Long[a.length];
346: for (int i = 0; i < a.length; i++)
347: wrapped[i] = Long.valueOf(a[i]);
348: return new SimpleForEachIterator(Arrays.asList(wrapped)
349: .iterator());
350: }
351:
352: // returns an iterator over a float[] array, wrapping items in Float
353: protected ForEachIterator toForEachIterator(float[] a) {
354: Float[] wrapped = new Float[a.length];
355: for (int i = 0; i < a.length; i++)
356: wrapped[i] = new Float(a[i]);
357: return new SimpleForEachIterator(Arrays.asList(wrapped)
358: .iterator());
359: }
360:
361: // returns an iterator over a double[] array, wrapping items in Double
362: protected ForEachIterator toForEachIterator(double[] a) {
363: Double[] wrapped = new Double[a.length];
364: for (int i = 0; i < a.length; i++)
365: wrapped[i] = new Double(a[i]);
366: return new SimpleForEachIterator(Arrays.asList(wrapped)
367: .iterator());
368: }
369:
370: // retrieves an iterator from a Collection
371: protected ForEachIterator toForEachIterator(Collection c) {
372: return new SimpleForEachIterator(c.iterator());
373: }
374:
375: // simply passes an Iterator through...
376: protected ForEachIterator toForEachIterator(Iterator i) {
377: return new SimpleForEachIterator(i);
378: }
379:
380: // converts an Enumeration to an Iterator via a local adapter
381: protected ForEachIterator toForEachIterator(Enumeration e) {
382:
383: // local adapter
384: class EnumerationAdapter implements ForEachIterator {
385: private Enumeration e;
386:
387: public EnumerationAdapter(Enumeration e) {
388: this .e = e;
389: }
390:
391: public boolean hasNext() {
392: return e.hasMoreElements();
393: }
394:
395: public Object next() {
396: return e.nextElement();
397: }
398: }
399:
400: return new EnumerationAdapter(e);
401: }
402:
403: // retrieves an iterator over the Map.Entry items in a Map
404: protected ForEachIterator toForEachIterator(Map m) {
405: return new SimpleForEachIterator(m.entrySet().iterator());
406: }
407:
408: /* No more supported in JSTL. See interface Result instead.
409: // thinly wraps a ResultSet in an appropriate Iterator
410: protected ForEachIterator toForEachIterator(ResultSet rs)
411: throws JspTagException {
412:
413: // local adapter
414: class ResultSetAdapter implements ForEachIterator {
415: private ResultSet rs;
416: public ResultSetAdapter(ResultSet rs) {
417: this.rs = rs;
418: }
419: public boolean hasNext() throws JspTagException {
420: try {
421: return !(rs.isLast()); // dependent on JDBC 2.0
422: } catch (java.sql.SQLException ex) {
423: throw new JspTagException(ex.getMessage());
424: }
425: }
426: public Object next() throws JspTagException {
427: try {
428: rs.next();
429: return rs;
430: } catch (java.sql.SQLException ex) {
431: throw new JspTagException(ex.getMessage());
432: }
433: }
434: }
435:
436: return new ResultSetAdapter(rs);
437: }
438: */
439:
440: // tokenizes a String as a CSV and returns an iterator over it
441: protected ForEachIterator toForEachIterator(String s) {
442: StringTokenizer st = new StringTokenizer(s, ",");
443: return toForEachIterator(st); // convert from Enumeration
444: }
445:
446: }
|