You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2007/12/18 01:23:23 UTC
svn commit: r605051 - in /velocity/tools/trunk: examples/showcase/loop.vm
src/main/java/org/apache/velocity/tools/generic/LoopTool.java
src/test/java/org/apache/velocity/tools/LoopToolTests.java
Author: nbubna
Date: Mon Dec 17 16:23:22 2007
New Revision: 605051
URL: http://svn.apache.org/viewvc?rev=605051&view=rev
Log:
add some more loop watching features
Modified:
velocity/tools/trunk/examples/showcase/loop.vm
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java
velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LoopToolTests.java
Modified: velocity/tools/trunk/examples/showcase/loop.vm
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/examples/showcase/loop.vm?rev=605051&r1=605050&r2=605051&view=diff
==============================================================================
--- velocity/tools/trunk/examples/showcase/loop.vm (original)
+++ velocity/tools/trunk/examples/showcase/loop.vm Mon Dec 17 16:23:22 2007
@@ -19,23 +19,29 @@
#set( $demo = $text.demo )
$demo.thisPage.insert("#doclink( 'LoopTool' true )").
This tool is a convenience tool to use with ${esc.h}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.
+list to let you prematurely end stop iterating, skip iterations, easily
+determine if you are on the first or last iteration, get the number of iterations
+completed, and even easily do all the above with complex, nested loops.
</p>
<form method="post" action="$link.self">
<textarea name="demo" rows="10" cols="65">
#if( $params.demo )##
-$params.demo
+$params.demo##
#else##
-${esc.h}set( ${esc.d}list = [1, 2, 3, 4, 5, 6] )
-${esc.h}foreach( ${esc.d}item in ${esc.d}loop.watch(${esc.d}list) )
-${esc.d}item
-${esc.h}if( ${esc.d}item > 1 )${esc.d}loop.skip(1)${esc.h}end
-${esc.h}if( ${esc.d}item >= 5 )${esc.d}loop.stop()${esc.h}end
+${esc.h}set( ${esc.d}numbers = [1, 2, 3, 4, 5, 6] )
+${esc.h}set( ${esc.d}letters = ['A','B','C'] )
+${esc.h}foreach( ${esc.d}l in ${esc.d}loop.watch(${esc.d}letters, 'l') )
+${esc.h}foreach( ${esc.d}n in ${esc.d}loop.watch(${esc.d}numbers) )
+${esc.h}if( ${esc.d}loop.first )${esc.d}l[${esc.h}end${esc.h}${esc.h}
+${esc.d}n${esc.h}${esc.h}
+${esc.h}if( ${esc.d}loop.last )] ${esc.h}else, ${esc.h}end${esc.h}${esc.h}
+${esc.h}if( ${esc.d}n > 1 )${esc.d}loop.skip(1)${esc.h}end${esc.h}${esc.h} skip ahead each number over 1
+${esc.h}if( ${esc.d}l == 'B' )${esc.d}loop.stop('l')${esc.h}end${esc.h}${esc.h} stop the letter loop after B
${esc.h}end
-#end
- </textarea>
+${esc.h}end##
+#end#*
+*#</textarea>
<input type="submit" value="$demo.try">
#if( $params.layout )
<input type="hidden" name="layout" value="$params.layout">
Modified: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java?rev=605051&r1=605050&r2=605051&view=diff
==============================================================================
--- velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java (original)
+++ velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java Mon Dec 17 16:23:22 2007
@@ -38,11 +38,14 @@
* 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 with a simplified API, support for nested loops
- * (which can optionally be given names), and skipping ahead in loops.
+ * (which can optionally be given names), skipping ahead in loops,
+ * getting the iteration count of loops, and identifying if a loop is
+ * on its first or last iteration.
* </p>
* <p>
* Most users, of course, will probably never need anything beyond the
- * simple {@link #watch(Object)}, {@link #stop}, and {@link #skip(int)}
+ * simple {@link #watch(Object)}, {@link #stop}, {@link #isFirst},
+ * {@link #isLast}, {@link #getCount}, or maybe {@link #skip(int)}
* 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.
@@ -250,14 +253,131 @@
public void skip(int number, String name)
{
// just tell the matching one to skip
+ ManagedIterator iterator = findIterator(name);
+ if (iterator != null)
+ {
+ iterator.skip(number);
+ }
+ }
+
+ /**
+ * Returns {@code true} if the current loop is on its first iteration.
+ */
+ public Boolean isFirst()
+ {
+ if (iterators.empty())
+ {
+ return null;
+ }
+ return iterators.peek().isFirst();
+ }
+
+ /**
+ * Returns {@code true} if the loop with the specified name
+ * is on its first iteration.
+ */
+ public Boolean isFirst(String name)
+ {
+ // just tell the matching one to skip
+ ManagedIterator iterator = findIterator(name);
+ if (iterator != null)
+ {
+ return iterator.isFirst();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the result of {@link #isFirst}. Exists to allow $loop.first syntax.
+ */
+ public Boolean getFirst()
+ {
+ return isFirst();
+ }
+
+ /**
+ * Returns {@code true} if the current loop is on its last iteration.
+ */
+ public Boolean isLast()
+ {
+ if (iterators.empty())
+ {
+ return null;
+ }
+ return iterators.peek().isLast();
+ }
+
+ /**
+ * Returns {@code true} if the loop with the specified name
+ * is on its last iteration.
+ */
+ public Boolean isLast(String name)
+ {
+ // just tell the matching one to skip
+ ManagedIterator iterator = findIterator(name);
+ if (iterator != null)
+ {
+ return iterator.isLast();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the result of {@link #isLast}. Exists to allow $loop.last syntax.
+ */
+ public Boolean getLast()
+ {
+ return isLast();
+ }
+
+ /**
+ * 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 {@link #skip}
+ * ahead in this loop, those skipped iterations will still be included in
+ * the count.
+ */
+ public Integer getCount()
+ {
+ if (iterators.empty())
+ {
+ return null;
+ }
+ return iterators.peek().getCount();
+ }
+
+ /**
+ * 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 {@link #skip}
+ * ahead in this loop, those skipped iterations will still be included in
+ * the count.
+ */
+ public Integer getCount(String name)
+ {
+ // just tell the matching one to skip
+ ManagedIterator iterator = findIterator(name);
+ if (iterator != null)
+ {
+ return iterator.getCount();
+ }
+ return null;
+ }
+
+
+ /**
+ * Finds the {@link ManagedIterator} with the specified name
+ * if it is in this instance's iterator stack.
+ */
+ protected ManagedIterator findIterator(String name)
+ {
+ // look for the one with the specified name
for (ManagedIterator iterator : iterators)
{
if (iterator.getName().equals(name))
{
- iterator.skip(number);
- break;
+ return iterator;
}
}
+ return null;
}
/**
@@ -305,6 +425,8 @@
private Iterator iterator;
private LoopTool owner;
private boolean stopped = false;
+ private Boolean first = null;
+ private int count = 0;
public ManagedIterator(Iterator iterator, LoopTool owner)
{
@@ -327,6 +449,20 @@
return this.name;
}
+ public boolean isFirst()
+ {
+ if (first == null || first.booleanValue())
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isLast()
+ {
+ return !iterator.hasNext();
+ }
+
public boolean hasNext()
{
if (!stopped)
@@ -336,6 +472,8 @@
if (!hasNext)
{
owner.pop();
+ // and make sure we don't pop twice
+ stopped = true;
}
return hasNext;
}
@@ -345,8 +483,22 @@
}
}
+ public int getCount()
+ {
+ return count;
+ }
+
public Object next()
{
+ if (first == null)
+ {
+ first = Boolean.TRUE;
+ }
+ else if (first.booleanValue())
+ {
+ first = Boolean.FALSE;
+ }
+ count++;
return iterator.next();
}
@@ -367,13 +519,19 @@
{
if (iterator.hasNext())
{
- iterator.next();
+ next();
}
else
{
break;
}
}
+ }
+
+ @Override
+ public String toString()
+ {
+ return ManagedIterator.class.getSimpleName()+':'+getName();
}
}
Modified: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LoopToolTests.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LoopToolTests.java?rev=605051&r1=605050&r2=605051&view=diff
==============================================================================
--- velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LoopToolTests.java (original)
+++ velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LoopToolTests.java Mon Dec 17 16:23:22 2007
@@ -193,5 +193,116 @@
assertNotNull(loop.watch(ARRAY, "name"));
}
+ public @Test void methodIsFirst() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY);
+ assertTrue(loop.isFirst());
+ i.next();
+ assertTrue(loop.isFirst());
+ i.next();
+ assertFalse(loop.isFirst());
+ }
+
+ public @Test void methodIsFirst_String() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY, "i");
+ assertTrue(loop.isFirst());
+ i.next();
+ Iterator j = loop.watch(ARRAY, "j");
+ assertTrue(loop.isFirst());
+ assertTrue(loop.isFirst("i"));
+ assertTrue(loop.isFirst("j"));
+ i.next();
+ assertFalse(loop.isFirst("i"));
+ j.next();
+ assertTrue(loop.isFirst());
+ assertTrue(loop.isFirst("j"));
+ j.next();
+ assertFalse(loop.isFirst("j"));
+ }
+
+ public @Test void methodIsLast() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY);
+ assertFalse(loop.isLast());
+ i.next();
+ assertFalse(loop.isLast());
+ i.next();
+ assertFalse(loop.isLast());
+ i.next();
+ assertTrue(loop.isLast());
+ }
+
+ public @Test void methodIsLast_String() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY, "i");
+ assertFalse(loop.isLast());
+ i.next();
+ i.next();
+ i.next();
+ assertTrue(loop.isLast());
+ Iterator j = loop.watch(ARRAY, "j");
+ assertFalse(loop.isLast());
+ assertTrue(loop.isLast("i"));
+ assertFalse(loop.isLast("j"));
+ j.next();
+ j.next();
+ j.next();
+ assertTrue(loop.isLast());
+ assertTrue(loop.isLast("j"));
+ }
+
+ public @Test void methodGetCount() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY);
+ assertEquals(0, loop.getCount());
+ i.next();
+ assertEquals(1, loop.getCount());
+ i.next();
+ assertEquals(2, loop.getCount());
+ i.next();
+ assertEquals(3, loop.getCount());
+ loop.pop();
+ // test that skipped iterations are still included
+ i = loop.watch(ARRAY);
+ loop.skip(2);
+ assertEquals(2, loop.getCount());
+ }
+
+ public @Test void methodGetCount_String() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY, "i");
+ assertEquals(0, loop.getCount("i"));
+ i.next();
+ assertEquals(1, loop.getCount("i"));
+ Iterator j = loop.watch(ARRAY, "j");
+ loop.skip(2);
+ assertEquals(2, loop.getCount("j"));
+ assertEquals(1, loop.getCount("i"));
+ }
+
+ public @Test void aliasMethods() throws Exception
+ {
+ LoopTool loop = new LoopTool();
+ Iterator i = loop.watch(ARRAY);
+ assertEquals(loop.isFirst(), loop.getFirst());
+ assertEquals(loop.isLast(), loop.getLast());
+ i.next();
+ assertEquals(loop.isFirst(), loop.getFirst());
+ assertEquals(loop.isLast(), loop.getLast());
+ i.next();
+ assertEquals(loop.isFirst(), loop.getFirst());
+ assertEquals(loop.isLast(), loop.getLast());
+ i.next();
+ assertEquals(loop.isFirst(), loop.getFirst());
+ assertEquals(loop.isLast(), loop.getLast());
+ }
+
}