@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>
Modifier and Type | Class and Description |
---|---|
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 . |
LOCK_CONFIG_KEY, log, LOGGER_NAME_KEY, SAFE_MODE_KEY, USE_CLASS_LOGGER_KEY
Constructor and Description |
---|
LoopTool() |
Modifier and Type | Method and Description |
---|---|
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. |
configure, configure, getLog, initLogger, isConfigLocked, isSafeMode, setLockConfig, setSafeMode
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()).
obj
- an object that Velocity's #foreach directive can iterate overLoopTool.ManagedIterator
that this tool instance will trackpublic LoopTool.ManagedIterator watch(Object obj, String name)
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
.obj
- an object that Velocity's #foreach directive can iterate overname
- loop nameLoopTool.ManagedIterator
that this tool instance will trackwatch(Object)
public LoopTool.ManagedIterator sync(Object main, Object synced)
protected LoopTool.ManagedIterator manage(Iterator iterator, String name)
public void stop()
public void stop(String name)
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.name
- loop namestop()
public void stopTo(String name)
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.name
- loop namestop()
,
stop(String)
public void stopAll()
stop()
except that the stop command is issued
all the loops being watched by this tool.stop()
public void skip(int number)
LoopTool.ManagedIterator.exclude(Object)
, any elements
skipped are still considered in the results returned by getCount()
and isFirst()
.number
- number of iterationspublic void skip(int number, String name)
number
- number of iterationsname
- loop nameskip(int)
public Boolean isFirst()
true
if the current loop is on its first iteration.public Boolean isFirst(String name)
name
- loop nametrue
if the loop with the specified name
is on its first iteration.public Boolean getFirst()
isFirst()
. Exists to allow $loop.first syntax.public Boolean isLast()
true
if the current loop is on its last iteration.public Boolean isLast(String name)
name
- loop nametrue
if the loop with the specified name
is on its last iteration.public Boolean getLast()
isLast()
. Exists to allow $loop.last syntax.public Object get(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.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.
key
- sync'ed Iterator namepublic Object get(String name, String synced)
name
- loop namesynced
- sync'ed Iterator namepublic Integer getIndex()
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
.public Integer getIndex(String name)
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
.name
- loop namepublic Integer getCount()
skip(int)
ahead in this loop, those skipped iterations will still be included in
the count.public Integer getCount(String name)
skip(int)
ahead in this loop, those skipped iterations will still be included in
the count.name
- loop namepublic LoopTool.ManagedIterator getThis()
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)public int getDepth()
protected LoopTool.ManagedIterator findIterator(String name)
LoopTool.ManagedIterator
with the specified name
if it is in this instance's iterator stack.name
- loop nameprotected LoopTool.ManagedIterator pop()
protected Iterator getIterator(Object obj)
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
.obj
- target iteratorCopyright © 2002–2021 The Apache Software Foundation. All rights reserved.