Class LoopTool
- java.lang.Object
-
- org.apache.velocity.tools.generic.LoopTool
-
@DefaultKey("loop") @ValidScope("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 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
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
LoopTool.Action
Represents an automatic action taken by aLoopTool.ManagedIterator
when aLoopTool.Condition
is satisfied by the subsequent element.static class
LoopTool.ActionCondition
Composition class which associates anLoopTool.Action
andLoopTool.Condition
for aLoopTool.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 aLoopTool.ManagedIterator
can pass it's next element to see if anLoopTool.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 standardIterator
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 aLoopTool.ManagedIterator
.
-
Field Summary
Fields Modifier and Type Field Description private java.util.Stack<LoopTool.ManagedIterator>
iterators
private LoopTool.ManagedIterator
last
-
Constructor Summary
Constructors Constructor Description LoopTool()
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description protected LoopTool.ManagedIterator
findIterator(java.lang.String name)
Finds theLoopTool.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 loopsjava.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.java.lang.Boolean
getFirst()
Returns the result ofisFirst()
.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 toClassUtils.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 ofisLast()
.LoopTool.ManagedIterator
getThis()
Returns the most recentLoopTool.ManagedIterator
for this instance.java.lang.Boolean
isFirst()
Returnstrue
if the current loop is on its first iteration.java.lang.Boolean
isFirst(java.lang.String name)
Returnstrue
if the loop with the specified name is on its first iteration.java.lang.Boolean
isLast()
Returnstrue
if the current loop is on its last iteration.java.lang.Boolean
isLast(java.lang.String name)
Returnstrue
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 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, java.lang.String name)
This tells the specified loop to skip ahead the specified number of iterations.private void
skip(int number, LoopTool.ManagedIterator iterator)
void
stop()
This tells the current loop to stop after the current iteration.void
stop(java.lang.String name)
This is just likestop()
except that the stop command is issued only to the loop/iterator with the specified name.void
stopAll()
This is just likestop()
except that the stop command is issued all the loops being watched by this tool.void
stopTo(java.lang.String name)
This is just likestop(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 likewatch(Object)
except that it also takes a name which is given to theLoopTool.ManagedIterator
that is returned.
-
-
-
Field Detail
-
iterators
private java.util.Stack<LoopTool.ManagedIterator> iterators
-
last
private LoopTool.ManagedIterator last
-
-
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 likewatch(Object)
except that it also takes a name which is given to theLoopTool.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 isnull
, then this will returnnull
even if the object can be watched. Provided names cannot benull
.- 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 likestop()
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 likestop(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 likestop()
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 ofLoopTool.ManagedIterator.exclude(Object)
, any elements skipped are still considered in the results returned bygetCount()
andisFirst()
.
-
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()
Returnstrue
if the current loop is on its first iteration.
-
isFirst
public java.lang.Boolean isFirst(java.lang.String name)
Returnstrue
if the loop with the specified name is on its first iteration.
-
getFirst
public java.lang.Boolean getFirst()
Returns the result ofisFirst()
. Exists to allow $loop.first syntax.
-
isLast
public java.lang.Boolean isLast()
Returnstrue
if the current loop is on its last iteration.
-
isLast
public java.lang.Boolean isLast(java.lang.String name)
Returnstrue
if the loop with the specified name is on its last iteration.
-
getLast
public java.lang.Boolean getLast()
Returns the result ofisLast()
. Exists to allow $loop.last syntax.
-
get
public 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
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.
-
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 youskip(int)
ahead in this loop, those skipped iterations will still be reflected in the index. If iteration has not begun, this will returnnull
.
-
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 youskip(int)
ahead in this loop, those skipped iterations will still be reflected in the index. If iteration has not begun, this will returnnull
.
-
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 youskip(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 youskip(int)
ahead in this loop, those skipped iterations will still be included in the count.
-
getThis
public LoopTool.ManagedIterator getThis()
Returns the most recentLoopTool.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)
-
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".
-
findIterator
protected LoopTool.ManagedIterator findIterator(java.lang.String name)
Finds theLoopTool.ManagedIterator
with the specified name if it is in this instance's iterator stack.
-
pop
protected LoopTool.ManagedIterator 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 toClassUtils.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 returnnull
.
-
-