You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by wg...@apache.org on 2007/08/22 18:31:34 UTC

svn commit: r568691 [1/2] - in /velocity/engine/trunk: src/java/org/apache/velocity/ src/java/org/apache/velocity/context/ src/java/org/apache/velocity/runtime/directive/ src/java/org/apache/velocity/runtime/parser/ src/java/org/apache/velocity/runtime...

Author: wglass
Date: Wed Aug 22 09:31:31 2007
New Revision: 568691

URL: http://svn.apache.org/viewvc?rev=568691&view=rev
Log:
refactoring of macro parsing to allow macros to be parsed at run time.  addresses combination of #parse and macro calls from VELOCITY-362, VELOCITY-277, VELOCITY-146.  Also adds new feature -- developers can specify macro libraries to be included at merge time with new overloaded mergeTemplate method call.  (VELOCITY-529).

Added:
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java   (with props)
    velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java   (with props)
    velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java   (with props)
    velocity/engine/trunk/test/macrolibs/
    velocity/engine/trunk/test/macrolibs/compare/
    velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp   (with props)
    velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp   (with props)
    velocity/engine/trunk/test/macrolibs/compare/vm_library_local.cmp   (with props)
    velocity/engine/trunk/test/macrolibs/vm_library.vm   (with props)
    velocity/engine/trunk/test/macrolibs/vm_library1.vm   (with props)
    velocity/engine/trunk/test/macrolibs/vm_library2.vm   (with props)
    velocity/engine/trunk/test/macrolibs/vm_library_global.vm   (with props)
    velocity/engine/trunk/test/macrolibs/vm_library_local.vm   (with props)
    velocity/engine/trunk/test/parsemacros/
    velocity/engine/trunk/test/parsemacros/compare/
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_1.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_1b.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_2.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_2b.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_3.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_3b.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_4.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro1_4b.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/compare/parseMacro2.cmp   (with props)
    velocity/engine/trunk/test/parsemacros/parseMacro1.vm   (with props)
    velocity/engine/trunk/test/parsemacros/parseMacro2.vm   (with props)
    velocity/engine/trunk/test/parsemacros/vm_library1.vm   (with props)
    velocity/engine/trunk/test/parsemacros/vm_library2.vm   (with props)
Modified:
    velocity/engine/trunk/src/java/org/apache/velocity/Template.java
    velocity/engine/trunk/src/java/org/apache/velocity/context/EvaluateContext.java
    velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextAdapterImpl.java
    velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextBase.java
    velocity/engine/trunk/src/java/org/apache/velocity/context/InternalHousekeepingContext.java
    velocity/engine/trunk/src/java/org/apache/velocity/context/VMContext.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Foreach.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Parse.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.jj
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTDirective.java
    velocity/engine/trunk/src/parser/Parser.jjt
    velocity/engine/trunk/src/test/org/apache/velocity/test/MacroForwardDefineTestCase.java
    velocity/engine/trunk/test/macroforwarddefine/compare/velocity.log.cmp

Modified: velocity/engine/trunk/src/java/org/apache/velocity/Template.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/Template.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/Template.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/Template.java Wed Aug 22 09:31:31 2007
@@ -25,6 +25,7 @@
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
+import java.util.List;
 
 import org.apache.velocity.context.Context;
 import org.apache.velocity.context.InternalContextAdapterImpl;
@@ -228,6 +229,27 @@
     public void merge( Context context, Writer writer)
         throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException
     {
+        merge(context, writer, null);
+    }
+
+    
+    /**
+     * The AST node structure is merged with the
+     * context to produce the final output.
+     *
+     *  @param context Conext with data elements accessed by template
+     *  @param writer output writer for rendered template
+     *  @param macroLibraries a list of template files containing macros to be used when merging
+     *  @throws ResourceNotFoundException if template not found
+     *          from any available source.
+     *  @throws ParseErrorException if template cannot be parsed due
+     *          to syntax (or other) error.
+     *  @throws MethodInvocationException When a method on a referenced object in the context could not invoked.
+     *  @throws IOException  Might be thrown while rendering.
+     */
+    public void merge( Context context, Writer writer, List macroLibraries)
+        throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException
+    {
         /*
          *  we shouldn't have to do this, as if there is an error condition,
          *  the application code should never get a reference to the
@@ -248,6 +270,52 @@
 
             InternalContextAdapterImpl ica = new InternalContextAdapterImpl( context );
 
+            /**
+             * Set the macro libraries
+             */
+            ica.setMacroLibraries(macroLibraries);
+
+            if (macroLibraries != null)
+            {
+                for (int i = 0; i < macroLibraries.size(); i++)
+                {
+                    /**
+                     * Build the macro library
+                     */
+                    try
+                    {
+                        rsvc.getTemplate((String) macroLibraries.get(i));
+                    }
+                    catch (ResourceNotFoundException re)
+                    {
+                        /*
+                        * the macro lib wasn't found.  Note it and throw
+                        */
+                        rsvc.getLog().error("template.merge(): " +
+                                "cannot find template " +
+                                (String) macroLibraries.get(i));
+                        throw re;
+                    }
+                    catch (ParseErrorException pe)
+                    {
+                        /*
+                        * the macro lib was found, but didn't parse - syntax error
+                        *  note it and throw
+                        */
+                        rsvc.getLog().error("template.merge(): " +
+                                "syntax error in template " +
+                                (String) macroLibraries.get(i) + ".");
+                        throw pe;
+                    }
+                    
+                    catch (Exception e)
+                    {
+                        throw new RuntimeException("Template.merge(): parse failed in template  " +
+                                (String) macroLibraries.get(i) + ".", e);
+                    }
+                }
+            }
+
             try
             {
                 ica.pushCurrentTemplateName( name );
@@ -278,5 +346,6 @@
         }
     }
 }
+
 
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/context/EvaluateContext.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/context/EvaluateContext.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/context/EvaluateContext.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/context/EvaluateContext.java Wed Aug 22 09:31:31 2007
@@ -21,6 +21,7 @@
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.List;
 
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.event.EventCartridge;
@@ -287,6 +288,22 @@
     }
 
     /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#setMacroLibraries(List)
+     */
+    public void setMacroLibraries(List macroLibraries)
+    {
+        innerContext.setMacroLibraries(macroLibraries);
+    }
+    
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getMacroLibraries()
+     */
+    public List getMacroLibraries()
+    {
+        return innerContext.getMacroLibraries();
+    }
+
+    /**
      * @see org.apache.velocity.context.InternalEventContext#attachEventCartridge(org.apache.velocity.app.event.EventCartridge)
      */
     public EventCartridge attachEventCartridge( EventCartridge ec )
@@ -320,6 +337,7 @@
         return innerContext.getCurrentResource();
     }
 }
+
 
 
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextAdapterImpl.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextAdapterImpl.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextAdapterImpl.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextAdapterImpl.java Wed Aug 22 09:31:31 2007
@@ -23,6 +23,8 @@
 import org.apache.velocity.runtime.resource.Resource;
 import org.apache.velocity.util.introspection.IntrospectionCacheData;
 
+import java.util.List;
+
 /**
  *  This adapter class is the container for all context types for internal
  *  use.  The AST now uses this class rather than the app-level Context
@@ -192,6 +194,21 @@
         icb.setAllowRendering(v);
     }
 
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#setMacroLibraries(List)
+     */
+    public void setMacroLibraries(List macroLibraries)
+    {
+        icb.setMacroLibraries(macroLibraries);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getMacroLibraries()
+     */
+    public List getMacroLibraries()
+    {
+        return icb.getMacroLibraries();
+    }
 
     /* ---  Context interface methods --- */
 
@@ -202,13 +219,13 @@
     {
         return context.put( key , value );
     }
-    
+
     /**
      * @see InternalWrapperContext#localPut(String, Object)
      */
     public Object localPut(final String key, final Object value)
     {
-	return put(key, value);
+    return put(key, value);
     }
 
     /**
@@ -296,5 +313,6 @@
         return null;
     }
 }
+
 
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextBase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextBase.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextBase.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/context/InternalContextBase.java Wed Aug 22 09:31:31 2007
@@ -21,6 +21,7 @@
 
 import java.util.HashMap;
 import java.util.Stack;
+import java.util.List;
 
 import org.apache.velocity.app.event.EventCartridge;
 import org.apache.velocity.runtime.resource.Resource;
@@ -75,6 +76,12 @@
     private boolean allowRendering = true;
 
     /**
+     *  List for holding the macro libraries. Contains the macro library
+     *  template name as strings.
+     */
+    private List macroLibraries = null;
+
+    /**
      *  set the current template name on top of stack
      *
      *  @param s current template name
@@ -172,6 +179,22 @@
         allowRendering = v;
     }
 
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#setMacroLibraries(List)
+     */
+    public void setMacroLibraries(List macroLibraries)
+    {
+        this.macroLibraries = macroLibraries;
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getMacroLibraries()
+     */
+    public List getMacroLibraries()
+    {
+        return macroLibraries;
+    }
+
 
     /**
      * @see org.apache.velocity.context.InternalEventContext#attachEventCartridge(org.apache.velocity.app.event.EventCartridge)
@@ -193,4 +216,5 @@
         return eventCartridge;
     }
 }
+
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/context/InternalHousekeepingContext.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/context/InternalHousekeepingContext.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/context/InternalHousekeepingContext.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/context/InternalHousekeepingContext.java Wed Aug 22 09:31:31 2007
@@ -23,6 +23,8 @@
 
 import org.apache.velocity.runtime.resource.Resource;
 
+import java.util.List;
+
 /**
  *  interface to encapsulate the 'stuff' for internal operation of velocity.
  *  We use the context as a thread-safe storage : we take advantage of the
@@ -111,5 +113,19 @@
      * @param v
      */
      void setAllowRendering(boolean v);
+
+    /**
+     * Set the macro library list for the current template.
+     *
+     * @param macroLibraries list of macro libraries to set
+     */
+     void setMacroLibraries(List macroLibraries);
+
+    /**
+     * Get the macro library list for the current template.
+     *
+     * @return List of macro library names
+     */
+     List getMacroLibraries();
 
 }

Modified: velocity/engine/trunk/src/java/org/apache/velocity/context/VMContext.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/context/VMContext.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/context/VMContext.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/context/VMContext.java Wed Aug 22 09:31:31 2007
@@ -20,6 +20,7 @@
  */
 
 import java.util.HashMap;
+import java.util.List;
 
 import org.apache.velocity.runtime.RuntimeServices;
 import org.apache.velocity.runtime.RuntimeConstants;
@@ -326,6 +327,22 @@
     }
 
     /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#setMacroLibraries(List)
+     */
+    public void setMacroLibraries(List macroLibraries)
+    {
+        innerContext.setMacroLibraries(macroLibraries);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getMacroLibraries()
+     */
+    public List getMacroLibraries()
+    {
+        return innerContext.getMacroLibraries();  
+    }
+
+    /**
      * @see org.apache.velocity.context.InternalEventContext#attachEventCartridge(org.apache.velocity.app.event.EventCartridge)
      */
     public EventCartridge attachEventCartridge( EventCartridge ec )
@@ -359,6 +376,7 @@
         return innerContext.getCurrentResource();
     }
 }
+
 
 
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Foreach.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Foreach.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Foreach.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Foreach.java Wed Aug 22 09:31:31 2007
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.io.Writer;
 import java.util.Iterator;
+import java.util.List;
 
 import org.apache.velocity.app.event.EventCartridge;
 import org.apache.velocity.context.Context;
@@ -254,6 +255,22 @@
         public void setAllowRendering(boolean v)
         {
             innerContext.setAllowRendering(v);
+        }
+
+        /**
+         * @see org.apache.velocity.context.InternalContextAdapter#setMacroLibraries(List)
+         */
+        public void setMacroLibraries(List macroLibraries)
+        {
+            innerContext.setMacroLibraries(macroLibraries);
+        }
+
+        /**
+         * @see org.apache.velocity.context.InternalContextAdapter#getMacroLibraries() 
+         */
+        public List getMacroLibraries()
+        {
+            return innerContext.getMacroLibraries();
         }
 
     }

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Parse.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Parse.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Parse.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Parse.java Wed Aug 22 09:31:31 2007
@@ -21,6 +21,8 @@
 
 import java.io.IOException;
 import java.io.Writer;
+import java.util.List;
+import java.util.ArrayList;
 
 import org.apache.velocity.Template;
 import org.apache.velocity.app.event.EventHandlerUtil;
@@ -214,6 +216,23 @@
             return false;
         }
 
+        /**
+         * Add the template name to the macro libraries list
+         */
+        List macroLibraries = context.getMacroLibraries();
+
+        /**
+         * if macroLibraries are not set create a new one
+         */
+        if (macroLibraries == null)
+        {
+            macroLibraries = new ArrayList();
+        }
+
+        context.setMacroLibraries(macroLibraries);
+
+        macroLibraries.add(arg);
+
         /*
          *  and render it
          */
@@ -224,7 +243,7 @@
                 ((SimpleNode) t.getData()).render( context, writer );
             }
         }
-        
+
         /*
          *  if it's a MIE, it came from the render.... throw it...
          */

Added: 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=568691&view=auto
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java (added)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java Wed Aug 22 09:31:31 2007
@@ -0,0 +1,236 @@
+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 org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.parser.node.Node;
+import org.apache.velocity.runtime.parser.Token;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.TemplateInitException;
+import org.apache.velocity.util.introspection.Info;
+
+import java.io.Writer;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * This class acts as a proxy for potential macros.  When the AST is built
+ * this class is inserted as a placeholder for the macro (whether or not
+ * the macro is actually defined).  At render time we check whether there is
+ * a implementation for the macro call. Ifn a implementation cannot be
+ * found the literal text is rendered.
+ */
+
+public class RuntimeMacro extends Directive
+{
+    /**
+     * Name of the macro
+     */
+    private String macroName = "";
+
+    /**
+     * source template name
+     */
+    private String sourceTemplate = "";
+
+    /**
+     * Internal context adapter of macro caller.
+     */
+    private InternalContextAdapter context = null;
+
+    /**
+     * Literal text of the macro
+     */
+    private String literal = "";
+
+    /**
+     * Node of the macro call
+     */
+    private Node node = null;
+
+    /**
+     * Create a RuntimeMacro instance. Macro name and source
+     * template stored for later use.
+     *
+     * @param macroName name of the macro
+     * @param sourceTemplate template where macro call is made
+     */
+    public RuntimeMacro(String macroName, String sourceTemplate)
+    {
+        if (macroName == null || sourceTemplate == null)
+        {
+            throw new IllegalArgumentException("Null arguments");
+        }
+        
+        this.macroName = macroName;
+        this.sourceTemplate = sourceTemplate;
+    }
+
+    /**
+     * Return name of this Velocimacro.
+     *
+     * @return The name of this Velocimacro.
+     */
+    public String getName()
+    {
+        return macroName;
+    }
+
+    /**
+     * Velocimacros are always LINE
+     * type directives.
+     *
+     * @return The type of this directive.
+     */
+    public int getType()
+    {
+        return LINE;
+    }
+
+
+    /**
+     * Intialize the Runtime macro. At the init time no implementation so we
+     * just save the values to use at the rende time.
+     *
+     * @param rs runtime services
+     * @param context InternalContexAdapter
+     * @param node node conating the macro call
+     */
+    public void init(RuntimeServices rs, InternalContextAdapter context,
+                     Node node)
+    {
+        super.init(rs, context, node);
+        rsvc = rs;
+        this.context = context;
+        this.node = node;
+
+        StringBuffer buffer = new StringBuffer();
+        Token t = node.getFirstToken();
+
+        /**
+         * Retrieve the literal text
+         */
+        while (t != null && t != node.getLastToken())
+        {
+            buffer.append(t.image);
+            t = t.next;
+        }
+
+        if (t != null)
+        {
+            buffer.append(t.image);
+        }
+        /**
+         * Store the literal text
+         */
+        literal = buffer.toString();
+    }
+
+    /**
+     * Velocimacro implementation is not known at the init time. So look for
+     * a implementation in the macro libaries and if finds one renders it. The
+     * actual rendering is delegated to the VelocimacroProxy object. When
+     * looking for a macro we first loot at the template with has the
+     * macro call then we look at the macro lbraries in the order they appear
+     * in the list. If a macro has many definitions above look up will
+     * determine the precedence.
+     *
+     * @param context
+     * @param writer
+     * @param node
+     * @return true if the rendering is successfull
+     * @throws IOException
+     * @throws ResourceNotFoundException
+     * @throws ParseErrorException
+     * @throws MethodInvocationException
+     */
+    public boolean render(InternalContextAdapter context, Writer writer,
+                          Node node)
+            throws IOException, ResourceNotFoundException,
+            ParseErrorException, MethodInvocationException
+    {
+        VelocimacroProxy vmProxy = null;
+        
+        /**
+         * first look in the source template
+         */
+        Object o = rsvc.getVelocimacro(macroName,
+                sourceTemplate);
+        if (o != null && o instanceof VelocimacroProxy)
+        {
+            vmProxy = (VelocimacroProxy)o;
+        }
+
+        /**
+         * if not found, look in the macro libraries.
+         */
+        if (vmProxy == null)
+        {
+            List macroLibraries = context.getMacroLibraries();
+            if (macroLibraries != null)
+            {
+                for (int i = 0; i < macroLibraries.size(); i++)
+                {
+                    o = rsvc.getVelocimacro(macroName,
+                            (String)macroLibraries.get(i));
+
+                    /**
+                     * Get the first matching macro
+                     */
+                    if (o != null && o instanceof VelocimacroProxy)
+                    {
+                        vmProxy = (VelocimacroProxy) o;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (vmProxy != null)
+        {
+            /**
+             * initialize the macro if necessary
+             */
+            try
+            {
+                vmProxy.init(rsvc, this.context, this.node);
+            }
+            catch (TemplateInitException die)
+            {
+                Info info = new Info( sourceTemplate, node.getLine(), node.getColumn() );
+
+                throw new ParseErrorException(die.getMessage(), info);
+            }
+            /**
+             * render it
+             */
+            return vmProxy.render(context, writer, node);
+        }
+
+        /**
+         * If we cannot find an implementation write the literal text
+         */
+        writer.write(literal);
+        return true;
+    }
+}

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.java Wed Aug 22 09:31:31 2007
@@ -210,6 +210,51 @@
             return ( strImage );
     }
 
+    /**
+     * Check whether their is a left parenthesis with leading optional
+     * whitespaces. This method is used in the semantic look ahead of
+     * Directive method. It requires lot of changes to implement the
+     * required look ahead as a BNF production and the code will become
+     * inefficient.
+     */
+    private boolean isLeftParantheses()
+    {
+        char c;
+        int no = 0;
+        try {
+            while(true)
+            {
+                /**
+                 * Read a character
+                 */
+                c = velcharstream.readChar();
+                no++;
+                if (c == '(')
+                {
+                    return true;
+                }
+                /**
+                 * if not a white space return
+                 */
+                else if (c != ' ' && c != '\n' && c != '\r' && c != '\t')
+                {
+                    return false;
+                }
+            }
+        }
+        catch (IOException e)
+        {
+        }
+        finally
+        {
+            /**
+             * Backup the stream to the initial state
+             */
+            velcharstream.backup(no);
+        }
+        return false;
+    }
+
 /**
  * This method is what starts the whole parsing
  * process. After the parsing is complete and
@@ -671,14 +716,6 @@
 
             isVM = rsvc.isVelocimacro(directiveName, currentTemplateName);
 
-            if (!isVM)
-            {
-                token_source.stateStackPop();
-                token_source.inDirective = false;
-                {if (true) return jjtn000;}
-            }
-
-
             /*
              *  Currently, all VMs are LINE directives
              */
@@ -697,47 +734,48 @@
         token_source.SwitchTo(DIRECTIVE);
 
         argPos = 0;
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case WHITESPACE:
-        jj_consume_token(WHITESPACE);
-        break;
-      default:
-        jj_la1[8] = jj_gen;
-        ;
-      }
-      jj_consume_token(LPAREN);
-      label_3:
-      while (true) {
-        if (jj_2_4(2)) {
-          ;
-        } else {
-          break label_3;
-        }
+      if (isLeftParantheses()) {
         switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
         case WHITESPACE:
           jj_consume_token(WHITESPACE);
           break;
         default:
-          jj_la1[9] = jj_gen;
+          jj_la1[8] = jj_gen;
           ;
         }
-        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-        case COMMA:
-          jj_consume_token(COMMA);
+        jj_consume_token(LPAREN);
+        label_3:
+        while (true) {
+          if (jj_2_4(2)) {
+            ;
+          } else {
+            break label_3;
+          }
           switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
           case WHITESPACE:
             jj_consume_token(WHITESPACE);
             break;
           default:
-            jj_la1[10] = jj_gen;
+            jj_la1[9] = jj_gen;
             ;
           }
-          break;
-        default:
-          jj_la1[11] = jj_gen;
-          ;
-        }
-        argType = DirectiveArg();
+          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+          case COMMA:
+            jj_consume_token(COMMA);
+            switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+            case WHITESPACE:
+              jj_consume_token(WHITESPACE);
+              break;
+            default:
+              jj_la1[10] = jj_gen;
+              ;
+            }
+            break;
+          default:
+            jj_la1[11] = jj_gen;
+            ;
+          }
+          argType = DirectiveArg();
                 if (argType == ParserTreeConstants.JJTWORD)
                 {
                     if (doItNow && argPos == 0)
@@ -771,20 +809,28 @@
                 }
 
                 argPos++;
-      }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case WHITESPACE:
-        jj_consume_token(WHITESPACE);
-        break;
-      default:
-        jj_la1[12] = jj_gen;
-        ;
-      }
-      jj_consume_token(RPAREN);
+        }
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case WHITESPACE:
+          jj_consume_token(WHITESPACE);
+          break;
+        default:
+          jj_la1[12] = jj_gen;
+          ;
+        }
+        jj_consume_token(RPAREN);
         if (directiveType  == Directive.LINE)
         {
             {if (true) return jjtn000;}
         }
+      } else {
+        /**
+         * Not a directive
+         */
+        token_source.stateStackPop();
+        token_source.inDirective = false;
+        {if (true) return jjtn000;}
+      }
       ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK);
       boolean jjtc001 = true;
       jjtree.openNodeScope(jjtn001);
@@ -2531,349 +2577,321 @@
     finally { jj_save(11, xla); }
   }
 
-  final private boolean jj_3R_82() {
-    if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_25()) return true;
-    return false;
-  }
-
-  final private boolean jj_3_8() {
-    if (jj_3R_29()) return true;
+  final private boolean jj_3R_52() {
+    if (jj_3R_65()) return true;
     return false;
   }
 
-  final private boolean jj_3R_26() {
-    if (jj_3R_20()) return true;
+  final private boolean jj_3R_31() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3_11()) {
+    jj_scanpos = xsp;
+    if (jj_3R_58()) return true;
+    }
     return false;
   }
 
-  final private boolean jj_3R_66() {
-    if (jj_scan_token(FALSE)) return true;
+  final private boolean jj_3_11() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    if (jj_scan_token(LOGICAL_NOT)) return true;
+    if (jj_3R_31()) return true;
     return false;
   }
 
-  final private boolean jj_3R_65() {
-    if (jj_scan_token(TRUE)) return true;
+  final private boolean jj_3R_58() {
+    if (jj_3R_67()) return true;
     return false;
   }
 
-  final private boolean jj_3_9() {
-    if (jj_scan_token(DOT)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3_10()) {
-    jj_scanpos = xsp;
-    if (jj_3R_30()) return true;
-    }
+  final private boolean jj_3R_51() {
+    if (jj_3R_64()) return true;
     return false;
   }
 
-  final private boolean jj_3R_57() {
+  final private boolean jj_3R_85() {
+    if (jj_scan_token(COMMA)) return true;
+    if (jj_3R_25()) return true;
+    if (jj_scan_token(COLON)) return true;
     if (jj_3R_25()) return true;
-    Token xsp;
-    while (true) {
-      xsp = jj_scanpos;
-      if (jj_3R_82()) { jj_scanpos = xsp; break; }
-    }
-    return false;
-  }
-
-  final private boolean jj_3_7() {
-    if (jj_scan_token(DOT)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3_8()) {
-    jj_scanpos = xsp;
-    if (jj_3R_28()) return true;
-    }
     return false;
   }
 
-  final private boolean jj_3R_35() {
-    if (jj_scan_token(LCURLY)) return true;
-    if (jj_scan_token(IDENTIFIER)) return true;
-    Token xsp;
-    while (true) {
-      xsp = jj_scanpos;
-      if (jj_3_9()) { jj_scanpos = xsp; break; }
-    }
-    if (jj_scan_token(RCURLY)) return true;
+  final private boolean jj_3R_50() {
+    if (jj_3R_63()) return true;
     return false;
   }
 
-  final private boolean jj_3_12() {
-    if (jj_scan_token(LBRACKET)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_32()) {
-    jj_scanpos = xsp;
-    if (jj_3R_33()) return true;
-    }
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    if (jj_scan_token(DOUBLEDOT)) return true;
+  final private boolean jj_3R_49() {
+    if (jj_3R_61()) return true;
     return false;
   }
 
-  final private boolean jj_3_2() {
-    if (jj_scan_token(DOUBLE_ESCAPE)) return true;
+  final private boolean jj_3R_48() {
+    if (jj_3R_36()) return true;
     return false;
   }
 
-  final private boolean jj_3R_34() {
-    if (jj_scan_token(IDENTIFIER)) return true;
-    Token xsp;
-    while (true) {
-      xsp = jj_scanpos;
-      if (jj_3_7()) { jj_scanpos = xsp; break; }
-    }
+  final private boolean jj_3R_47() {
+    if (jj_3R_60()) return true;
     return false;
   }
 
-  final private boolean jj_3R_81() {
-    if (jj_scan_token(LPAREN)) return true;
+  final private boolean jj_3R_84() {
+    if (jj_3R_36()) return true;
     return false;
   }
 
-  final private boolean jj_3R_80() {
-    if (jj_3R_66()) return true;
+  final private boolean jj_3R_69() {
+    if (jj_3R_36()) return true;
     return false;
   }
 
-  final private boolean jj_3R_79() {
-    if (jj_3R_65()) return true;
+  final private boolean jj_3R_86() {
+    if (jj_scan_token(COMMA)) return true;
+    if (jj_3R_25()) return true;
     return false;
   }
 
-  final private boolean jj_3R_20() {
+  final private boolean jj_3R_25() {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_34()) {
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_47()) {
     jj_scanpos = xsp;
-    if (jj_3R_35()) return true;
+    if (jj_3R_48()) {
+    jj_scanpos = xsp;
+    if (jj_3R_49()) {
+    jj_scanpos = xsp;
+    if (jj_3R_50()) {
+    jj_scanpos = xsp;
+    if (jj_3R_51()) {
+    jj_scanpos = xsp;
+    if (jj_3R_52()) {
+    jj_scanpos = xsp;
+    if (jj_3R_53()) {
+    jj_scanpos = xsp;
+    if (jj_3R_54()) {
+    jj_scanpos = xsp;
+    if (jj_3R_55()) return true;
+    }
+    }
+    }
+    }
+    }
+    }
     }
+    }
+    xsp = jj_scanpos;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
     return false;
   }
 
-  final private boolean jj_3R_78() {
-    if (jj_3R_64()) return true;
+  final private boolean jj_3R_22() {
+    if (jj_3R_36()) return true;
     return false;
   }
 
-  final private boolean jj_3R_77() {
-    if (jj_3R_63()) return true;
+  final private boolean jj_3R_83() {
+    if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_76() {
-    if (jj_3R_62()) return true;
+  final private boolean jj_3R_68() {
+    if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_75() {
-    if (jj_3R_61()) return true;
+  final private boolean jj_3R_71() {
+    if (jj_3R_25()) return true;
+    Token xsp;
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3R_86()) { jj_scanpos = xsp; break; }
+    }
     return false;
   }
 
-  final private boolean jj_3R_74() {
-    if (jj_3R_36()) return true;
+  final private boolean jj_3_1() {
+    if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_73() {
+  final private boolean jj_3R_21() {
     if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3_6() {
+  final private boolean jj_3R_61() {
     if (jj_scan_token(LBRACKET)) return true;
     Token xsp;
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
     xsp = jj_scanpos;
-    if (jj_3R_26()) {
+    if (jj_3R_68()) {
     jj_scanpos = xsp;
-    if (jj_3R_27()) return true;
+    if (jj_3R_69()) return true;
     }
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
     if (jj_scan_token(DOUBLEDOT)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_29() {
-    if (jj_3R_56()) return true;
-    if (jj_scan_token(LPAREN)) return true;
-    Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_57()) jj_scanpos = xsp;
-    if (jj_scan_token(REFMOD2_RPAREN)) return true;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_83()) {
+    jj_scanpos = xsp;
+    if (jj_3R_84()) return true;
+    }
+    xsp = jj_scanpos;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  final private boolean jj_3R_72() {
-    if (jj_3R_60()) return true;
+  final private boolean jj_3R_64() {
+    if (jj_scan_token(LBRACKET)) return true;
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_71()) jj_scanpos = xsp;
+    if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  final private boolean jj_3R_67() {
+  final private boolean jj_3R_70() {
     Token xsp;
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_72()) {
-    jj_scanpos = xsp;
-    if (jj_3R_73()) {
-    jj_scanpos = xsp;
-    if (jj_3R_74()) {
-    jj_scanpos = xsp;
-    if (jj_3R_75()) {
-    jj_scanpos = xsp;
-    if (jj_3R_76()) {
-    jj_scanpos = xsp;
-    if (jj_3R_77()) {
-    jj_scanpos = xsp;
-    if (jj_3R_78()) {
-    jj_scanpos = xsp;
-    if (jj_3R_79()) {
-    jj_scanpos = xsp;
-    if (jj_3R_80()) {
-    jj_scanpos = xsp;
-    if (jj_3R_81()) return true;
-    }
-    }
-    }
-    }
-    }
-    }
-    }
-    }
-    }
     return false;
   }
 
-  final private boolean jj_3R_55() {
-    if (jj_3R_62()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_54() {
-    if (jj_3R_20()) return true;
+  final private boolean jj_3R_46() {
+    if (jj_3R_66()) return true;
     return false;
   }
 
-  final private boolean jj_3R_53() {
-    if (jj_3R_66()) return true;
+  final private boolean jj_3_5() {
+    if (jj_3R_25()) return true;
+    if (jj_scan_token(COLON)) return true;
+    if (jj_3R_25()) return true;
+    Token xsp;
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3R_85()) { jj_scanpos = xsp; break; }
+    }
     return false;
   }
 
-  final private boolean jj_3R_52() {
+  final private boolean jj_3R_45() {
     if (jj_3R_65()) return true;
     return false;
   }
 
-  final private boolean jj_3R_31() {
+  final private boolean jj_3R_63() {
+    if (jj_scan_token(LEFT_CURLEY)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3_11()) {
+    if (jj_3_5()) {
     jj_scanpos = xsp;
-    if (jj_3R_58()) return true;
+    if (jj_3R_70()) return true;
+    }
+    xsp = jj_scanpos;
+    if (jj_scan_token(7)) {
+    jj_scanpos = xsp;
+    if (jj_scan_token(65)) return true;
     }
     return false;
   }
 
-  final private boolean jj_3_11() {
+  final private boolean jj_3_3() {
+    if (jj_scan_token(LBRACKET)) return true;
     Token xsp;
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
-    if (jj_scan_token(LOGICAL_NOT)) return true;
-    if (jj_3R_31()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_58() {
-    if (jj_3R_67()) return true;
+    xsp = jj_scanpos;
+    if (jj_3R_21()) {
+    jj_scanpos = xsp;
+    if (jj_3R_22()) return true;
+    }
+    xsp = jj_scanpos;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    if (jj_scan_token(DOUBLEDOT)) return true;
     return false;
   }
 
-  final private boolean jj_3R_51() {
+  final private boolean jj_3R_44() {
     if (jj_3R_64()) return true;
     return false;
   }
 
-  final private boolean jj_3R_85() {
-    if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_25()) return true;
-    if (jj_scan_token(COLON)) return true;
-    if (jj_3R_25()) return true;
+  final private boolean jj_3R_43() {
+    if (jj_3R_63()) return true;
     return false;
   }
 
-  final private boolean jj_3R_50() {
-    if (jj_3R_63()) return true;
+  final private boolean jj_3R_42() {
+    if (jj_3R_62()) return true;
     return false;
   }
 
-  final private boolean jj_3R_49() {
+  final private boolean jj_3R_41() {
     if (jj_3R_61()) return true;
     return false;
   }
 
-  final private boolean jj_3R_48() {
+  final private boolean jj_3R_40() {
     if (jj_3R_36()) return true;
     return false;
   }
 
-  final private boolean jj_3R_47() {
+  final private boolean jj_3R_39() {
     if (jj_3R_60()) return true;
     return false;
   }
 
-  final private boolean jj_3R_84() {
-    if (jj_3R_36()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_22() {
-    if (jj_3R_36()) return true;
+  final private boolean jj_3R_38() {
+    if (jj_3R_59()) return true;
     return false;
   }
 
-  final private boolean jj_3R_69() {
-    if (jj_3R_36()) return true;
+  final private boolean jj_3R_23() {
+    if (jj_scan_token(COMMA)) return true;
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
     return false;
   }
 
-  final private boolean jj_3R_86() {
-    if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_25()) return true;
+  final private boolean jj_3R_37() {
+    if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_25() {
+  final private boolean jj_3R_24() {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_47()) {
+    if (jj_3R_37()) {
     jj_scanpos = xsp;
-    if (jj_3R_48()) {
+    if (jj_3R_38()) {
     jj_scanpos = xsp;
-    if (jj_3R_49()) {
+    if (jj_3R_39()) {
     jj_scanpos = xsp;
-    if (jj_3R_50()) {
+    if (jj_3R_40()) {
     jj_scanpos = xsp;
-    if (jj_3R_51()) {
+    if (jj_3R_41()) {
     jj_scanpos = xsp;
-    if (jj_3R_52()) {
+    if (jj_3R_42()) {
     jj_scanpos = xsp;
-    if (jj_3R_53()) {
+    if (jj_3R_43()) {
     jj_scanpos = xsp;
-    if (jj_3R_54()) {
+    if (jj_3R_44()) {
     jj_scanpos = xsp;
-    if (jj_3R_55()) return true;
+    if (jj_3R_45()) {
+    jj_scanpos = xsp;
+    if (jj_3R_46()) return true;
+    }
     }
     }
     }
@@ -2882,130 +2900,153 @@
     }
     }
     }
+    return false;
+  }
+
+  final private boolean jj_3R_59() {
+    if (jj_scan_token(WORD)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_56() {
+    if (jj_scan_token(IDENTIFIER)) return true;
+    return false;
+  }
+
+  final private boolean jj_3_4() {
+    Token xsp;
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_23()) jj_scanpos = xsp;
+    if (jj_3R_24()) return true;
     return false;
   }
 
-  final private boolean jj_3R_83() {
-    if (jj_3R_20()) return true;
+  final private boolean jj_3R_30() {
+    if (jj_3R_56()) return true;
     return false;
   }
 
-  final private boolean jj_3_1() {
-    if (jj_3R_20()) return true;
+  final private boolean jj_3R_60() {
+    if (jj_scan_token(STRING_LITERAL)) return true;
     return false;
   }
 
-  final private boolean jj_3R_21() {
-    if (jj_3R_20()) return true;
+  final private boolean jj_3R_36() {
+    if (jj_scan_token(INTEGER_LITERAL)) return true;
     return false;
   }
 
-  final private boolean jj_3R_68() {
+  final private boolean jj_3R_28() {
+    if (jj_3R_56()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_33() {
+    if (jj_3R_36()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_62() {
+    if (jj_scan_token(FLOATING_POINT_LITERAL)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_32() {
     if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_71() {
+  final private boolean jj_3R_27() {
+    if (jj_3R_36()) return true;
+    return false;
+  }
+
+  final private boolean jj_3_10() {
+    if (jj_3R_29()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_82() {
+    if (jj_scan_token(COMMA)) return true;
     if (jj_3R_25()) return true;
-    Token xsp;
-    while (true) {
-      xsp = jj_scanpos;
-      if (jj_3R_86()) { jj_scanpos = xsp; break; }
-    }
     return false;
   }
 
-  final private boolean jj_3R_61() {
-    if (jj_scan_token(LBRACKET)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_68()) {
-    jj_scanpos = xsp;
-    if (jj_3R_69()) return true;
-    }
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    if (jj_scan_token(DOUBLEDOT)) return true;
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_83()) {
-    jj_scanpos = xsp;
-    if (jj_3R_84()) return true;
-    }
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    if (jj_scan_token(RBRACKET)) return true;
+  final private boolean jj_3_8() {
+    if (jj_3R_29()) return true;
     return false;
   }
 
-  final private boolean jj_3R_64() {
-    if (jj_scan_token(LBRACKET)) return true;
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_71()) jj_scanpos = xsp;
-    if (jj_scan_token(RBRACKET)) return true;
+  final private boolean jj_3R_26() {
+    if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_46() {
-    if (jj_3R_66()) return true;
+  final private boolean jj_3R_66() {
+    if (jj_scan_token(FALSE)) return true;
     return false;
   }
 
-  final private boolean jj_3R_70() {
+  final private boolean jj_3R_65() {
+    if (jj_scan_token(TRUE)) return true;
+    return false;
+  }
+
+  final private boolean jj_3_9() {
+    if (jj_scan_token(DOT)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
+    if (jj_3_10()) {
+    jj_scanpos = xsp;
+    if (jj_3R_30()) return true;
+    }
     return false;
   }
 
-  final private boolean jj_3_5() {
-    if (jj_3R_25()) return true;
-    if (jj_scan_token(COLON)) return true;
+  final private boolean jj_3R_57() {
     if (jj_3R_25()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_85()) { jj_scanpos = xsp; break; }
+      if (jj_3R_82()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  final private boolean jj_3R_45() {
-    if (jj_3R_65()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_63() {
-    if (jj_scan_token(LEFT_CURLEY)) return true;
+  final private boolean jj_3_7() {
+    if (jj_scan_token(DOT)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3_5()) {
+    if (jj_3_8()) {
     jj_scanpos = xsp;
-    if (jj_3R_70()) return true;
+    if (jj_3R_28()) return true;
     }
-    xsp = jj_scanpos;
-    if (jj_scan_token(7)) {
-    jj_scanpos = xsp;
-    if (jj_scan_token(65)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_35() {
+    if (jj_scan_token(LCURLY)) return true;
+    if (jj_scan_token(IDENTIFIER)) return true;
+    Token xsp;
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3_9()) { jj_scanpos = xsp; break; }
     }
+    if (jj_scan_token(RCURLY)) return true;
     return false;
   }
 
-  final private boolean jj_3_3() {
+  final private boolean jj_3_12() {
     if (jj_scan_token(LBRACKET)) return true;
     Token xsp;
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
     xsp = jj_scanpos;
-    if (jj_3R_21()) {
+    if (jj_3R_32()) {
     jj_scanpos = xsp;
-    if (jj_3R_22()) return true;
+    if (jj_3R_33()) return true;
     }
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
@@ -3013,76 +3054,131 @@
     return false;
   }
 
-  final private boolean jj_3R_44() {
+  final private boolean jj_3_2() {
+    if (jj_scan_token(DOUBLE_ESCAPE)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_34() {
+    if (jj_scan_token(IDENTIFIER)) return true;
+    Token xsp;
+    while (true) {
+      xsp = jj_scanpos;
+      if (jj_3_7()) { jj_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  final private boolean jj_3R_81() {
+    if (jj_scan_token(LPAREN)) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_80() {
+    if (jj_3R_66()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_79() {
+    if (jj_3R_65()) return true;
+    return false;
+  }
+
+  final private boolean jj_3R_20() {
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_34()) {
+    jj_scanpos = xsp;
+    if (jj_3R_35()) return true;
+    }
+    return false;
+  }
+
+  final private boolean jj_3R_78() {
     if (jj_3R_64()) return true;
     return false;
   }
 
-  final private boolean jj_3R_43() {
+  final private boolean jj_3R_77() {
     if (jj_3R_63()) return true;
     return false;
   }
 
-  final private boolean jj_3R_42() {
+  final private boolean jj_3R_76() {
     if (jj_3R_62()) return true;
     return false;
   }
 
-  final private boolean jj_3R_41() {
+  final private boolean jj_3R_75() {
     if (jj_3R_61()) return true;
     return false;
   }
 
-  final private boolean jj_3R_40() {
+  final private boolean jj_3R_74() {
     if (jj_3R_36()) return true;
     return false;
   }
 
-  final private boolean jj_3R_39() {
-    if (jj_3R_60()) return true;
+  final private boolean jj_3R_73() {
+    if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_23() {
-    if (jj_scan_token(COMMA)) return true;
+  final private boolean jj_3_6() {
+    if (jj_scan_token(LBRACKET)) return true;
     Token xsp;
     xsp = jj_scanpos;
     if (jj_scan_token(26)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_26()) {
+    jj_scanpos = xsp;
+    if (jj_3R_27()) return true;
+    }
+    xsp = jj_scanpos;
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    if (jj_scan_token(DOUBLEDOT)) return true;
     return false;
   }
 
-  final private boolean jj_3R_38() {
-    if (jj_3R_59()) return true;
+  final private boolean jj_3R_29() {
+    if (jj_3R_56()) return true;
+    if (jj_scan_token(LPAREN)) return true;
+    Token xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_57()) jj_scanpos = xsp;
+    if (jj_scan_token(REFMOD2_RPAREN)) return true;
     return false;
   }
 
-  final private boolean jj_3R_37() {
-    if (jj_3R_20()) return true;
+  final private boolean jj_3R_72() {
+    if (jj_3R_60()) return true;
     return false;
   }
 
-  final private boolean jj_3R_24() {
+  final private boolean jj_3R_67() {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_37()) {
+    if (jj_scan_token(26)) jj_scanpos = xsp;
+    xsp = jj_scanpos;
+    if (jj_3R_72()) {
     jj_scanpos = xsp;
-    if (jj_3R_38()) {
+    if (jj_3R_73()) {
     jj_scanpos = xsp;
-    if (jj_3R_39()) {
+    if (jj_3R_74()) {
     jj_scanpos = xsp;
-    if (jj_3R_40()) {
+    if (jj_3R_75()) {
     jj_scanpos = xsp;
-    if (jj_3R_41()) {
+    if (jj_3R_76()) {
     jj_scanpos = xsp;
-    if (jj_3R_42()) {
+    if (jj_3R_77()) {
     jj_scanpos = xsp;
-    if (jj_3R_43()) {
+    if (jj_3R_78()) {
     jj_scanpos = xsp;
-    if (jj_3R_44()) {
+    if (jj_3R_79()) {
     jj_scanpos = xsp;
-    if (jj_3R_45()) {
+    if (jj_3R_80()) {
     jj_scanpos = xsp;
-    if (jj_3R_46()) return true;
+    if (jj_3R_81()) return true;
     }
     }
     }
@@ -3095,68 +3191,18 @@
     return false;
   }
 
-  final private boolean jj_3R_59() {
-    if (jj_scan_token(WORD)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_56() {
-    if (jj_scan_token(IDENTIFIER)) return true;
-    return false;
-  }
-
-  final private boolean jj_3_4() {
-    Token xsp;
-    xsp = jj_scanpos;
-    if (jj_scan_token(26)) jj_scanpos = xsp;
-    xsp = jj_scanpos;
-    if (jj_3R_23()) jj_scanpos = xsp;
-    if (jj_3R_24()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_60() {
-    if (jj_scan_token(STRING_LITERAL)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_30() {
-    if (jj_3R_56()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_36() {
-    if (jj_scan_token(INTEGER_LITERAL)) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_28() {
-    if (jj_3R_56()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_33() {
-    if (jj_3R_36()) return true;
-    return false;
-  }
-
-  final private boolean jj_3R_62() {
-    if (jj_scan_token(FLOATING_POINT_LITERAL)) return true;
+  final private boolean jj_3R_55() {
+    if (jj_3R_62()) return true;
     return false;
   }
 
-  final private boolean jj_3R_32() {
+  final private boolean jj_3R_54() {
     if (jj_3R_20()) return true;
     return false;
   }
 
-  final private boolean jj_3R_27() {
-    if (jj_3R_36()) return true;
-    return false;
-  }
-
-  final private boolean jj_3_10() {
-    if (jj_3R_29()) return true;
+  final private boolean jj_3R_53() {
+    if (jj_3R_66()) return true;
     return false;
   }
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.jj
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.jj?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.jj (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/Parser.jj Wed Aug 22 09:31:31 2007
@@ -265,6 +265,51 @@
         else
             return ( strImage );
     }
+
+    /**
+     * Check weather their is a left parenthesis with leading optional
+     * whitespaces. This method is used in the semantic look ahead of
+     * Directive method. It requires lot of changes to implement the
+     * required look ahead as a BNF production and the code will become
+     * inefficient.
+     */
+    private boolean isLeftParantheses()
+    {
+        char c;
+        int no = 0;
+        try {
+            while(true)
+            {
+                /**
+                 * Read a character
+                 */
+                c = velcharstream.readChar();
+                no++;
+                if (c == '(')
+                {
+                    return true;
+                }
+                /**
+                 * if not a white space return
+                 */
+                else if (c != ' ' && c != '\n' && c != '\r' && c != '\t')
+                {
+                    return false;
+                }
+            }
+        }
+        catch (IOException e)
+        {
+        }
+        finally
+        {
+            /**
+             * Backup the stream to the initial state
+             */
+            velcharstream.backup(no);
+        }
+        return false;
+    }
 }
 
 PARSER_END(Parser)
@@ -1449,14 +1494,6 @@
 
             isVM = rsvc.isVelocimacro(directiveName, currentTemplateName);
 
-            if (!isVM)
-            {
-                token_source.stateStackPop();
-                token_source.inDirective = false;
-                return jjtn000;
-            }
-
-
             /*
              *  Currently, all VMs are LINE directives
              */
@@ -1478,12 +1515,14 @@
     }
 
 
-
+     /**
+      * Look for the patter [WHITESPACE] <LPAREN>
+      */
+    (LOOKAHEAD( { isLeftParantheses() } )
     /*
      *  if this is indeed a token, match the #foo ( arg ) pattern
      */
-
-    [<WHITESPACE>] <LPAREN>  ( LOOKAHEAD(2) [<WHITESPACE>] [<COMMA> [<WHITESPACE>]]
+    (([<WHITESPACE>] <LPAREN>) ( LOOKAHEAD(2) [<WHITESPACE>] [<COMMA> [<WHITESPACE>]]
 
             argType = DirectiveArg()
             {
@@ -1522,14 +1561,24 @@
                 argPos++;
             }
 
-    )* [<WHITESPACE>] <RPAREN>
+    )* [<WHITESPACE>] <RPAREN>)
     {
 
         if (directiveType  == Directive.LINE)
         {
             return jjtn000;
         }
-    }/*@bgen(jjtree) Block */
+    }
+    |
+    {
+        /**
+         * Not a directive
+         */
+        token_source.stateStackPop();
+        token_source.inDirective = false;
+        return jjtn000;
+    }
+    )/*@bgen(jjtree) Block */
     {
       ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK);
       boolean jjtc001 = true;
@@ -1537,7 +1586,6 @@
     }
     try {
 /*@egen*/
-
     /*
      *  and the following block if the PD needs it
      */

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTDirective.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTDirective.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTDirective.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTDirective.java Wed Aug 22 09:31:31 2007
@@ -29,6 +29,7 @@
 import org.apache.velocity.exception.ResourceNotFoundException;
 import org.apache.velocity.exception.TemplateInitException;
 import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.directive.RuntimeMacro;
 import org.apache.velocity.runtime.parser.ParseException;
 import org.apache.velocity.runtime.parser.Parser;
 import org.apache.velocity.util.ExceptionUtils;
@@ -119,20 +120,22 @@
 
             directive.setLocation( getLine(), getColumn() );
         }
-        else if (rsvc.isVelocimacro( directiveName, context.getCurrentTemplateName()  ))
+        else
         {
-            /*
-             *  we seem to be a Velocimacro.
+            /**
+             * Create a new RuntimeMacro
              */
+            directive = new RuntimeMacro(directiveName,
+                    context.getCurrentTemplateName());
 
-            isDirective = true;
-            directive = rsvc.getVelocimacro( directiveName,  context.getCurrentTemplateName());
-
-            try 
+            /**
+             * Initialize it
+             */
+            try
             {
                 directive.init( rsvc, context, this );
             }
-            
+
             /**
              * correct the line/column number if an exception is caught
              */
@@ -145,12 +148,9 @@
                         die.getLineNumber() + getLine());
             }
             directive.setLocation( getLine(), getColumn() );
-        }
-        else
-        {
-            isDirective = false;
-        }
 
+            isDirective = true;
+        }
         return data;
     }
 
@@ -208,5 +208,6 @@
     }
 
 }
+
 
 

Modified: velocity/engine/trunk/src/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/parser/Parser.jjt?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/parser/Parser.jjt (original)
+++ velocity/engine/trunk/src/parser/Parser.jjt Wed Aug 22 09:31:31 2007
@@ -286,6 +286,50 @@
         else
             return ( strImage );
     }
+
+    /**
+     * Check whether there is a left parenthesis with leading optional
+     * whitespaces. This method is used in the semantic look ahead of
+     * Directive method. This is done in code instead of as a production
+     * for simplicity and efficiency.
+     */
+    private boolean isLeftParantheses()
+    {
+        char c;
+        int no = 0;
+        try {
+            while(true)
+            {
+                /**
+                 * Read a character
+                 */
+                c = velcharstream.readChar();
+                no++;
+                if (c == '(')
+                {
+                    return true;
+                }
+                /**
+                 * if not a white space return
+                 */
+                else if (c != ' ' && c != '\n' && c != '\r' && c != '\t')
+                {
+                    return false;
+                }
+            }
+        }
+        catch (IOException e)
+        {
+        }
+        finally
+        {
+            /**
+             * Backup the stream to the initial state
+             */
+            velcharstream.backup(no);
+        }
+        return false;
+    }
 }
 
 PARSER_END(Parser)
@@ -1327,14 +1371,6 @@
 
             isVM = rsvc.isVelocimacro(directiveName, currentTemplateName);
 
-            if (!isVM)
-            {
-                token_source.stateStackPop();
-                token_source.inDirective = false;
-                return jjtThis;
-            }
-
-
             /*
              *  Currently, all VMs are LINE directives
              */
@@ -1356,12 +1392,14 @@
     }
 
 
-
+     /**
+      * Look for the patter [WHITESPACE] <LPAREN>
+      */
+    (LOOKAHEAD( { isLeftParantheses() } )
     /*
      *  if this is indeed a token, match the #foo ( arg ) pattern
      */
-
-    [<WHITESPACE>] <LPAREN>  ( LOOKAHEAD(2) [<WHITESPACE>] [<COMMA> [<WHITESPACE>]]
+    (([<WHITESPACE>] <LPAREN>) ( LOOKAHEAD(2) [<WHITESPACE>] [<COMMA> [<WHITESPACE>]]
 
             argType = DirectiveArg()
             {
@@ -1400,7 +1438,7 @@
                 argPos++;
             }
 
-    )* [<WHITESPACE>] <RPAREN>
+    )* [<WHITESPACE>] <RPAREN>)
     {
 
         if (directiveType  == Directive.LINE)
@@ -1408,7 +1446,16 @@
             return jjtThis;
         }
     }
-
+    |
+    {
+        /**
+         * Not a directive
+         */
+        token_source.stateStackPop();
+        token_source.inDirective = false;
+        return jjtThis;
+    }
+    )
     /*
      *  and the following block if the PD needs it
      */

Modified: velocity/engine/trunk/src/test/org/apache/velocity/test/MacroForwardDefineTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/MacroForwardDefineTestCase.java?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/test/MacroForwardDefineTestCase.java (original)
+++ velocity/engine/trunk/src/test/org/apache/velocity/test/MacroForwardDefineTestCase.java Wed Aug 22 09:31:31 2007
@@ -25,6 +25,10 @@
 import org.apache.velocity.app.Velocity;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.test.misc.TestLogChute;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+
+import java.io.*;
 
 /**
  * Make sure that a forward referenced macro inside another macro definition does
@@ -57,7 +61,7 @@
      * Collects the log messages.
      */
     private TestLogChute logger = new TestLogChute();
-    
+
     /**
      * Default constructor.
      */
@@ -70,18 +74,16 @@
         throws Exception
     {
         assureResultsDirectoryExists(RESULTS_DIR);
-                
+
         // use Velocity.setProperty (instead of properties file) so that we can use actual instance of log
         Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER,"file");
         Velocity.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH );
         Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID,"true");
-        Velocity.setProperty(RuntimeConstants.VM_LIBRARY, "macros.vm");
 
         // actual instance of logger
         logger = new TestLogChute();
         Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM,logger);
         Velocity.setProperty("runtime.log.logsystem.test.level", "error");
-
         Velocity.init();
     }
 
@@ -93,6 +95,11 @@
     public void testLogResult()
         throws Exception
     {
+        VelocityContext context = new VelocityContext();
+        Template template = Velocity.getTemplate("macros.vm");
+
+        template.merge(context, new StringWriter());
+
         if ( !isMatch(logger.getLog(), COMPARE_DIR, "velocity.log", "cmp"))
         {
             fail("Output incorrect.");

Added: velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java?rev=568691&view=auto
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java (added)
+++ velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java Wed Aug 22 09:31:31 2007
@@ -0,0 +1,244 @@
+package org.apache.velocity.test;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.log.NullLogChute;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.Template;
+import junit.framework.TestSuite;
+
+import java.io.*;
+
+/*
+ * 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.
+ */
+
+/**
+ * Test case for including macro libraries via the #parse method.
+ */
+public class ParseWithMacroLibsTestCase extends BaseTestCase
+{
+    private static final String RESULT_DIR = TEST_RESULT_DIR + "/parsemacros";
+
+    private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/parsemacros/compare";
+
+    public ParseWithMacroLibsTestCase(String name)
+    {
+        super(name);
+    }
+
+    public void setUp()
+            throws Exception
+    {
+        super.setUp();
+    }
+
+    /**
+     * Test suite
+     * @return test suite
+     */
+    public static junit.framework.Test suite()
+    {
+        return new TestSuite(ParseWithMacroLibsTestCase.class);
+    }
+    
+    public void testParseMacroLocalCacheOn()
+    throws Exception
+    {
+        /*
+         *  local scope, cache on
+         */
+        VelocityEngine ve = createEngine(true, true);
+
+        // render twice to make sure there is no difference with cached templates
+        testParseMacro(ve, "vm_library1.vm", "parseMacro1_1", false);
+        testParseMacro(ve, "vm_library1.vm", "parseMacro1_1", false);
+        
+        // run again with different macro library
+        testParseMacro(ve, "vm_library2.vm", "parseMacro1_1b", false);
+        testParseMacro(ve, "vm_library2.vm", "parseMacro1_1b", false);
+    }
+
+    /**
+     * Runs the tests with global namespace.
+     */
+    public void testParseMacroLocalCacheOff()
+    throws Exception
+    {
+        /*
+         *  local scope, cache off
+         */
+        VelocityEngine ve = createEngine(false, true);
+
+        testParseMacro(ve, "vm_library1.vm", "parseMacro1_2", true);
+
+        // run again with different macro library
+        testParseMacro(ve, "vm_library2.vm", "parseMacro1_2b", true);
+    }
+
+    public void testParseMacroGlobalCacheOn()
+    throws Exception
+    {
+        /*
+         *  global scope, cache on
+         */
+        VelocityEngine ve = createEngine(true, false);
+
+        // render twice to make sure there is no difference with cached templates
+        testParseMacro(ve, "vm_library1.vm", "parseMacro1_3", false);
+        testParseMacro(ve, "vm_library1.vm", "parseMacro1_3", false);
+
+        // run again with different macro library
+        testParseMacro(ve, "vm_library2.vm", "parseMacro1_3b", false);
+        testParseMacro(ve, "vm_library2.vm", "parseMacro1_3b", false);
+    }
+    
+    public void testParseMacroGlobalCacheOff()
+    throws Exception
+    {
+        /*
+         *  global scope, cache off
+         */
+        VelocityEngine ve = createEngine(false, false);
+
+        testParseMacro(ve, "vm_library1.vm", "parseMacro1_4", true);
+        
+        // run again with different macro library
+        testParseMacro(ve, "vm_library2.vm", "parseMacro1_4b", true);
+
+    }
+    
+    /**
+     * Test #parse with macros.  Can be used to test different engine configurations
+     * @param ve
+     * @param outputBaseFileName
+     * @param testCachingOff
+     * @throws Exception
+     */
+    private void testParseMacro(VelocityEngine ve, String includeFile, String outputBaseFileName, boolean testCachingOff)
+            throws Exception
+    {
+        assureResultsDirectoryExists(RESULT_DIR);
+
+        FileOutputStream fos = new FileOutputStream (getFileName(
+                RESULT_DIR, outputBaseFileName, RESULT_FILE_EXT));
+
+        VelocityContext context = new VelocityContext();
+        context.put("includefile", includeFile);
+
+        Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+        Template template = ve.getTemplate("parseMacro1.vm");
+        template.merge(context, writer);
+
+        /**
+         * Write to the file
+         */
+        writer.flush();
+        writer.close();
+
+        if (!isMatch(RESULT_DIR, COMPARE_DIR, outputBaseFileName,
+                RESULT_FILE_EXT,CMP_FILE_EXT))
+        {
+            fail("Processed template did not match expected output");
+        }
+
+        /*
+         * Show that caching is turned off
+         */
+        if (testCachingOff)
+        {
+            Template t1 = ve.getTemplate("parseMacro1.vm");
+            Template t2 = ve.getTemplate("parseMacro1.vm");
+
+            assertNotSame("Different objects", t1, t2);
+        }
+    }
+
+    /**
+     * Return and initialize engine
+     * @return
+     */
+    private VelocityEngine createEngine(boolean cache, boolean local)
+    throws Exception
+    {
+        VelocityEngine ve = new VelocityEngine();
+        ve.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+        ve.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+                new Boolean(local));
+        ve.setProperty("file.resource.loader.cache", new Boolean(cache));
+        ve.setProperty(
+                Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, NullLogChute.class.getName());
+        ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
+        ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+                TEST_COMPARE_DIR + "/parsemacros");
+        ve.init();
+        
+        return ve;
+    }
+    /**
+     * Test whether the literal text is given if a definition cannot be
+     * found for a macro.
+     *
+     * @throws Exception
+     */
+    public void testParseMacrosWithNoDefinition()
+            throws Exception
+    {
+        /*
+         *  ve1: local scope, cache on
+         */
+        VelocityEngine ve1 = new VelocityEngine();
+        
+        ve1.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+        ve1.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+                Boolean.FALSE);
+        ve1.setProperty("file.resource.loader.cache", Boolean.TRUE);
+        ve1.setProperty(
+                Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, NullLogChute.class.getName());
+        ve1.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
+        ve1.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+                TEST_COMPARE_DIR + "/parsemacros");
+        ve1.init();
+        
+        assureResultsDirectoryExists(RESULT_DIR);
+
+        FileOutputStream fos = new FileOutputStream (getFileName(
+                RESULT_DIR, "parseMacro2", RESULT_FILE_EXT));
+
+        VelocityContext context = new VelocityContext();
+
+        Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+        Template template = ve1.getTemplate("parseMacro2.vm");
+        template.merge(context, writer);
+
+        /**
+         * Write to the file
+         */
+        writer.flush();
+        writer.close();
+
+        if (!isMatch(RESULT_DIR, COMPARE_DIR, "parseMacro2",
+                RESULT_FILE_EXT,CMP_FILE_EXT))
+        {
+            fail("Processed template did not match expected output");
+        }
+    }
+}

Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/ParseWithMacroLibsTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java?rev=568691&view=auto
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java (added)
+++ velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java Wed Aug 22 09:31:31 2007
@@ -0,0 +1,277 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.log.NullLogChute;
+
+/**
+ * Macro library inclution via the Template.merge method is tested using this
+ * class.
+ */
+
+public class VMLibraryTestCase extends BaseTestCase
+{
+    /**
+     * This engine is used with local namespaces
+     */
+    private VelocityEngine ve1 = new VelocityEngine();
+
+    /**
+     * This engine is used with global namespaces
+     */
+    private VelocityEngine ve2 = new VelocityEngine();
+
+    private static final String RESULT_DIR = TEST_RESULT_DIR + "/macrolibs";
+
+    private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/macrolibs/compare";
+
+    public VMLibraryTestCase(String name)
+    {
+        super(name);
+    }
+
+    public void setUp()
+            throws Exception
+    {
+        /*
+         *  setup local scope for templates
+         */
+        ve1.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+        ve1.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+                Boolean.FALSE);
+        /**
+         * Turn on the cache
+         */
+        ve1.setProperty("file.resource.loader.cache", Boolean.TRUE);
+
+        ve1.setProperty(
+                Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, NullLogChute.class.getName());
+
+        ve1.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
+        ve1.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+                TEST_COMPARE_DIR + "/macrolibs");
+        ve1.init();
+
+        /**
+         * Set to global namespaces
+         */
+        ve2.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.FALSE);
+        ve2.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+                Boolean.TRUE);
+        /**
+         * Turn on the cache
+         */
+        ve2.setProperty("file.resource.loader.cache", Boolean.FALSE);
+
+        ve2.setProperty(
+                Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, NullLogChute.class.getName());
+
+        ve2.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
+        ve2.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+                TEST_COMPARE_DIR + "/macrolibs");
+        ve2.init();
+    }
+
+    public static junit.framework.Test suite()
+    {
+        return new TestSuite(VMLibraryTestCase.class);
+    }
+
+    /**
+     * Runs the tests with local namespace.
+     */
+    public void testVelociMacroLibWithLocalNamespace()
+            throws Exception
+    {
+        assureResultsDirectoryExists(RESULT_DIR);
+        /**
+         * Clear the file before proceeding
+         */
+        File file = new File(getFileName(
+                RESULT_DIR, "vm_library_local", RESULT_FILE_EXT));
+        if (file.exists())
+        {
+            file.delete();
+        }
+
+        /**
+         * Create a file output stream for appending
+         */
+        FileOutputStream fos = new FileOutputStream (getFileName(
+                RESULT_DIR, "vm_library_local", RESULT_FILE_EXT), true);
+
+        List templateList = new ArrayList();
+        VelocityContext context = new VelocityContext();
+        Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+        templateList.add("vm_library1.vm");
+
+        Template template = ve1.getTemplate("vm_library_local.vm");
+        template.merge(context, writer, templateList);
+
+        /**
+         * remove the first template library and includes a new library
+         * with a new definition for macros
+         */
+        templateList.remove(0);
+        templateList.add("vm_library2.vm");
+        template = ve1.getTemplate("vm_library_local.vm");
+        template.merge(context, writer, templateList);
+
+        /*
+         *Show that caching is working
+         */
+        Template t1 = ve1.getTemplate("vm_library_local.vm");
+        Template t2 = ve1.getTemplate("vm_library_local.vm");
+
+        assertEquals("Both templates refer to the same object", t1, t2);
+
+        /**
+         * Remove the libraries
+         */
+        template = ve1.getTemplate("vm_library_local.vm");
+        template.merge(context, writer);
+
+        /**
+         * Write to the file
+         */
+        writer.flush();
+        writer.close();
+
+        if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library_local",
+                RESULT_FILE_EXT,CMP_FILE_EXT))
+        {
+            fail("Processed template did not match expected output");
+        }
+    }
+
+    /**
+     * Runs the tests with global namespace.
+     */
+    public void testVelociMacroLibWithGlobalNamespace()
+            throws Exception
+    {
+        assureResultsDirectoryExists(RESULT_DIR);
+        /**
+         * Clear the file before proceeding
+         */
+        File file = new File(getFileName(
+                RESULT_DIR, "vm_library_global", RESULT_FILE_EXT));
+        if (file.exists())
+        {
+            file.delete();
+        }
+
+        /**
+         * Create a file output stream for appending
+         */
+        FileOutputStream fos = new FileOutputStream (getFileName(
+                RESULT_DIR, "vm_library_global", RESULT_FILE_EXT), true);
+
+        List templateList = new ArrayList();
+        VelocityContext context = new VelocityContext();
+        Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+        templateList.add("vm_library1.vm");
+
+        Template template = ve1.getTemplate("vm_library_global.vm");
+        template.merge(context, writer, templateList);
+
+        /**
+         * remove the first template library and includes a new library
+         * with a new definition for macros
+         */
+        templateList.remove(0);
+        templateList.add("vm_library2.vm");
+        template = ve1.getTemplate("vm_library_global.vm");
+        template.merge(context, writer, templateList);
+
+        /*
+         *Show that caching is not working (We have turned off cache)
+         */
+        Template t1 = ve2.getTemplate("vm_library_global.vm");
+        Template t2 = ve2.getTemplate("vm_library_global.vm");
+
+        assertNotSame("Defferent objects", t1, t2);
+
+        /**
+         * Write to the file
+         */
+        writer.flush();
+        writer.close();
+
+        if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library_global",
+                RESULT_FILE_EXT,CMP_FILE_EXT))
+        {
+            fail("Processed template did not match expected output");
+        }
+    }
+
+    /**
+     * Test whether the literal text is given if a definition cannot be
+     * found for a macro.
+     *
+     * @throws Exception
+     */
+    public void testMacrosWithNoDefinition()
+            throws Exception
+    {
+        assureResultsDirectoryExists(RESULT_DIR);
+
+        FileOutputStream fos = new FileOutputStream (getFileName(
+                RESULT_DIR, "vm_library", RESULT_FILE_EXT));
+
+        VelocityContext context = new VelocityContext();
+        Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+        Template template = ve1.getTemplate("vm_library.vm");
+        template.merge(context, writer, null);
+
+        /**
+         * Write to the file
+         */
+        writer.flush();
+        writer.close();
+
+        /**
+         * outputs the macro calls
+         */
+        if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library",
+                RESULT_FILE_EXT,CMP_FILE_EXT))
+        {
+            fail("Processed template did not match expected output");
+        }
+    }
+}

Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/VMLibraryTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Modified: velocity/engine/trunk/test/macroforwarddefine/compare/velocity.log.cmp
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/test/macroforwarddefine/compare/velocity.log.cmp?rev=568691&r1=568690&r2=568691&view=diff
==============================================================================
--- velocity/engine/trunk/test/macroforwarddefine/compare/velocity.log.cmp (original)
+++ velocity/engine/trunk/test/macroforwarddefine/compare/velocity.log.cmp Wed Aug 22 09:31:31 2007
@@ -1,8 +1,16 @@
- [error] VM #test1: error : too few arguments to macro. Wanted 1 got 0
- [error] VM #test2: error : too few arguments to macro. Wanted 1 got 0
- [error] VM #test3: error : too few arguments to macro. Wanted 1 got 0
- [error] VM #test4: error : too few arguments to macro. Wanted 1 got 0
- [error] VM #test1: error : too many arguments to macro. Wanted 1 got 2
- [error] VM #test2: error : too many arguments to macro. Wanted 1 got 2
- [error] VM #test3: error : too many arguments to macro. Wanted 1 got 2
- [error] VM #test4: error : too many arguments to macro. Wanted 1 got 2
+ [error] VM #test1: error : too few arguments to macro. Wanted 1 got 0
+ [error] VM error test1. Null AST
+ [error] VM #test2: error : too few arguments to macro. Wanted 1 got 0
+ [error] VM error test2. Null AST
+ [error] VM #test3: error : too few arguments to macro. Wanted 1 got 0
+ [error] VM error test3. Null AST
+ [error] VM #test4: error : too few arguments to macro. Wanted 1 got 0
+ [error] VM error test4. Null AST
+ [error] VM #test1: error : too many arguments to macro. Wanted 1 got 2
+ [error] VM error test1. Null AST
+ [error] VM #test2: error : too many arguments to macro. Wanted 1 got 2
+ [error] VM error test2. Null AST
+ [error] VM #test3: error : too many arguments to macro. Wanted 1 got 2
+ [error] VM error test3. Null AST
+ [error] VM #test4: error : too many arguments to macro. Wanted 1 got 2
+ [error] VM error test4. Null AST

Added: velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp?rev=568691&view=auto
==============================================================================
--- velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp (added)
+++ velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp Wed Aug 22 09:31:31 2007
@@ -0,0 +1,8 @@
+This is a test file for loading macro libs programatically
+
+call foo
+#foo(1)
+#bar(2)
+
+no macro definition
+#abc(2)

Propchange: velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/test/macrolibs/compare/vm_library.cmp
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Added: velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp?rev=568691&view=auto
==============================================================================
--- velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp (added)
+++ velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp Wed Aug 22 09:31:31 2007
@@ -0,0 +1,11 @@
+This is a test file for loading macro libs programatically
+
+call foo
+24
+no macro definition
+#abc(2) This is a test file for loading macro libs programatically
+
+call foo
+86
+no macro definition
+#abc(2) 
\ No newline at end of file

Propchange: velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/test/macrolibs/compare/vm_library_global.cmp
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision