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 2010/04/07 18:47:56 UTC

svn commit: r931617 - /velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java

Author: nbubna
Date: Wed Apr  7 16:47:56 2010
New Revision: 931617

URL: http://svn.apache.org/viewvc?rev=931617&view=rev
Log:
VELTOOLS-124 provide quasi-fix for count-base properties on last iteration, as well as getThis() to offer a reliable workaround in nested loop situations

Modified:
    velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java

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=931617&r1=931616&r2=931617&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 Wed Apr  7 16:47:56 2010
@@ -49,13 +49,12 @@ import org.apache.velocity.tools.config.
  * on its first or last iteration, and so on.
  * </p>
  * <p>
- * Most usage, of course, will probably never go much beyond the 
- * simple {@link #watch(Object)}, {@link ManagedIterator#sync(Object)}, 
- * {@link ManagedIterator#stop(Object)}, {@link #isFirst},
- * {@link #isLast},and maybe {@link #getCount} or {@link #getIndex}
- * 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.
+ * 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.
  * </p>
  * <p>
  * Example of use:
@@ -94,6 +93,7 @@ import org.apache.velocity.tools.config.
 public class LoopTool
 {
     private Stack<ManagedIterator> iterators = new Stack<ManagedIterator>();
+    private ManagedIterator last;
 
     /**
      * <p>Tells the LoopTool to watch the specified Array, Collection, Map,
@@ -117,6 +117,7 @@ public class LoopTool
 
         ManagedIterator managed = manage(iterator, null);
         iterators.push(managed);
+        this.last = managed;
         return managed;
     }
 
@@ -145,6 +146,7 @@ public class LoopTool
 
         ManagedIterator managed = manage(iterator, name);
         iterators.push(managed);
+        this.last = managed;
         return managed;
     }
 
@@ -306,11 +308,11 @@ public class LoopTool
      */
     public Boolean isFirst()
     {
-        if (iterators.empty())
+        if (last != null)
         {
-            return null;
+            return last.isFirst();
         }
-        return iterators.peek().isFirst();
+        return null;
     }
 
     /**
@@ -341,11 +343,11 @@ public class LoopTool
      */
     public Boolean isLast()
     {
-        if (iterators.empty())
+        if (last != null)
         {
-            return null;
+            return last.isLast();
         }
-        return iterators.peek().isLast();
+        return null;
     }
 
     /**
@@ -381,7 +383,7 @@ public class LoopTool
      * 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.
-     * {@code $loop.foo_count} or {@code $loop.foo_first}).
+     * {@code $loop.count_foo} or {@code $loop.first_foo}).
      * This syntax is shorter and clearer than {@code $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.
@@ -480,11 +482,11 @@ public class LoopTool
      */
     public Integer getCount()
     {
-        if (iterators.empty())
+        if (last != null)
         {
-            return null;
+            return last.getCount();
         }
-        return iterators.peek().getCount();
+        return null;
     }
 
     /**
@@ -505,8 +507,22 @@ public class LoopTool
     }
 
     /**
-     * Returns the number of loops currently on the stack to tell
-     * how deeply nested the current loop is.
+     * Returns the most recent {@link 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 ManagedIterator getThis()
+    {
+        return last;
+    }
+
+    /**
+     * 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".
      */
     public int getDepth()
     {
@@ -535,9 +551,9 @@ public class LoopTool
      * Don't let templates call this, but allow subclasses
      * and ManagedIterator to have access.
      */
-    protected void pop()
+    protected ManagedIterator pop()
     {
-        iterators.pop();
+        return iterators.pop();
     }
 
 
@@ -632,6 +648,22 @@ public class LoopTool
         }
 
         /**
+         * Returns the result of {@link #isFirst}. Exists to allow $loop.this.first syntax.
+         */
+        public boolean getFirst()
+        {
+            return isFirst();
+        }
+
+        /**
+         * Returns the result of {@link #isLast}. Exists to allow $loop.this.last syntax.
+         */
+        public boolean getLast()
+        {
+            return isLast();
+        }
+
+        /**
          * Returns true if there are more elements in the iterator
          * being managed by this instance which satisfy all the
          * {@link ActionCondition}s set for this instance.  Returns
@@ -642,6 +674,14 @@ public class LoopTool
             return hasNext(true);
         }
 
+        /**
+         * Returns the result of {@link #hasNext}. Exists to allow $loop.this.hasNext syntax.
+         */
+        public boolean getHasNext()
+        {
+            return hasNext(false);//no need to pop, #foreach will always call hasNext()
+        }
+
         // version that lets isLast check w/o popping this from the stack
         private boolean hasNext(boolean popWhenDone)
         {
@@ -757,6 +797,14 @@ public class LoopTool
         }
 
         /**
+         * Returns the 0-based index of the current item.
+         */
+        public int getIndex()
+        {
+            return count - 1;
+        }
+
+        /**
          * Returns the next element that meets the set {@link ActionCondition}s
          * (if any) in the iterator being managed. If there are none left, then
          * this will throw a {@link NoSuchElementException}.