org.apache.velocity.tools.generic

## Class LoopTool

- java.lang.Object
- - org.apache.velocity.tools.generic.SafeConfig
  - - org.apache.velocity.tools.generic.LoopTool

All Implemented Interfaces:
  Serializable

    @DefaultKey(value="loop")
     @ValidScope(value="request")
    public class LoopTool
    extends SafeConfig
    implements Serializable

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 functions of this tool will be obsolete with the release of Velocity 1.7, which will provide $foreach.hasNext, $foreach.isFirst, $foreach.isLast, $foreach.index and $foreach.count automatically. However, this will still be useful for the more advanced sync and skip features. Also, for very complicated nested loops, the loop naming feature may be easier than doing things like $foreach.parent.parent.

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
See Also:
  Serialized Form

### Nested Class Summary

Nested Classes
`static class` `LoopTool.Action` Represents an automatic action taken by a `LoopTool.ManagedIterator` when a `LoopTool.Condition` is satisfied by the subsequent element.
`static class` `LoopTool.ActionCondition` Composition class which associates an `LoopTool.Action` and `LoopTool.Condition` 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.
`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

### Fields inherited from class org.apache.velocity.tools.generic.SafeConfig

`LOCK_CONFIG_KEY, log, LOGGER_NAME_KEY, SAFE_MODE_KEY, USE_CLASS_LOGGER_KEY`

### Constructor Summary

Constructors
Constructor and Description
`LoopTool()`

### Method Summary

All Methods Instance Methods Concrete Methods
`protected LoopTool.ManagedIterator` `findIterator(String name)` Finds the `LoopTool.ManagedIterator` with the specified name if it is in this instance's iterator stack.
`Object` `get(String key)` This serves two purposes:
`Object` `get(String name, String synced)` Asks the loop with the specified name for the current value of the specified sync'ed iterator, if any.
`Integer` `getCount()` Returns the number of items the current loop has handled.
`Integer` `getCount(String name)` Returns the number of items the specified loop has handled.
`int` `getDepth()` Returns the number of loops currently on the stack.
`Boolean` `getFirst()`
`Integer` `getIndex()` Returns the 0-based index of the item the current loop is handling.
`Integer` `getIndex(String name)` Returns the 0-based index of the item the specified loop is handling.
`protected Iterator` `getIterator(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.
`Boolean` `getLast()`
`LoopTool.ManagedIterator` `getThis()` Returns the most recent `LoopTool.ManagedIterator` for this instance.
`Boolean` `isFirst()`
`Boolean` `isFirst(String name)`
`Boolean` `isLast()`
`Boolean` `isLast(String name)`
`protected LoopTool.ManagedIterator` `manage(Iterator iterator, String name)`
`protected LoopTool.ManagedIterator` `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).
`void` `skip(int number, 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(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(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(Object main, Object synced)`
`LoopTool.ManagedIterator` `watch(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(Object obj, 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 org.apache.velocity.tools.generic.SafeConfig

`configure, configure, getLog, initLogger, isConfigLocked, isSafeMode, setLockConfig, setSafeMode`

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

### Constructor Detail

#### LoopTool

    public LoopTool()

### Method Detail

#### watch

    public LoopTool.ManagedIterator watch(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(Object obj,
                                          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`.

Parameters:
  `obj` - an object that Velocity's #foreach directive can iterate over
  `name` - loop name
Returns:
  a `LoopTool.ManagedIterator` that this tool instance will track
See Also:
  `watch(Object)`

#### sync

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

#### manage

    protected LoopTool.ManagedIterator manage(Iterator iterator,
                                              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(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.

Parameters:
  `name` - loop name
See Also:
  `stop()`

#### stopTo

    public void stopTo(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.

Parameters:
  `name` - loop name
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()`.

Parameters:
  `number` - number of iterations

#### skip

    public void skip(int number,
                     String name)

This tells the specified loop to skip ahead the specified number of iterations.

Parameters:
  `number` - number of iterations
  `name` - loop name
See Also:
  `skip(int)`

#### isFirst

    public Boolean isFirst()

Returns:
  `true` if the current loop is on its first iteration.

#### isFirst

    public Boolean isFirst(String name)

Parameters:
  `name` - loop name
Returns:
  `true` if the loop with the specified name is on its first iteration.

#### getFirst

    public Boolean getFirst()

Returns:
  the result of `isFirst()`. Exists to allow $loop.first syntax.

#### isLast

    public Boolean isLast()

Returns:
  `true` if the current loop is on its last iteration.

#### isLast

    public Boolean isLast(String name)

Parameters:
  `name` - loop name
Returns:
  `true` if the loop with the specified name is on its last iteration.

#### getLast

    public Boolean getLast()

Returns:
  the result of `isLast()`. Exists to allow $loop.last syntax.

#### get

    public Object get(String key)

This serves two purposes:

- Getting the current value of a sync'ed iterator
- Abbreviate syntax for properties of outer loops

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.count_foo` or `$loop.first_foo`). 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.

Parameters:
  `key` - sync'ed Iterator name
Returns:
  current iterator value

#### get

    public Object get(String name,
                      String synced)

Asks the loop with the specified name for the current value of the specified sync'ed iterator, if any.

Parameters:
  `name` - loop name
  `synced` - sync'ed Iterator name
Returns:
  current iterator value

#### getIndex

    public 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`.

Returns:
  current loop index

#### getIndex

    public Integer getIndex(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`.

Parameters:
  `name` - loop name
Returns:
  current loop index

#### getCount

    public 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.

Returns:
  items count

#### getCount

    public Integer getCount(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.

Parameters:
  `name` - loop name
Returns:
  loop items count

#### getThis

    public LoopTool.ManagedIterator getThis()

Returns the most recent `LoopTool.ManagedIterator` for this instance. This can be used to access properties like the count, index, isFirst, isLast, etc which would otherwise fail on the last item in a loop due to the necessity of popping iterators off the stack when the last item is retrieved. (See VELTOOLS-124)

Returns:
  most recent iterator

#### getDepth

    public int getDepth()

Returns the number of loops currently on the stack. This is only useful for debugging, as iterators are popped off the stack at the start of their final iteration, making this frequently "incorrect".

Returns:
  depth

#### findIterator

    protected LoopTool.ManagedIterator findIterator(String name)

Finds the `LoopTool.ManagedIterator` with the specified name if it is in this instance's iterator stack.

Parameters:
  `name` - loop name
Returns:
  loop iterator

#### pop

    protected LoopTool.ManagedIterator pop()

Don't let templates call this, but allow subclasses and ManagedIterator to have access.

Returns:
  pop'ed iterator

#### getIterator

    protected Iterator getIterator(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`.

Parameters:
  `obj` - target iterator
Returns:
  wrapped iterator or null