org.apache.velocity.tools.generic
Class LoopTool

java.lang.Object
  extended by org.apache.velocity.tools.generic.LoopTool

@DefaultKey(value="loop")
@ValidScope(value="request")
public class LoopTool
extends java.lang.Object

A convenience tool to use with #foreach loops. It wraps a list with a custom iterator to provide additional controls and feedback for managing loops.

This tool was originally inspired the now-deprecated IteratorTool, which provided similar base functionality but was somewhat more difficult to understand and use. Rather than try to migrate that implementation via deprecation and new methods, it was simplest to just create an entirely new tool that simplified the original API and was easy to augment with useful new features like support for nested (and nameable) loops, skipping ahead in loops, synchronizing multiple iterators, getting the iteration count of loops, identifying if a loop is on its first or last iteration, and so on.

Most usage, of course, will probably never go much beyond the simple watch(Object), LoopTool.ManagedIterator.sync(Object), LoopTool.ManagedIterator.stop(Object), isFirst(), isLast(),and maybe getCount() or getIndex() methods, if even that much. However, it is with complicated nested #foreach loops and varying "break" conditions, that this tool can probably do the most to simplify your templates.

Example of use:

  Template
  ---
  #set( $list = [1..7] )
  #set( $others = [3..10] )
  #foreach( $item in $loop.watch($list).sync($others, 'other') )
  $item -> $loop.other
  #if( $item >= 5 )$loop.stop()#end
  #end

  Output
  ------
  1 -> 3
  2 -> 4
  3 -> 5
  4 -> 6
  5 -> 7

 Example tools.xml config (if you want to use this with VelocityView):
 <tools>
   <toolbox scope="request">
     <tool class="org.apache.velocity.tools.generic.LoopTool"/>
   </toolbox>
 </tools>
 

Version:
$Id: LoopTool.java 590893 2007-11-01 04:40:21Z nbubna $
Author:
Nathan Bubna

Nested Class Summary
static class LoopTool.Action
          Represents an automatic action taken by a LoopTool.ManagedIterator when a LoopTool.ActionCondition is satisfied by the subsequent element.
static class LoopTool.ActionCondition
          Composition class which associates an LoopTool.Action and LoopTool.ActionCondition for a LoopTool.ManagedIterator.
static class LoopTool.Comparison
          Base condition class for conditions (assumption here is that conditions are all comparative.
static interface LoopTool.Condition
          Represents a function into which a LoopTool.ManagedIterator can pass it's next element to see if an LoopTool.Action should be taken.
static class LoopTool.Equals
          Simple condition that checks elements in the iterator for equality to a specified Object.
static class LoopTool.ManagedIterator
          Iterator implementation that wraps a standard Iterator and allows it to be prematurely stopped, skipped ahead, and associated with a name for advanced nested loop control.
static class LoopTool.SyncedIterator
          Simple wrapper to make it easy to keep an arbitray Iterator in sync with a LoopTool.ManagedIterator.
 
Field Summary
private  java.util.Stack<LoopTool.ManagedIterator> iterators
           
 
Constructor Summary
LoopTool()
           
 
Method Summary
protected  LoopTool.ManagedIterator findIterator(java.lang.String name)
          Finds the LoopTool.ManagedIterator with the specified name if it is in this instance's iterator stack.
 java.lang.Object get(java.lang.String key)
          This serves two purposes: Getting the current value of a sync'ed iterator Abbreviate syntax for properties of outer loops
 java.lang.Object get(java.lang.String name, java.lang.String synced)
          Asks the loop with the specified name for the current value of the specified sync'ed iterator, if any.
 java.lang.Integer getCount()
          Returns the number of items the current loop has handled.
 java.lang.Integer getCount(java.lang.String name)
          Returns the number of items the specified loop has handled.
 int getDepth()
          Returns the number of loops currently on the stack to tell how deeply nested the current loop is.
 java.lang.Boolean getFirst()
          Returns the result of isFirst().
 java.lang.Integer getIndex()
          Returns the 0-based index of the item the current loop is handling.
 java.lang.Integer getIndex(java.lang.String name)
          Returns the 0-based index of the item the specified loop is handling.
protected static java.util.Iterator getIterator(java.lang.Object obj)
          Wraps access to ClassUtils.getIterator(java.lang.Object) is a nice little try/catch block to prevent exceptions from escaping into the template.
 java.lang.Boolean getLast()
          Returns the result of isLast().
 java.lang.Boolean isFirst()
          Returns true if the current loop is on its first iteration.
 java.lang.Boolean isFirst(java.lang.String name)
          Returns true if the loop with the specified name is on its first iteration.
 java.lang.Boolean isLast()
          Returns true if the current loop is on its last iteration.
 java.lang.Boolean isLast(java.lang.String name)
          Returns true if the loop with the specified name is on its last iteration.
protected  LoopTool.ManagedIterator manage(java.util.Iterator iterator, java.lang.String name)
           
protected  void pop()
          Don't let templates call this, but allow subclasses and ManagedIterator to have access.
 void skip(int number)
          Skips ahead the specified number of iterations (if possible).
private  void skip(int number, LoopTool.ManagedIterator iterator)
           
 void skip(int number, java.lang.String name)
          This tells the specified loop to skip ahead the specified number of iterations.
 void stop()
          This tells the current loop to stop after the current iteration.
 void stop(java.lang.String name)
          This is just like stop() except that the stop command is issued only to the loop/iterator with the specified name.
 void stopAll()
          This is just like stop() except that the stop command is issued all the loops being watched by this tool.
 void stopTo(java.lang.String name)
          This is just like stop(String) except that the stop command is issued both to the loop/iterator with the specified name and all loops nested within it.
 LoopTool.ManagedIterator sync(java.lang.Object main, java.lang.Object synced)
           
 LoopTool.ManagedIterator watch(java.lang.Object obj)
          Tells the LoopTool to watch the specified Array, Collection, Map, Iterator, Iterable, Enumeration or POJO with an iterator() method while the template iterates over the values within it.
 LoopTool.ManagedIterator watch(java.lang.Object obj, java.lang.String name)
          This is just like watch(Object) except that it also takes a name which is given to the LoopTool.ManagedIterator that is returned.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

iterators

private java.util.Stack<LoopTool.ManagedIterator> iterators
Constructor Detail

LoopTool

public LoopTool()
Method Detail

watch

public LoopTool.ManagedIterator watch(java.lang.Object obj)

Tells the LoopTool to watch the specified Array, Collection, Map, Iterator, Iterable, Enumeration or POJO with an iterator() method while the template iterates over the values within it.

Under the covers, this is returning an iterable wrapper that is also pushed onto this tool's stack. That allows this tool to know which iterator to give later commands (i.e. stop() or skip()).

Parameters:
obj - an object that Velocity's #foreach directive can iterate over
Returns:
a LoopTool.ManagedIterator that this tool instance will track

watch

public LoopTool.ManagedIterator watch(java.lang.Object obj,
                                      java.lang.String name)
This is just like watch(Object) except that it also takes a name which is given to the LoopTool.ManagedIterator that is returned. This allows the user to send stop or skip commands to that specific iterator even when there are nested iterators within it that are being watched. If the given name is null, then this will return null even if the object can be watched. Provided names cannot be null.

See Also:
watch(Object)

sync

public LoopTool.ManagedIterator sync(java.lang.Object main,
                                     java.lang.Object synced)

manage

protected LoopTool.ManagedIterator manage(java.util.Iterator iterator,
                                          java.lang.String name)

stop

public void stop()
This tells the current loop to stop after the current iteration. This is different from "break" common to most programming languages, in that it does not immediately cease activity in the current iteration. Instead, it merely tells the #foreach loop that this is the last time around.


stop

public void stop(java.lang.String name)
This is just like stop() except that the stop command is issued only to the loop/iterator with the specified name. If no such loop is found with that name, then no stop command is issued.

See Also:
stop()

stopTo

public void stopTo(java.lang.String name)
This is just like stop(String) except that the stop command is issued both to the loop/iterator with the specified name and all loops nested within it. If no such loop is found with that name, then no stop commands are issued.

See Also:
stop(), stop(String)

stopAll

public void stopAll()
This is just like stop() except that the stop command is issued all the loops being watched by this tool.

See Also:
stop()

skip

public void skip(int number)
Skips ahead the specified number of iterations (if possible). Since this is manual skipping (unlike the automatic skipping provided by the likes of LoopTool.ManagedIterator.exclude(Object), any elements skipped are still considered in the results returned by getCount() and isFirst().


skip

public void skip(int number,
                 java.lang.String name)
This tells the specified loop to skip ahead the specified number of iterations.

See Also:
skip(int)

skip

private void skip(int number,
                  LoopTool.ManagedIterator iterator)

isFirst

public java.lang.Boolean isFirst()
Returns true if the current loop is on its first iteration.


isFirst

public java.lang.Boolean isFirst(java.lang.String name)
Returns true if the loop with the specified name is on its first iteration.


getFirst

public java.lang.Boolean getFirst()
Returns the result of isFirst(). Exists to allow $loop.first syntax.


isLast

public java.lang.Boolean isLast()
Returns true if the current loop is on its last iteration.


isLast

public java.lang.Boolean isLast(java.lang.String name)
Returns true if the loop with the specified name is on its last iteration.


getLast

public java.lang.Boolean getLast()
Returns the result of isLast(). Exists to allow $loop.last syntax.


get

public java.lang.Object get(java.lang.String key)

This serves two purposes:

First, it searches all the loops being managed for one with a sync'ed Iterator under the specified name and returns the current value for that sync'ed iterator, if any. If there is no sync'ed iterators or none with that name, then this will check if the specified key is requesting a "property" of an outer loop (e.g. $loop.foo_count or $loop.foo_first). This syntax is shorter and clearer than $loop.getCount('foo'). If the key starts with a property name and ends with an outer loop name, then the value of that property for that loop is returned.


get

public java.lang.Object get(java.lang.String name,
                            java.lang.String synced)
Asks the loop with the specified name for the current value of the specified sync'ed iterator, if any.


getIndex

public java.lang.Integer getIndex()
Returns the 0-based index of the item the current loop is handling. So, if this is the first iteration, then the index will be 0. If you skip(int) ahead in this loop, those skipped iterations will still be reflected in the index. If iteration has not begun, this will return null.


getIndex

public java.lang.Integer getIndex(java.lang.String name)
Returns the 0-based index of the item the specified loop is handling. So, if this is the first iteration, then the index will be 0. If you skip(int) ahead in this loop, those skipped iterations will still be reflected in the index. If iteration has not begun, this will return null.


getCount

public java.lang.Integer getCount()
Returns the number of items the current loop has handled. So, if this is the first iteration, then the count will be 1. If you skip(int) ahead in this loop, those skipped iterations will still be included in the count.


getCount

public java.lang.Integer getCount(java.lang.String name)
Returns the number of items the specified loop has handled. So, if this is the first iteration, then the count will be 1. If you skip(int) ahead in this loop, those skipped iterations will still be included in the count.


getDepth

public int getDepth()
Returns the number of loops currently on the stack to tell how deeply nested the current loop is.


findIterator

protected LoopTool.ManagedIterator findIterator(java.lang.String name)
Finds the LoopTool.ManagedIterator with the specified name if it is in this instance's iterator stack.


pop

protected void pop()
Don't let templates call this, but allow subclasses and ManagedIterator to have access.


getIterator

protected static java.util.Iterator getIterator(java.lang.Object obj)
Wraps access to ClassUtils.getIterator(java.lang.Object) is a nice little try/catch block to prevent exceptions from escaping into the template. In the case of such problems, this will return null.



Copyright (c) 2003-2007 Apache Software Foundation