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());
+    }
+
 }