Author: nbubna
Date: Sun Jan 25 17:24:29 2009
New Revision: 737539

URL: http://svn.apache.org/viewvc?rev=737539&view=rev
Log:
VELOCITY-686 bring parity between block macro bodies and #define'd references

Added:
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Block.java 
  (with props)
Modified:
    
velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/BlockMacro.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Define.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/Node.java
    
velocity/engine/trunk/src/test/org/apache/velocity/test/BlockMacroTestCase.java

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java 
(original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java 
Sun Jan 25 17:24:29 2009
@@ -28,6 +28,7 @@
 import org.apache.velocity.app.event.EventCartridge;
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.Renderable;
 import org.apache.velocity.runtime.RuntimeServices;
 import org.apache.velocity.runtime.parser.ParserTreeConstants;
 import org.apache.velocity.runtime.parser.node.ASTReference;
@@ -107,6 +108,24 @@
     }
 
     /**
+     * Used to put Velocity macro bodyContext arguments into this context. 
+     * 
+     * @param context rendering context
+     * @param macroArgumentName name of the macro argument that we received
+     * @param literalMacroArgumentName ".literal.$"+macroArgumentName
+     * @param argumentValue actual value of the macro body
+     * 
+     * @throws MethodInvocationException
+     */
+    public void addVMProxyArg(InternalContextAdapter context,
+                              String macroArgumentName,
+                              String literalMacroArgumentName,
+                              Renderable argumentValue) throws 
MethodInvocationException
+    {
+        localcontext.put(macroArgumentName, argumentValue);
+    }
+
+    /**
      * AST nodes that are considered constants can be directly
      * saved into the context. Dynamic values are stored in
      * another argument hashmap.
@@ -124,7 +143,6 @@
             case ParserTreeConstants.JJTMAP:
             case ParserTreeConstants.JJTSTRINGLITERAL:
             case ParserTreeConstants.JJTTEXT:
-            case ParserTreeConstants.JJTBLOCK:
                 return (false);
             default:
                 return (true);
@@ -220,28 +238,6 @@
                     return obj;
                 }
             }
-            else if (type == ParserTreeConstants.JJTBLOCK)
-            {
-                // this happens for #...@somemacro($arg1 $arg2) bodyAST #end 
calls
-                try
-                {
-                    // astNode is actually BlockMacro.BlockMacroContainer 
which contains a Writer internally although
-                    // we seem to pass null here
-                    astNode.render(innerContext, null);
-                    // return an empty string because the Node already 
rendered all content
-                    return "";
-                }
-                catch (RuntimeException e)
-                {
-                    throw e;
-                }
-                catch (Exception e)
-                {
-                    String msg = "ProxyVMContext.get() : error rendering 
reference";
-                    rsvc.getLog().error(msg, e);
-                    throw new VelocityException(msg, e);
-                }
-            }
             else if (type == ParserTreeConstants.JJTTEXT)
             {
                 // this really shouldn't happen. text is just a throwaway arg 
for #foreach()

Added: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Block.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Block.java?rev=737539&view=auto
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Block.java 
(added)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Block.java 
Sun Jan 25 17:24:29 2009
@@ -0,0 +1,173 @@
+package org.apache.velocity.runtime.directive;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+
+import java.io.Writer;
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.apache.commons.lang.text.StrBuilder;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.TemplateInitException;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.Renderable;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.log.Log;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * Directive that puts an unrendered AST block in the context
+ * under the specified key, postponing rendering until the
+ * reference is used and rendered.
+ *
+ * @author Andrew Tetlaw
+ * @author Nathan Bubna
+ * @author <a href="mailto:[email protected]";>Jarkko Viinamaki</a>
+ * @since 1.7
+ * @version $Id: Block.java 686842 2008-08-18 18:29:31Z nbubna $
+ */
+public abstract class Block extends Directive
+{
+    protected Node block;
+    protected Log log;
+    protected int maxDepth;
+    protected String definingTemplate;
+    protected String key;
+
+    /**
+     * Return type of this directive.
+     */
+    public int getType()
+    {
+        return BLOCK;
+    }
+
+    /**
+     *  simple init - get the key
+     */
+    public void init(RuntimeServices rs, InternalContextAdapter context, Node 
node)
+        throws TemplateInitException
+    {
+        super.init(rs, context, node);
+
+        log = rs.getLog();
+
+        /**
+         * No checking is done. We just grab the last child node and assume
+         * that it's the block!
+         */
+        block = node.jjtGetChild(node.jjtGetNumChildren() - 1);
+
+        /**
+         * keep tabs on the template this came from
+         */
+        definingTemplate = context.getCurrentTemplateName();
+    }
+
+    /**
+     * Creates a string identifying the source and location of the block
+     * definition, and the current template being rendered if that is
+     * different.
+     */
+    protected String id(InternalContextAdapter context)
+    {
+        StrBuilder str = new StrBuilder(100)
+            .append("block $").append(key)
+            .append(" (defined in ").append(definingTemplate)
+            .append(" [line ").append(getLine())
+            .append(", column ").append(getColumn()).append("])");
+
+        if (!context.getCurrentTemplateName().equals(definingTemplate))
+        {
+            str.append(" used in ").append(context.getCurrentTemplateName());
+        }
+        return str.toString();
+    }
+    
+    /**
+     * actual class placed in the context, holds the context
+     * being used for the render, as well as the parent (which already holds
+     * everything else we need).
+     */
+    public static class Reference implements Renderable
+    {
+        private InternalContextAdapter context;
+        private Block parent;
+        private int depth;
+        
+        public Reference(InternalContextAdapter context, Block parent)
+        {
+            this.context = context;
+            this.parent = parent;
+        }
+        
+        /**
+         * Render the AST of this block into the writer using the context.
+         */
+        public boolean render(InternalContextAdapter context, Writer writer)
+        {
+            try
+            {
+                depth++;
+                if (depth > parent.maxDepth)
+                {
+                    /* this is only a debug message, as recursion can
+                     * happen in quasi-innocent situations and is relatively
+                     * harmless due to how we handle it here.
+                     * this is more to help anyone nuts enough to intentionally
+                     * use recursive block definitions and having problems
+                     * pulling it off properly.
+                     */
+                    parent.log.debug("Max recursion depth reached for 
"+parent.id(context));
+                    depth--;
+                    return false;
+                }
+                else
+                {
+                    parent.block.render(context, writer);
+                    depth--;
+                    return true;
+                }
+            }
+            catch (IOException e)
+            {
+                String msg = "Failed to render "+parent.id(context)+" to 
writer";
+                parent.log.error(msg, e);
+                throw new RuntimeException(msg, e);
+            }
+            catch (VelocityException ve)
+            {
+                String msg = "Failed to render "+parent.id(context)+" due to 
"+ve;
+                parent.log.error(msg, ve);
+                throw ve;
+            }
+        }
+
+        public String toString()
+        {
+            Writer writer = new StringWriter();
+            if (render(context, writer))
+            {
+                return writer.toString();
+            }
+            return null;
+        }
+    }
+}

Propchange: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Block.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/BlockMacro.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/BlockMacro.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/BlockMacro.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/BlockMacro.java
 Sun Jan 25 17:24:29 2009
@@ -21,16 +21,12 @@
 
 import java.io.IOException;
 import java.io.Writer;
-
 import org.apache.velocity.context.InternalContextAdapter;
-import org.apache.velocity.exception.MethodInvocationException;
-import org.apache.velocity.exception.ParseErrorException;
-import org.apache.velocity.exception.ResourceNotFoundException;
 import org.apache.velocity.exception.TemplateInitException;
+import org.apache.velocity.runtime.Renderable;
+import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeServices;
-import org.apache.velocity.runtime.parser.ParserTreeConstants;
 import org.apache.velocity.runtime.parser.node.Node;
-import org.apache.velocity.runtime.parser.node.SimpleNode;
 
 /**
  * BlockMacro directive is used to invoke Velocity macros with normal 
parameters and a macro body.
@@ -58,36 +54,23 @@
  * bodyContent reference name is configurable (see velocity.properties).
  *
  * @author <a href="mailto:[email protected]";>Jarkko Viinamaki</a>
- * @since 1.6.2
+ * @since 1.7
  * @version $Id$
  */
-public class BlockMacro extends Directive
+public class BlockMacro extends Block
 {
     private String name;
     private RuntimeMacro macro;
-    private Node macroBody;
 
     public BlockMacro(String name)
     {
         this.name = name;
     }
     
-    /**
-     * Return name of this directive.
-     * @return The name of this directive.
-     */
+    // This is required, but not actually used.
     public String getName()
     {
-        return "blockmacro";
-    }
-
-    /**
-     * Return type of this directive.
-     * @return The type of this directive.
-     */
-    public int getType()
-    {
-        return BLOCK;
+        throw new UnsupportedOperationException("BlockMacro is not actually a 
named macro.");
     }
 
     /**
@@ -98,13 +81,16 @@
      * @param node
      * @throws TemplateInitException
      */
-    public void init(RuntimeServices rs, InternalContextAdapter context,
-                     Node node)
+    public void init(RuntimeServices rs, InternalContextAdapter context, Node 
node)
         throws TemplateInitException
     {
         super.init(rs, context, node);
         
-        macroBody = node.jjtGetChild(node.jjtGetNumChildren() - 1);
+        // get name of the reference that refers to AST block passed to block 
macro call
+        key = rsvc.getString(RuntimeConstants.VM_BODY_REFERENCE, 
"bodyContent");
+
+        // use the macro max depth for bodyContent max depth as well
+        maxDepth = rs.getInt(RuntimeConstants.VM_MAX_DEPTH);
 
         macro = new RuntimeMacro(name);
         macro.setLocation(getLine(), getColumn(), getTemplateName());
@@ -123,35 +109,7 @@
     public boolean render(InternalContextAdapter context, Writer writer, Node 
node)
         throws IOException
     {
-        return macro.render(context, 
-                            writer, 
-                            node,
-                            new BlockMacroContainer(macroBody, 
ParserTreeConstants.JJTBLOCK, writer));
+        return macro.render(context, writer, node, new Reference(context, 
this));
     }
 
-    /**
-     * BlockMacro body is wrapped in this container. 
-     *
-     * With this we can have reference to the output Writer in 
ProxyVMContext.get call.
-     * This approach lets us write directly to the output instead of using 
costly StringWriter.
-     */
-    public static class BlockMacroContainer extends SimpleNode
-    {
-        private Node node;
-        private Writer w;
-    
-        public BlockMacroContainer(Node node, int type, Writer writer)
-        {
-            super(type);
-            this.node = node;
-            this.w = writer;
-        }
-        
-        public boolean render( InternalContextAdapter context, Writer writer)
-            throws IOException, MethodInvocationException, 
ParseErrorException, ResourceNotFoundException
-        {
-            return node.render(context, writer != null ? writer : w);
-        }
-    }
 }
-

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Define.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Define.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Define.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Define.java
 Sun Jan 25 17:24:29 2009
@@ -20,17 +20,10 @@
  */
 
 import java.io.Writer;
-import java.io.IOException;
-import java.io.StringWriter;
-
-import org.apache.commons.lang.text.StrBuilder;
 import org.apache.velocity.context.InternalContextAdapter;
 import org.apache.velocity.exception.TemplateInitException;
-import org.apache.velocity.exception.VelocityException;
-import org.apache.velocity.runtime.Renderable;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeServices;
-import org.apache.velocity.runtime.log.Log;
 import org.apache.velocity.runtime.parser.node.Node;
 
 /**
@@ -42,14 +35,8 @@
  * @author Nathan Bubna
  * @version $Id: Define.java 686842 2008-08-18 18:29:31Z nbubna $
  */
-public class Define extends Directive
+public class Define extends Block
 {
-    private String key;
-    private Node block;
-    private Log log;
-    private int maxDepth;
-    private String definingTemplate;
-    
     /**
      * Return name of this directive.
      */
@@ -59,14 +46,6 @@
     }
 
     /**
-     * Return type of this directive.
-     */
-    public int getType()
-    {
-        return BLOCK;
-    }
-
-    /**
      *  simple init - get the key
      */
     public void init(RuntimeServices rs, InternalContextAdapter context, Node 
node)
@@ -74,14 +53,6 @@
     {
         super.init(rs, context, node);
 
-        log = rs.getLog();
-
-        /*
-         * default max depth of two is used because intentional recursion is
-         * unlikely and discouraged, so make unintentional ones end fast
-         */
-        maxDepth = rs.getInt(RuntimeConstants.DEFINE_DIRECTIVE_MAXDEPTH, 2);
-
         /*
          * first token is the name of the block. We don't even check the 
format,
          * just assume it looks like this: $block_name. Should we check if it 
has
@@ -89,16 +60,11 @@
          */
         key = node.jjtGetChild(0).getFirstToken().image.substring(1);
 
-        /**
-         * No checking is done. We just grab the second child node and assume
-         * that it's the block!
-         */
-        block = node.jjtGetChild(1);
-
-        /**
-         * keep tabs on the template this came from
+        /*
+         * default max depth of two is used because intentional recursion is
+         * unlikely and discouraged, so make unintentional ones end fast
          */
-        definingTemplate = context.getCurrentTemplateName();
+        maxDepth = rs.getInt(RuntimeConstants.DEFINE_DIRECTIVE_MAXDEPTH, 2);
     }
 
     /**
@@ -107,104 +73,11 @@
      */
     public boolean render(InternalContextAdapter context, Writer writer, Node 
node)
     {
-        /* put a Block instance into the context,
+        /* put a Block.Reference instance into the context,
          * using the user-defined key, for later inline rendering.
          */
-        context.put(key, new Block(context, this));
+        context.put(key, new Reference(context, this));
         return true;
     }
 
-    /**
-     * Creates a string identifying the source and location of the block
-     * definition, and the current template being rendered if that is
-     * different.
-     */
-    protected String id(InternalContextAdapter context)
-    {
-        StrBuilder str = new StrBuilder(100)
-            .append("block $").append(key)
-            .append(" (defined in ").append(definingTemplate)
-            .append(" [line ").append(getLine())
-            .append(", column ").append(getColumn()).append("])");
-
-        if (!context.getCurrentTemplateName().equals(definingTemplate))
-        {
-            str.append(" used in ").append(context.getCurrentTemplateName());
-        }
-
-        return str.toString();
-    }
-    
-    /**
-     * actual class placed in the context, holds the context and writer
-     * being used for the render, as well as the parent (which already holds
-     * everything else we need).
-     */
-    public static class Block implements Renderable
-    {
-        private InternalContextAdapter context;
-        private Define parent;
-        private int depth;
-        
-        public Block(InternalContextAdapter context, Define parent)
-        {
-            this.context = context;
-            this.parent = parent;
-        }
-        
-        /**
-         *
-         */
-        public boolean render(InternalContextAdapter context, Writer writer)
-        {
-            try
-            {
-                depth++;
-                if (depth > parent.maxDepth)
-                {
-                    /* this is only a debug message, as recursion can
-                     * happen in quasi-innocent situations and is relatively
-                     * harmless due to how we handle it here.
-                     * this is more to help anyone nuts enough to intentionally
-                     * use recursive block definitions and having problems
-                     * pulling it off properly.
-                     */
-                    parent.log.debug("Max recursion depth reached for 
"+parent.id(context));
-                    depth--;
-                    return false;
-                }
-                else
-                {
-                    parent.block.render(context, writer);
-                    depth--;
-                    return true;
-                }
-            }
-            catch (IOException e)
-            {
-                String msg = "Failed to render "+parent.id(context)+" to 
writer";
-                parent.log.error(msg, e);
-                throw new RuntimeException(msg, e);
-            }
-            catch (VelocityException ve)
-            {
-                String msg = "Failed to render "+parent.id(context)+" due to 
"+ve;
-                parent.log.error(msg, ve);
-                throw ve;
-            }
-        }
-
-        public String toString()
-        {
-            Writer stringwriter = new StringWriter();
-            if(render(context,stringwriter))
-            {
-                return stringwriter.toString();
-            }
-            else
-            {
-                return null;
-            }
-        }
-    }
 }

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
 Sun Jan 25 17:24:29 2009
@@ -30,6 +30,7 @@
 import org.apache.velocity.exception.ResourceNotFoundException;
 import org.apache.velocity.exception.TemplateInitException;
 import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.Renderable;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeServices;
 import org.apache.velocity.runtime.log.Log;
@@ -229,7 +230,7 @@
      * @throws MethodInvocationException
      */
     public boolean render(InternalContextAdapter context, Writer writer,
-                          Node node, Node body)
+                          Node node, Renderable body)
             throws IOException, ResourceNotFoundException,
             ParseErrorException, MethodInvocationException
     {

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java
 Sun Jan 25 17:24:29 2009
@@ -29,6 +29,7 @@
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.TemplateInitException;
 import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.Renderable;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.RuntimeServices;
 import org.apache.velocity.runtime.log.Log;
@@ -145,7 +146,8 @@
      * @throws MethodInvocationException
      * @throws MacroOverflowException
      */
-    public boolean render(InternalContextAdapter context, Writer writer, Node 
node, Node body)
+    public boolean render(InternalContextAdapter context, Writer writer,
+                          Node node, Renderable body)
             throws IOException, MethodInvocationException, 
MacroOverflowException
     {
         // wrap the current context and add the macro arguments

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/Node.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/Node.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/Node.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/Node.java
 Sun Jan 25 17:24:29 2009
@@ -27,6 +27,7 @@
 import org.apache.velocity.exception.ParseErrorException;
 import org.apache.velocity.exception.ResourceNotFoundException;
 import org.apache.velocity.exception.TemplateInitException;
+import org.apache.velocity.runtime.Renderable;
 import org.apache.velocity.runtime.parser.Token;
 
 /**
@@ -37,7 +38,7 @@
  * @version $Id$
  */
 
-public interface Node
+public interface Node extends Renderable
 {
     /** This method is called after the node has been made the current
      * node.  It indicates that child nodes can now be added to it. */

Modified: 
velocity/engine/trunk/src/test/org/apache/velocity/test/BlockMacroTestCase.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/BlockMacroTestCase.java?rev=737539&r1=737538&r2=737539&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/test/org/apache/velocity/test/BlockMacroTestCase.java 
(original)
+++ 
velocity/engine/trunk/src/test/org/apache/velocity/test/BlockMacroTestCase.java 
Sun Jan 25 17:24:29 2009
@@ -19,18 +19,7 @@
  * under the License.    
  */
 
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import junit.framework.TestCase;
-
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.Velocity;
 import org.apache.velocity.runtime.RuntimeConstants;
-import org.apache.velocity.test.misc.TestLogChute;
-import org.apache.velocity.test.provider.ForeachMethodCallHelper;
 
 /**
  * This class tests the BlockMacro functionality.
@@ -102,5 +91,28 @@
         engine.setProperty(RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE);
         assertEvalEquals(" ", "#macro(foo)#end #...@foo() junk #end");
     }
+
+    public void testVelocity686() throws Exception
+    {
+        String template = "#macro(foo)#set( $x = $bodyContent )#end"+
+                          "#...@foo()b#end a $x ";
+        assertEvalEquals(" a b ", template);
+    }
+
+    public void testNestedBlockMacro()
+    {
+        String template = "#macro(foo)foo:$bodyContent#end"+
+                          "#macro(bar)bar:$bodyContent#end"+
+                          "#...@foo()foo,#...@bar()bar#end#end";
+        assertEvalEquals("foo:foo,bar:bar", template);
+    }
+
+    public void testRecursiveBlockMacro()
+    {
+        engine.setProperty(RuntimeConstants.VM_MAX_DEPTH, 3);
+        String template = "#macro(foo)start:$bodyContent#end"+
+                          "#...@foo()call:$bodyContent#end";
+        assertEvalEquals("start:call:call:call:$bodyContent", template);
+    }
 }
 


Reply via email to