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]

Reply via email to