nbubna 2003/03/10 13:37:04 Added: src/java/org/apache/velocity/tools/generic IteratorTool.java Log: Initial revision (contribution of Denis Bredelet formerly called FisherMill) Revision Changes Path 1.1 jakarta-velocity-tools/src/java/org/apache/velocity/tools/generic/IteratorTool.java Index: IteratorTool.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. 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 following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Velocity", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * 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 APACHE SOFTWARE FOUNDATION OR * ITS 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 Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.velocity.tools.generic; import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.Map; import java.util.Vector; import org.apache.velocity.util.ArrayIterator; import org.apache.velocity.util.EnumerationIterator; /** * <p> * A convenience tool to use with #foreach loops. It wraps a list * to let the designer specify a condition to terminate the loop, * and reuse the same list in different loops. * </p> * <p> * Example of use: * <pre> * Java * ---- * context.put("mill", new IteratorTool()); * * * VTL * --- * * #set ($list = [1, 2, 3, 5, 8, 13]) * #set ($numbers = $mill.wrap($list)) * * #foreach ($item in $numbers) * #if ($item < 8) $numbers.more()#end * #end * * $numbers.more() * * * Output * ------ * * 1 2 3 5 * 8 * </pre> * </p> * <p> * <b>Warning:</b> It is not recommended to use hasNext() with this * tool as it is used to control the #foreach. Use hasMore() instead. * </p> * @author <a href="mailto:[EMAIL PROTECTED]">Denis Bredelet</a> * * @version $Id: IteratorTool.java,v 1.1 2003/03/10 21:37:04 nbubna Exp $ */ public class IteratorTool implements Iterator { private Object wrapped; private Iterator iterator; private boolean wantMore; private boolean cachedNext; protected Object next; /** * Create a IteratorTool instance to use as tool. * When it is created this way, the tool returns a new * instance each time wrap() is called. This is * useful when you want to allow the designers to create instances. */ public IteratorTool() { this(null); } /** * Create a IteratorTool instance to use in #foreach. * * @param wrapped The list to wrap. */ public IteratorTool(Object wrapped) { internalWrap(wrapped); } /** * Wraps a list with the tool. * <br>The list can be an array, a Collection, a Map, an Iterator * or an Enumeration. * <br>If the list is a Map, the tool iterates over the values. * <br>If the list is an Iterator or an Enumeration, the tool can * be used only once. * * @param list The list to wrap. * @return A new wrapper if this object is used as a tool, or * itself if it is a wrapper. */ public IteratorTool wrap(Object list) { if (this.wrapped == null) { return new IteratorTool(list); } else if (list != null) { internalWrap(list); return this; } else { throw new IllegalArgumentException("Need a valid list to wrap"); } } /** * Wraps a list with the tool. This object can therefore * be used instead of the list itself in a #foreach. * The list can be an array, a Collection, a Map, an * Iterator or an Enumeration. * <br>- If the list is a Map, the tool iterates over the values. * <br>- If the list is an Iterator or an Enumeration, the tool * can be used only once. * * @param wrapped The list to wrap. * @return This object, wrapped around the list. */ private void internalWrap(Object wrapped) { if (wrapped != null) { /* rip-off from org/apache/velocity/runtime/directive/ForEach.java */ if (wrapped.getClass().isArray()) { this.iterator = new ArrayIterator((Object[])wrapped); } else if (wrapped instanceof Collection) { this.iterator = ((Collection)wrapped).iterator(); } else if (wrapped instanceof Map) { this.iterator = ((Map)wrapped).values().iterator(); } else if (wrapped instanceof Iterator) { this.iterator = (Iterator)wrapped; } else if (wrapped instanceof Enumeration) { this.iterator = new EnumerationIterator((Enumeration)wrapped); } else { /* Don't know what is the object. * Should we put it in a one-item array? */ throw new IllegalArgumentException("Don't know how to wrap this list"); } this.wrapped = wrapped; this.wantMore = true; this.cachedNext = false; } else { this.iterator = null; this.wrapped = null; this.wantMore = false; this.cachedNext = false; } } /** * <p> * Resets the wrapper so that it starts over at the beginning of the list. * </p> * <p> * <b>Note to programmers:</b> This method has no effect if the wrapped * object is an enumeration or an iterator. */ public void reset() { if (this.wrapped != null) { internalWrap(this.wrapped); } } /** * <p> * Gets the next object in the list. This method is called * by #foreach to define $item in: * <pre> * #foreach( $item in $list ) * </pre> * </p> * <p> * This method is not intended for template designers, but they can use * them if they want to read the value of the next item without doing * more(). * </p> * * @return The next item in the list. * @throws NoSuchElementException if there are no more * elements in the list. */ public Object next() { if (this.wrapped == null) { throw new IllegalStateException("Use wrap() before calling next()"); } if (!this.cachedNext) { this.cachedNext = true; this.next = this.iterator.next(); return this.next; } else { return this.next; } } /** * Returns true if there are more elements in the * list and more() was called. * <br>This code always return false: * <pre> * tool.hasNext()? tool.hasNext(): false; * </pre> * * @return true if there are more elements, and either more() * or hasNext() was called since last call. */ public boolean hasNext() { if (this.wantMore) { /* don't want more unless more is called */ this.wantMore = false; return hasMore(); } else { /* prepare for next #foreach */ this.wantMore = true; return false; } } /** * Removes the current element from the list. * The current element is defined as the last element that was read * from the list, either with next() or with more(). * * @throws UnsupportedOperationException if the wrapped list * iterator doesn't support this operation. */ public void remove() throws UnsupportedOperationException { if (this.wrapped == null) { throw new IllegalStateException("Use wrap() before calling remove()"); } /* Let the iterator decide whether to implement this or not */ this.iterator.remove(); } /** * <p> * Asks for the next element in the list. This method is to be used * by the template designer in #foreach loops. * </p> * <p> * If this method is called in the body of #foreach, the loop * continues as long as there are elements in the list. * <br>If this method is not called the loop terminates after the * current iteration. * </p> * * @return The next element in the list, or null if there are no * more elements. */ public Object more() { this.wantMore = true; if (hasMore()) { Object next = next(); this.cachedNext = false; return next; } else { return null; } } /** * Returns true if there are more elements in the wrapped list. * <br>If this object doesn't wrap a list, the method always returns false. * * @return true if there are more elements in the list. */ public boolean hasMore() { if (this.wrapped == null) { return false; } return cachedNext || this.iterator.hasNext(); } /** * Puts a condition to break out of the loop. * The #foreach loop will terminate after this iteration, unless more() * is called after stop(). */ public void stop() { this.wantMore = false; } /** * Returns this object as a String. * <br>If this object is used as a tool, it just gives the class name. * <br>Otherwise it appends the wrapped list to the class name. * * @return A string representation of this object. */ public String toString() { StringBuffer out = new StringBuffer(this.getClass().getName()); if (this.wrapped != null) { out.append('('); out.append(this.wrapped); out.append(')'); } return out.toString(); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
