You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2008/08/13 02:07:26 UTC

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

Author: nbubna
Date: Tue Aug 12 17:07:23 2008
New Revision: 685390

URL: http://svn.apache.org/viewvc?rev=685390&view=rev
Log:
VELOCITY-607 - refactor velocimacro proxying to fix major performance drops introduced after v1.5 (thanks to Jarkko Viinamaki)

Added:
    velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java   (with props)
Modified:
    velocity/engine/trunk/src/java/org/apache/velocity/context/VMContext.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/Runtime.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeServices.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeSingleton.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroFactory.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroManager.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Macro.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/RuntimeMacro.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VMProxyArg.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/visitor/VMReferenceMungeVisitor.java
    velocity/engine/trunk/test/macroforwarddefine/compare/velocity.log.cmp

Added: 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=685390&view=auto
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java (added)
+++ velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java Tue Aug 12 17:07:23 2008
@@ -0,0 +1,492 @@
+package org.apache.velocity.context;
+
+/*
+ * 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.StringWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.parser.ParserTreeConstants;
+import org.apache.velocity.runtime.parser.node.ASTReference;
+import org.apache.velocity.runtime.parser.node.Node;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.util.introspection.IntrospectionCacheData;
+
+/**
+ * Context for Velocity macro arguments.
+ * 
+ * This special context combines ideas of earlier VMContext and VMProxyArgs
+ * by implementing routing functionality internally. This significantly
+ * reduces memory allocation upon macro invocations.
+ * Since the macro AST is now shared and RuntimeMacro directive is used,
+ * the earlier implementation of precalculating VMProxyArgs would not work.
+ * 
+ * @author <a href="mailto:wyla@removeme.sci.fi">Jarkko Viinamaki</a>
+ * @see http://issues.apache.org/jira/browse/VELOCITY-607
+ * @version $Id$
+ */
+public class ProxyVMContext implements InternalContextAdapter
+{
+    /** container for our macro AST node arguments. Size must be power of 2. */
+    Map vmproxyhash = new HashMap(8, 0.8f);
+
+    /** container for any local or constant macro arguments. Size must be power of 2. */
+    Map localcontext = new HashMap(8, 0.8f);;
+
+    /** the base context store. This is the 'global' context */
+    InternalContextAdapter innerContext;
+
+    /** context that we are wrapping */
+    InternalContextAdapter wrappedContext;
+
+    /** support for local context scope feature, where all references are local */
+    private boolean localContextScope;
+
+    /** needed for writing log entries. */
+    private RuntimeServices rsvc;
+
+    /**
+     * @param inner Velocity context for processing
+     * @param rsvc RuntimeServices provides logging reference
+     * @param localContextScope if true, all references are set to be local
+     */
+    public ProxyVMContext(InternalContextAdapter inner,
+                          RuntimeServices rsvc,
+                          boolean localContextScope)
+    {
+        this.localContextScope = localContextScope;
+        this.rsvc = rsvc;
+
+        wrappedContext = inner;
+        innerContext = inner.getBaseContext();
+    }
+
+    /**
+     * Return the inner / user context.
+     * 
+     * @return The inner / user context.
+     */
+    public Context getInternalUserContext()
+    {
+        return innerContext.getInternalUserContext();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalWrapperContext#getBaseContext()
+     */
+    public InternalContextAdapter getBaseContext()
+    {
+        return innerContext.getBaseContext();
+    }
+
+    /**
+     * Used to put Velocity macro 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 argument
+     * 
+     * @throws MethodInvocationException
+     */
+    public void addVMProxyArg(InternalContextAdapter context,
+                              String macroArgumentName,
+                              String literalMacroArgumentName,
+                              Node argumentValue) throws MethodInvocationException
+    {
+        if (isConstant(argumentValue))
+        {
+            localcontext.put(macroArgumentName, argumentValue.value(context));
+        }
+        else
+        {
+            vmproxyhash.put(macroArgumentName, argumentValue);
+            localcontext.put(literalMacroArgumentName, argumentValue);
+        }
+    }
+
+    /**
+     * AST nodes that are considered constants can be directly
+     * saved into the context. Dynamic values are stored in
+     * another argument hashmap.
+     * 
+     * @param node macro argument as AST node
+     * @return true if the node is a constant value
+     */
+    private boolean isConstant(Node node)
+    {
+        switch (node.getType())
+        {
+            case ParserTreeConstants.JJTINTEGERRANGE:
+            case ParserTreeConstants.JJTREFERENCE:
+            case ParserTreeConstants.JJTOBJECTARRAY:
+            case ParserTreeConstants.JJTMAP:
+            case ParserTreeConstants.JJTSTRINGLITERAL:
+            case ParserTreeConstants.JJTTEXT:
+                return (false);
+            default:
+                return (true);
+        }
+    }
+
+    /**
+     * Impl of the Context.put() method.
+     * 
+     * @param key name of item to set
+     * @param value object to set to key
+     * @return old stored object
+     */
+    public Object put(final String key, final Object value)
+    {
+        return put(key, value, localContextScope);
+    }
+
+    /**
+     * Allows callers to explicitly put objects in the local context, no matter what the
+     * velocimacro.context.local setting says. Needed e.g. for loop variables in foreach.
+     * 
+     * @param key name of item to set.
+     * @param value object to set to key.
+     * @return old stored object
+     */
+    public Object localPut(final String key, final Object value)
+    {
+        return put(key, value, true);
+    }
+
+    /**
+     * Internal put method to select between local and global scope.
+     * 
+     * @param key name of item to set
+     * @param value object to set to key
+     * @param forceLocal True forces the object into the local scope.
+     * @return old stored object
+     */
+    protected Object put(final String key, final Object value, final boolean forceLocal)
+    {
+        Node astNode = (Node) vmproxyhash.get(key);
+
+        if (astNode != null)
+        {
+            if (astNode.getType() == ParserTreeConstants.JJTREFERENCE)
+            {
+                ASTReference ref = (ASTReference) astNode;
+
+                if (ref.jjtGetNumChildren() > 0)
+                    ref.setValue(wrappedContext, value);
+                else
+                    wrappedContext.put(ref.getRootString(), value);
+
+            }
+            else
+            {
+                rsvc.getLog().error("ProxyVMContext.put() : New value cannot be assigned to a constant: "
+                                    + key + " / " + get("$" + key + ".literal"));
+            }
+            return null;
+        }
+        else
+        {
+            if (forceLocal)
+            {
+                return localcontext.put(key, value);
+            }
+            else
+            {
+                if (localcontext.containsKey(key))
+                {
+                    return localcontext.put(key, value);
+                }
+                else
+                {
+                    return innerContext.put(key, value);
+                }
+            }
+        }
+    }
+
+    /**
+     * Implementation of the Context.get() method.
+     * 
+     * @param key name of item to get
+     * @return stored object or null
+     */
+    public Object get(String key)
+    {
+        Object o = null;
+
+        Node astNode = (Node) vmproxyhash.get(key);
+
+        if (astNode != null)
+        {
+            int type = astNode.getType();
+
+            // if the macro argument (astNode) is a reference, we need to evaluate it
+            // in case it is a multilevel node
+            if (type == ParserTreeConstants.JJTREFERENCE)
+            {
+                ASTReference ref = (ASTReference) astNode;
+
+                if (ref.jjtGetNumChildren() > 0)
+                {
+                    return ref.execute(null, wrappedContext);
+                }
+                else
+                {
+                    return wrappedContext.get(ref.getRootString());
+                }
+            }
+            else if (type == ParserTreeConstants.JJTTEXT)
+            {
+                // this really shouldn't happen. text is just a throwaway arg for #foreach()
+                try
+                {
+                    StringWriter writer = new StringWriter();
+                    astNode.render(wrappedContext, writer);
+
+                    return writer.toString();
+                }
+                catch (RuntimeException e)
+                {
+                    throw e;
+                }
+                catch (Exception e)
+                {
+                    rsvc.getLog().error("ProxyVMContext.get() : error rendering reference", e);
+                }
+            }
+            else
+            {
+                // use value method to render other dynamic nodes
+                return astNode.value(wrappedContext);
+            }
+        }
+        else
+        {
+            o = localcontext.get(key);
+
+            if (o == null)
+            {
+                o = innerContext.get(key);
+            }
+        }
+
+        return o;
+    }
+
+    /**
+     * @see org.apache.velocity.context.Context#containsKey(java.lang.Object)
+     */
+    public boolean containsKey(Object key)
+    {
+        return false;
+    }
+
+    /**
+     * @see org.apache.velocity.context.Context#getKeys()
+     */
+    public Object[] getKeys()
+    {
+        return vmproxyhash.keySet().toArray();
+    }
+
+    /**
+     * @see org.apache.velocity.context.Context#remove(java.lang.Object)
+     */
+    public Object remove(Object key)
+    {
+        if (vmproxyhash.containsKey(key))
+        {
+            return vmproxyhash.remove(key);
+        }
+        else
+        {
+            if (localContextScope)
+            {
+                return localcontext.remove(key);
+            }
+
+            Object oldValue = localcontext.remove(key);
+            if (oldValue == null)
+            {
+                oldValue = innerContext.remove(key);
+            }
+            return oldValue;
+        }
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#pushCurrentTemplateName(java.lang.String)
+     */
+    public void pushCurrentTemplateName(String s)
+    {
+        innerContext.pushCurrentTemplateName(s);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#popCurrentTemplateName()
+     */
+    public void popCurrentTemplateName()
+    {
+        innerContext.popCurrentTemplateName();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getCurrentTemplateName()
+     */
+    public String getCurrentTemplateName()
+    {
+        return innerContext.getCurrentTemplateName();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getTemplateNameStack()
+     */
+    public Object[] getTemplateNameStack()
+    {
+        return innerContext.getTemplateNameStack();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#pushCurrentMacroName(java.lang.String)
+     */
+    public void pushCurrentMacroName(String s)
+    {
+        innerContext.pushCurrentMacroName(s);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#popCurrentMacroName()
+     */
+    public void popCurrentMacroName()
+    {
+        innerContext.popCurrentMacroName();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getCurrentMacroName()
+     */
+    public String getCurrentMacroName()
+    {
+        return innerContext.getCurrentMacroName();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getCurrentMacroCallDepth()
+     */
+    public int getCurrentMacroCallDepth()
+    {
+        return innerContext.getCurrentMacroCallDepth();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getMacroNameStack()
+     */
+    public Object[] getMacroNameStack()
+    {
+        return innerContext.getMacroNameStack();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#icacheGet(java.lang.Object)
+     */
+    public IntrospectionCacheData icacheGet(Object key)
+    {
+        return innerContext.icacheGet(key);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#icachePut(java.lang.Object,
+     *      org.apache.velocity.util.introspection.IntrospectionCacheData)
+     */
+    public void icachePut(Object key, IntrospectionCacheData o)
+    {
+        innerContext.icachePut(key, o);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getAllowRendering()
+     */
+    public boolean getAllowRendering()
+    {
+        return innerContext.getAllowRendering();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#setAllowRendering(boolean)
+     */
+    public void setAllowRendering(boolean v)
+    {
+        innerContext.setAllowRendering(v);
+    }
+
+    /**
+     * @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)
+    {
+        EventCartridge cartridge = innerContext.attachEventCartridge(ec);
+        return cartridge;
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalEventContext#getEventCartridge()
+     */
+    public EventCartridge getEventCartridge()
+    {
+        return innerContext.getEventCartridge();
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#setCurrentResource(org.apache.velocity.runtime.resource.Resource)
+     */
+    public void setCurrentResource(Resource r)
+    {
+        innerContext.setCurrentResource(r);
+    }
+
+    /**
+     * @see org.apache.velocity.context.InternalHousekeepingContext#getCurrentResource()
+     */
+    public Resource getCurrentResource()
+    {
+        return innerContext.getCurrentResource();
+    }
+}

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

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java
------------------------------------------------------------------------------
    svn:keywords = Revision

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/context/ProxyVMContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=685390&r1=685389&r2=685390&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 Tue Aug 12 17:07:23 2008
@@ -40,6 +40,8 @@
  *  Further, this context also supports the 'VM local context' mode, where
  *  any put() of references that aren't args to the VM are considered
  *  local to the vm, protecting the global context.
+ *  
+ *  @deprecated ProxyVMContext is used instead
  *
  *  @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  *  @version $Id$

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/Runtime.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/Runtime.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/Runtime.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/Runtime.java Tue Aug 12 17:07:23 2008
@@ -426,6 +426,7 @@
      * @param sourceTemplate The template from which the macro is requested.
      * @return boolean  True if added, false if rejected for some
      *                  reason (either parameters or permission settings)
+     * @deprecated Just like the whole class....
      */
     public static boolean addVelocimacro( String name,
                                           String macro,

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java Tue Aug 12 17:07:23 2008
@@ -19,7 +19,6 @@
  * under the License.    
  */
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -44,7 +43,6 @@
 import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
 import org.apache.velocity.context.Context;
 import org.apache.velocity.context.InternalContextAdapterImpl;
-import org.apache.velocity.exception.MacroOverflowException;
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.ParseErrorException;
 import org.apache.velocity.exception.ResourceNotFoundException;
@@ -54,6 +52,7 @@
 import org.apache.velocity.runtime.log.LogManager;
 import org.apache.velocity.runtime.parser.ParseException;
 import org.apache.velocity.runtime.parser.Parser;
+import org.apache.velocity.runtime.parser.node.Node;
 import org.apache.velocity.runtime.parser.node.SimpleNode;
 import org.apache.velocity.runtime.resource.ContentResource;
 import org.apache.velocity.runtime.resource.ResourceManager;
@@ -1478,7 +1477,7 @@
     }
 
     /**
-     * Returns the appropriate VelocimacroProxy object if strVMname
+     * Returns the appropriate VelocimacroProxy object if vmName
      * is a valid current Velocimacro.
      *
      * @param vmName Name of velocimacro requested
@@ -1490,6 +1489,26 @@
         return vmFactory.getVelocimacro( vmName, templateName );
     }
 
+    /**
+     * Returns the appropriate VelocimacroProxy object if vmName
+     * is a valid current Velocimacro.
+     *
+     * @param vmName  Name of velocimacro requested
+     * @param templateName Name of the namespace.
+     * @param renderingTemplate Name of the template we are currently rendering. This
+     *    information is needed when VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL setting is true
+     *    and template contains a macro with the same name as the global macro library.
+     * 
+     * @since Velocity 1.6
+     * 
+     * @return VelocimacroProxy
+     */
+    public Directive getVelocimacro(String vmName, String templateName, String renderingTemplate)
+    {
+        return vmFactory.getVelocimacro( vmName, templateName, renderingTemplate );
+    }
+    
+    
    /**
     * Adds a new Velocimacro. Usually called by Macro only while parsing.
     *
@@ -1498,6 +1517,9 @@
     * @param argArray Array of strings, containing the
     *                         #macro() arguments.  the 0th is the name.
     * @param sourceTemplate Name of the template that contains the velocimacro.
+    * 
+    * @deprecated Use addVelocimacro(String, Node, String[], String) instead
+    * 
     * @return True if added, false if rejected for some
     *                  reason (either parameters or permission settings)
     */
@@ -1510,6 +1532,31 @@
     }
 
     /**
+     * Adds a new Velocimacro. Usually called by Macro only while parsing.
+     * 
+     * Called by org.apache.velocity.runtime.directive.processAndRegister
+     *
+     * @param name  Name of velocimacro
+     * @param macro  root AST node of the parsed macro
+     * @param argArray  Array of strings, containing the
+     *                         #macro() arguments.  the 0th is the name.
+     * @param sourceTemplate
+     * 
+     * @since Velocity 1.6
+     *                   
+     * @return boolean  True if added, false if rejected for some
+     *                  reason (either parameters or permission settings)
+     */
+    public boolean addVelocimacro( String name,
+                                          Node macro,
+                                          String argArray[],
+                                          String sourceTemplate )
+    {
+        return vmFactory.addVelocimacro(name, macro,  argArray,  sourceTemplate);
+    }
+    
+    
+    /**
      *  Checks to see if a VM exists
      *
      * @param vmName Name of the Velocimacro.

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeServices.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeServices.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeServices.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeServices.java Tue Aug 12 17:07:23 2008
@@ -23,11 +23,11 @@
 import java.io.Reader;
 import java.io.Writer;
 import java.util.Properties;
+
 import org.apache.commons.collections.ExtendedProperties;
 import org.apache.velocity.Template;
 import org.apache.velocity.app.event.EventCartridge;
 import org.apache.velocity.context.Context;
-import org.apache.velocity.exception.MacroOverflowException;
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.ParseErrorException;
 import org.apache.velocity.exception.ResourceNotFoundException;
@@ -35,6 +35,7 @@
 import org.apache.velocity.runtime.log.Log;
 import org.apache.velocity.runtime.parser.ParseException;
 import org.apache.velocity.runtime.parser.Parser;
+import org.apache.velocity.runtime.parser.node.Node;
 import org.apache.velocity.runtime.parser.node.SimpleNode;
 import org.apache.velocity.runtime.resource.ContentResource;
 import org.apache.velocity.util.introspection.Introspector;
@@ -346,6 +347,22 @@
      * @return VelocimacroProxy
      */
     public Directive getVelocimacro( String vmName, String templateName  );
+    
+    /**
+     * Returns the appropriate VelocimacroProxy object if strVMname
+     * is a valid current Velocimacro.
+     *
+     * @param vmName  Name of velocimacro requested
+     * @param templateName Name of the namespace.
+     * @param renderingTemplate Name of the template we are currently rendering. This
+     *    information is needed when VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL setting is true
+     *    and template contains a macro with the same name as the global macro library.
+     * 
+     * @since Velocity 1.6
+     * 
+     * @return VelocimacroProxy
+     */
+    public Directive getVelocimacro( String vmName, String templateName, String renderingTemplate  );
 
    /**
      * Adds a new Velocimacro. Usually called by Macro only while parsing.
@@ -355,6 +372,9 @@
      * @param argArray  Array of strings, containing the
      *                         #macro() arguments.  the 0th is the name.
      * @param sourceTemplate
+     * 
+     * @deprecated Use addVelocimacro(String, Node, String[], String) instead
+     *                   
      * @return boolean  True if added, false if rejected for some
      *                  reason (either parameters or permission settings)
      */
@@ -364,6 +384,26 @@
                                           String sourceTemplate );
 
     /**
+     * Adds a new Velocimacro. Usually called by Macro only while parsing.
+     *
+     * @param name  Name of velocimacro
+     * @param macro  root AST node of the parsed macro
+     * @param argArray  Array of strings, containing the
+     *                         #macro() arguments.  the 0th is the name.
+     * @param sourceTemplate
+     * 
+     * @since Velocity 1.6
+     *                   
+     * @return boolean  True if added, false if rejected for some
+     *                  reason (either parameters or permission settings)
+     */
+    public boolean addVelocimacro( String name,
+                                          Node macro,
+                                          String argArray[],
+                                          String sourceTemplate );
+                                          
+                                          
+    /**
      *  Checks to see if a VM exists
      *
      * @param vmName  Name of velocimacro

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeSingleton.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeSingleton.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeSingleton.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeSingleton.java Tue Aug 12 17:07:23 2008
@@ -30,6 +30,7 @@
 import org.apache.velocity.runtime.directive.Directive;
 import org.apache.velocity.runtime.log.Log;
 import org.apache.velocity.runtime.parser.ParseException;
+import org.apache.velocity.runtime.parser.node.Node;
 import org.apache.velocity.runtime.parser.node.SimpleNode;
 import org.apache.velocity.runtime.resource.ContentResource;
 import org.apache.velocity.util.introspection.Introspector;
@@ -459,6 +460,24 @@
         return ri.getVelocimacro( vmName, templateName );
     }
 
+    /**
+     * Adds a new Velocimacro. Usually called by Macro only while parsing.
+     *
+     * @param name  Name of a new velocimacro.
+     * @param macro  root AST node of the parsed macro
+     * @param argArray  Array of strings, containing the
+     *                         #macro() arguments.  the 0th argument is the name.
+     * @param sourceTemplate The template from which the macro is requested.
+     * @return boolean  True if added, false if rejected for some
+     *                  reason (either parameters or permission settings)
+     * @see RuntimeInstance#addVelocimacro(String, Node, String[], String)
+     */
+    public static boolean addVelocimacro(String name, Node macro,
+                                         String argArray[], String sourceTemplate)
+    {
+        return ri.addVelocimacro(name, macro, argArray, sourceTemplate);
+    }
+
    /**
     * Adds a new Velocimacro. Usually called by Macro only while parsing.
     *
@@ -469,7 +488,10 @@
     * @param sourceTemplate Name of the template that contains the velocimacro.
     * @return True if added, false if rejected for some
     *                  reason (either parameters or permission settings)
-     * @see RuntimeInstance#addVelocimacro(String, String, String[], String)
+    *                  
+    * @deprecated Use addVelocimacro(String, Node, String[], String) instead                  
+    *                  
+    * @see RuntimeInstance#addVelocimacro(String, String, String[], String)
     */
     public static boolean addVelocimacro( String name,
                                           String macro,

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroFactory.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroFactory.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroFactory.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroFactory.java Tue Aug 12 17:07:23 2008
@@ -19,18 +19,21 @@
  * under the License.    
  */
 
+import java.io.StringReader;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Vector;
-import java.util.Stack;
+import java.util.ArrayList;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.velocity.Template;
-import org.apache.velocity.exception.MacroOverflowException;
 import org.apache.velocity.runtime.directive.Directive;
 import org.apache.velocity.runtime.directive.Macro;
 import org.apache.velocity.runtime.directive.VelocimacroProxy;
 import org.apache.velocity.runtime.log.LogDisplayWrapper;
+import org.apache.velocity.runtime.parser.ParseException;
+import org.apache.velocity.runtime.parser.node.Node;
 
 /**
  *  VelocimacroFactory.java
@@ -86,7 +89,7 @@
     /**
      *  vector of the library names
      */
-    private Vector macroLibVec = null;
+    private List macroLibVec = null;
 
     /**
      *  map of the library Template objects
@@ -164,19 +167,19 @@
 
              if(libfiles != null)
              {
+                 macroLibVec = new ArrayList();
                  if (libfiles instanceof Vector)
                  {
-                     macroLibVec = (Vector) libfiles;
+                     macroLibVec.addAll((Vector)libfiles);
                  }
                  else if (libfiles instanceof String)
                  {
-                     macroLibVec = new Vector();
-                     macroLibVec.addElement(libfiles);
+                     macroLibVec.add(libfiles);
                  }
 
-                 for(int i = 0; i < macroLibVec.size(); i++)
+                 for(int i = 0, is = macroLibVec.size(); i < is; i++)
                  {
-                     String lib = (String) macroLibVec.elementAt(i);
+                     String lib = (String) macroLibVec.get(i);
 
                      /*
                       * only if it's a non-empty string do we bother
@@ -256,7 +259,7 @@
                  RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, false))
             {
                 setReplacementPermission(true);
-
+                
                 log.debug("allowInlineToOverride = true : VMs " +
                     "defined inline may replace previous VM definitions");
             }
@@ -312,13 +315,17 @@
     }
 
     /**
-     *  adds a macro to the factory.
+     * Adds a macro to the factory.
+     * 
+     * addVelocimacro(String, Node, String[] argArray, String) should be used internally
+     * instead of this.
      *
      * @param name Name of the Macro to add.
      * @param macroBody String representation of the macro.
      * @param argArray Macro arguments. First element is the macro name.
      * @param sourceTemplate Source template from which the macro gets registered.
-     * @return True if Macro was registered successfully.
+     * 
+     * @return true if Macro was registered successfully.
      */
     public boolean addVelocimacro(String name, String macroBody,
             String argArray[], String sourceTemplate)
@@ -359,25 +366,29 @@
         /*
          *  see if the current ruleset allows this addition
          */
-
+        
         if (!canAddVelocimacro(name, sourceTemplate))
         {
             return false;
         }
 
-        /*
-         *  seems like all is good.  Lets do it.
-         */
-        synchronized(this)
+        synchronized (this)
         {
-            vmManager.addVM(name, macroBody, argArray, sourceTemplate);
+            try
+            {
+                Node macroRootNode = rsvc.parse(new StringReader(macroBody), sourceTemplate);
+
+                vmManager.addVM(name, macroRootNode, argArray, sourceTemplate, replaceAllowed);
+            }
+            catch (ParseException ex)
+            {
+                // to keep things 1.3 compatible call toString() here
+                throw new RuntimeException(ex.toString());
+            }
         }
 
         if (log.isDebugEnabled())
         {
-            /*
-             * Report addition of the new Velocimacro.
-             */
             StringBuffer msg = new StringBuffer("added ");
             Macro.macroToString(msg, argArray);
             msg.append(" : source = ").append(sourceTemplate);
@@ -388,6 +399,71 @@
     }
 
     /**
+     * Adds a macro to the factory.
+     * 
+     * @param name Name of the Macro to add.
+     * @param macroBody root node of the parsed macro AST
+     * @param argArray Name of the macro arguments. First element is the macro name.
+     * @param sourceTemplate Source template from which the macro gets registered.
+     * 
+     * @return true if Macro was registered successfully.
+     */
+    public boolean addVelocimacro(String name, Node macroBody,
+            String argArray[], String sourceTemplate)
+    {
+        // Called by RuntimeInstance.addVelocimacro
+
+    	/*
+         * maybe we should throw an exception, maybe just tell
+         * the caller like this...
+         *
+         * I hate this : maybe exceptions are in order here...
+         * They definitely would be if this was only called by directly
+         * by users, but Velocity calls this internally.
+         */
+        if (name == null || macroBody == null || argArray == null ||
+            sourceTemplate == null)
+        {
+            String msg = "VM '"+name+"' addition rejected : ";
+            if (name == null)
+            {
+                msg += "name";
+            }
+            else if (macroBody == null)
+            {
+                msg += "macroBody";
+            }
+            else if (argArray == null)
+            {
+                msg += "argArray";
+            }
+            else
+            {
+                msg += "sourceTemplate";
+            }
+            msg += " argument was null";
+            log.error(msg);
+            return false;
+        }
+
+        /*
+         *  see if the current ruleset allows this addition
+         */
+
+        if (!canAddVelocimacro(name, sourceTemplate))
+        {
+            return false;
+        }
+
+        synchronized(this)
+        {
+            vmManager.addVM(name, macroBody, argArray, sourceTemplate, replaceAllowed);
+        }
+        return(true);
+    }
+    
+    
+    /**
      *  determines if a given macro/namespace (name, source) combo is allowed
      *  to be added
      *
@@ -401,22 +477,11 @@
          *  short circuit and do it if autoloader is on, and the
          *  template is one of the library templates
          */
-
-        if (getAutoload() && (macroLibVec != null))
+         
+        if (autoReloadLibrary && (macroLibVec != null))
         {
-            /*
-             *  see if this is a library template
-             */
-
-            for(int i = 0; i < macroLibVec.size(); i++)
-            {
-                String lib = (String) macroLibVec.elementAt(i);
-
-                if (lib.equals(sourceTemplate))
-                {
-                    return true;
-                }
-            }
+            if( macroLibVec.contains(sourceTemplate) )
+                return true;
         }
 
 
@@ -444,9 +509,15 @@
              *
              *  so if we have it, and we aren't allowed to replace, bail
              */
-            if (isVelocimacro(name, sourceTemplate) && !replaceAllowed)
+            if (!replaceAllowed && isVelocimacro(name, sourceTemplate))
             {
-                log.error("VM addition rejected : "+name+" : inline not allowed to replace existing VM");
+                /*
+                 * Concurrency fix: the log entry was changed to debug scope because it
+                 * causes false alarms when several concurrent threads simultaneously (re)parse
+                 * some macro
+                 */ 
+                if (log.isDebugEnabled())
+                    log.debug("VM addition rejected : "+name+" : inline not allowed to replace existing VM");
                 return false;
             }
         }
@@ -455,17 +526,15 @@
     }
 
     /**
-     *  Tells the world if a given directive string is a Velocimacro
+     * Tells the world if a given directive string is a Velocimacro
      * @param vm Name of the Macro.
      * @param sourceTemplate Source template from which the macro should be loaded.
      * @return True if the given name is a macro.
      */
-    public boolean isVelocimacro(String vm , String sourceTemplate)
+    public boolean isVelocimacro(String vm, String sourceTemplate)
     {
-        synchronized(this)
-        {
-            return vmManager.has(vm, sourceTemplate);
-        }
+        // synchronization removed
+        return(vmManager.get(vm, sourceTemplate) != null);
     }
 
     /**
@@ -476,25 +545,28 @@
      * @param sourceTemplate Source template from which the macro should be loaded.
      * @return A directive representing the Macro.
      */
-    public Directive getVelocimacro(String vmName, String sourceTemplate)
-    {
-        VelocimacroProxy vp = vmManager.get(vmName, sourceTemplate);
-        if (vp == null)
-        {
-            return null;
-        }
+     public Directive getVelocimacro(String vmName, String sourceTemplate)
+     {
+        return(getVelocimacro(vmName, sourceTemplate, null));
+     }
+
+     public Directive getVelocimacro(String vmName, String sourceTemplate, String renderingTemplate)
+     {
+        VelocimacroProxy vp = null;
+
+        vp = vmManager.get(vmName, sourceTemplate, renderingTemplate);
+
+        /*
+         * if this exists, and autoload is on, we need to check where this VM came from
+         */
 
-        synchronized(vp)
+        if (vp != null && autoReloadLibrary )
         {
-            /*
-             *  if this exists, and autoload is on, we need to check
-             *  where this VM came from
-             */
-            if (getAutoload())
+            synchronized (this)
             {
                 /*
-                 *  see if this VM came from a library.  Need to pass sourceTemplate
-                 *  in the event namespaces are set, as it could be masked by local
+                 * see if this VM came from a library. Need to pass sourceTemplate in the event
+                 * namespaces are set, as it could be masked by local
                  */
 
                 String lib = vmManager.getLibraryName(vmName, sourceTemplate);
@@ -504,7 +576,7 @@
                     try
                     {
                         /*
-                         *  get the template from our map
+                         * get the template from our map
                          */
 
                         Twonk tw = (Twonk) libModMap.get(lib);
@@ -514,10 +586,9 @@
                             Template template = tw.template;
 
                             /*
-                             *  now, compare the last modified time of the resource
-                             *  with the last modified time of the template
-                             *  if the file has changed, then reload. Otherwise, we should
-                             *  be ok.
+                             * now, compare the last modified time of the resource with the last
+                             * modified time of the template if the file has changed, then reload.
+                             * Otherwise, we should be ok.
                              */
 
                             long tt = tw.modificationTime;
@@ -525,14 +596,14 @@
 
                             if (ft > tt)
                             {
-                                log.debug("auto-reloading VMs from VM library : "+lib);
+                                log.debug("auto-reloading VMs from VM library : " + lib);
 
                                 /*
-                                 *  when there are VMs in a library that invoke each other,
-                                 *  there are calls into getVelocimacro() from the init()
-                                 *  process of the VM directive.  To stop the infinite loop
-                                 *  we save the current time reported by the resource loader
-                                 *  and then be honest when the reload is complete
+                                 * when there are VMs in a library that invoke each other, there are
+                                 * calls into getVelocimacro() from the init() process of the VM
+                                 * directive. To stop the infinite loop we save the current time
+                                 * reported by the resource loader and then be honest when the
+                                 * reload is complete
                                  */
 
                                 tw.modificationTime = ft;
@@ -547,23 +618,19 @@
                                 tw.modificationTime = template.getLastModified();
 
                                 /*
-                                 *  note that we don't need to put this twonk back
-                                 *  into the map, as we can just use the same reference
-                                 *  and this block is synchronized
+                                 * note that we don't need to put this twonk
+                                 * back into the map, as we can just use the
+                                 * same reference and this block is synchronized
                                  */
-                             }
-                         }
+                            }
+                        }
                     }
                     catch (Exception e)
                     {
-                        log.error(true, "Velocimacro : Error using VM library : "+lib, e);
+                        log.error(true, "Velocimacro : Error using VM library : " + lib, e);
                     }
 
-                    /*
-                     *  and get again
-                     */
-
-                    vp = vmManager.get(vmName, sourceTemplate);
+                    vp = vmManager.get(vmName, sourceTemplate, renderingTemplate);
                 }
             }
         }
@@ -572,7 +639,8 @@
     }
 
     /**
-     *  tells the vmManager to dump the specified namespace
+     * tells the vmManager to dump the specified namespace
+     * 
      * @param namespace Namespace to dump.
      * @return True if namespace has been dumped successfully.
      */
@@ -582,10 +650,9 @@
     }
 
     /**
-     *  sets permission to have VMs local in scope to their declaring template
-     *  note that this is really taken care of in the VMManager class, but
-     *  we need it here for gating purposes in addVM
-     *  eventually, I will slide this all into the manager, maybe.
+     * sets permission to have VMs local in scope to their declaring template note that this is
+     * really taken care of in the VMManager class, but we need it here for gating purposes in addVM
+     * eventually, I will slide this all into the manager, maybe.
      */
     private void setTemplateLocalInline(boolean b)
     {
@@ -598,7 +665,7 @@
     }
 
     /**
-     *   sets the permission to add new macros
+     * sets the permission to add new macros
      */
     private boolean setAddMacroPermission(final boolean addNewAllowed)
     {
@@ -608,13 +675,13 @@
     }
 
     /**
-     *    sets the permission for allowing addMacro() calls to
-     *    replace existing VM's
+     * sets the permission for allowing addMacro() calls to replace existing VM's
      */
     private boolean setReplacementPermission(boolean arg)
     {
         boolean b = replaceAllowed;
         replaceAllowed = arg;
+        vmManager.setInlineReplacesGlobal(arg);
         return b;
     }
 

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroManager.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroManager.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroManager.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/VelocimacroManager.java Tue Aug 12 17:07:23 2008
@@ -19,15 +19,12 @@
  * under the License.    
  */
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Set;
-import org.apache.velocity.context.InternalContextAdapter;
-import org.apache.velocity.exception.ParseErrorException;
+import java.util.Map;
+
 import org.apache.velocity.runtime.directive.VelocimacroProxy;
-import org.apache.velocity.runtime.parser.ParseException;
+import org.apache.velocity.runtime.parser.node.Node;
 import org.apache.velocity.runtime.parser.node.SimpleNode;
+import org.apache.velocity.util.MapFactory;
 
 /**
  * Manages VMs in namespaces.  Currently, two namespace modes are
@@ -47,56 +44,59 @@
  */
 public class VelocimacroManager
 {
-    private final RuntimeServices rsvc;
     private static String GLOBAL_NAMESPACE = "";
 
     private boolean registerFromLib = false;
 
     /** Hash of namespace hashes. */
-    private final Hashtable namespaceHash = new Hashtable();
-    /** reference to global namespace */
-    private final Hashtable globalNamespace;
+    private final Map namespaceHash = MapFactory.create(17, 0.5f, 20, false);
 
     /** map of names of library tempates/namespaces */
-    private final Set libraries = Collections.synchronizedSet(new HashSet());
+    private final Map libraryMap = MapFactory.create(256, 0.5f, 10, false);
 
     /*
      * big switch for namespaces.  If true, then properties control
      * usage. If false, no.
      */
     private boolean namespacesOn = true;
-    private boolean  inlineLocalMode = false;
+    private boolean inlineLocalMode = false;
+    private boolean inlineReplacesGlobal = false;
 
     /**
      * Adds the global namespace to the hash.
      */
     VelocimacroManager(RuntimeServices rsvc)
     {
-        this.rsvc = rsvc;
-
         /*
          *  add the global namespace to the namespace hash. We always have that.
          */
 
-        globalNamespace = addNamespace(GLOBAL_NAMESPACE);
+        addNamespace(GLOBAL_NAMESPACE);
     }
 
     /**
      * Adds a VM definition to the cache.
+     * 
+     * Called by VelocimacroFactory.addVelociMacro (after parsing and discovery in Macro directive)
+     * 
      * @param vmName Name of the new VelociMacro.
      * @param macroBody String representation of the macro body.
      * @param argArray Array of macro parameters, first parameter is the macro name.
      * @param namespace The namespace/template from which this macro has been loaded.
      * @return Whether everything went okay.
      */
-    public boolean addVM(final String vmName, final String macroBody, final String argArray[],
-                         final String namespace)
+    public boolean addVM(final String vmName, final Node macroBody, final String argArray[],
+                         final String namespace, boolean canReplaceGlobalMacro)
     {
-        MacroEntry me = new MacroEntry(vmName, macroBody, argArray,
-                                       namespace);
+        MacroEntry me = new MacroEntry(vmName, macroBody, argArray, namespace);
 
         me.setFromLibrary(registerFromLib);
 
+        // this can happen only if someone uses this class without the Macro directive
+        // and provides a null value as an argument
+        if( macroBody == null )
+            throw new RuntimeException("Null AST for "+vmName);
+        
         /*
          *  the client (VMFactory) will signal to us via
          *  registerFromLib that we are in startup mode registering
@@ -106,9 +106,11 @@
 
         boolean isLib = true;
 
+        MacroEntry exist = (MacroEntry) getNamespace(GLOBAL_NAMESPACE).get(vmName);
+        
         if (registerFromLib)
         {
-           libraries.add(namespace);
+           libraryMap.put(namespace, namespace);
         }
         else
         {
@@ -120,19 +122,19 @@
              *  global
              */
 
-            isLib = libraries.contains(namespace);
+            isLib = libraryMap.containsKey(namespace);
         }
 
-        if (!isLib && usingNamespaces(namespace))
+        if ( !isLib && usingNamespaces(namespace) )
         {
             /*
              *  first, do we have a namespace hash already for this namespace?
              *  if not, add it to the namespaces, and add the VM
              */
 
-            Hashtable local = getNamespace(namespace, true);
+            Map local = getNamespace(namespace, true);
             local.put(vmName, me);
-
+            
             return true;
         }
         else
@@ -142,7 +144,6 @@
              *  already have it to preserve some of the autoload information
              */
 
-            MacroEntry exist = (MacroEntry) globalNamespace.get(vmName);
 
             if (exist != null)
             {
@@ -153,50 +154,57 @@
              *  now add it
              */
 
-            globalNamespace.put(vmName, me);
+            getNamespace(GLOBAL_NAMESPACE).put(vmName, me);
 
             return true;
         }
     }
-
+    
     /**
-     * determines if such a macro exists
-     * @param vmName Name of the Velocitymacro to look up.
+     * Gets a VelocimacroProxy object by the name / source template duple.
+     * 
+     * @param vmName Name of the VelocityMacro to look up.
      * @param namespace Namespace in which to look up the macro.
+     * @return A proxy representing the Macro.
      */
-    public boolean has(final String vmName, final String namespace)
-    {
-        if (usingNamespaces(namespace))
+     public VelocimacroProxy get(final String vmName, final String namespace)
+     {
+        return(get(vmName, namespace, null));
+     }
+
+     /**
+      * Gets a VelocimacroProxy object by the name / source template duple.
+      * 
+      * @param vmName Name of the VelocityMacro to look up.
+      * @param namespace Namespace in which to look up the macro.
+      * @param renderingTemplate Name of the template we are currently rendering.
+      * @return A proxy representing the Macro.
+      */
+     public VelocimacroProxy get(final String vmName, final String namespace, final String renderingTemplate)
+     {
+        if( inlineReplacesGlobal && renderingTemplate != null )
         {
-            Hashtable local = getNamespace(namespace, false);
+            /*
+             * if VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL is true (local macros can
+             * override global macros) and we know which template we are rendering at the
+             * moment, check if local namespace contains a macro we are looking for
+             * if so, return it instead of the global one
+             */
+            Map local = getNamespace(renderingTemplate, false);
             if (local != null)
             {
-                if (local.containsKey(vmName))
+                MacroEntry me = (MacroEntry) local.get(vmName);
+
+                if (me != null)
                 {
-                    return true;
+                    return me.getProxy(namespace);
                 }
             }
         }
-        if (globalNamespace.containsKey(vmName))
-        {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * gets a new living VelocimacroProxy object by the
-     * name / source template duple
-     * @param vmName Name of the VelocityMacro to look up.
-     * @param namespace Namespace in which to look up the macro.
-     * @return A proxy representing the Macro.
-     */
-    public VelocimacroProxy get(final String vmName, final String namespace)
-    {
-
+        
         if (usingNamespaces(namespace))
         {
-            Hashtable local =  getNamespace(namespace, false);
+            Map local = getNamespace(namespace, false);
 
             /*
              *  if we have macros defined for this template
@@ -205,10 +213,10 @@
             if (local != null)
             {
                 MacroEntry me = (MacroEntry) local.get(vmName);
-
+                
                 if (me != null)
                 {
-                    return me.createVelocimacro(namespace);
+                    return me.getProxy(namespace);
                 }
             }
         }
@@ -218,11 +226,11 @@
          * if it's in the global namespace
          */
 
-        MacroEntry me = (MacroEntry) globalNamespace.get( vmName );
+        MacroEntry me = (MacroEntry) getNamespace(GLOBAL_NAMESPACE).get(vmName);
 
         if (me != null)
         {
-            return me.createVelocimacro(namespace);
+            return me.getProxy(namespace);
         }
 
         return null;
@@ -242,7 +250,7 @@
         {
             if (usingNamespaces(namespace))
             {
-                Hashtable h = (Hashtable) namespaceHash.remove(namespace);
+                Map h = (Map) namespaceHash.remove(namespace);
 
                 if (h == null)
                 {
@@ -294,9 +302,9 @@
      *  if it doesn't exist
      *
      *  @param namespace  name of the namespace :)
-     *  @return namespace Hashtable of VMs or null if doesn't exist
+     *  @return namespace Map of VMs or null if doesn't exist
      */
-    private Hashtable getNamespace(final String namespace)
+    private Map getNamespace(final String namespace)
     {
         return getNamespace(namespace, false);
     }
@@ -307,11 +315,11 @@
      *
      *  @param namespace  name of the namespace :)
      *  @param addIfNew  flag to add a new namespace if it doesn't exist
-     *  @return namespace Hashtable of VMs or null if doesn't exist
+     *  @return namespace Map of VMs or null if doesn't exist
      */
-    private Hashtable getNamespace(final String namespace, final boolean addIfNew)
+    private Map getNamespace(final String namespace, final boolean addIfNew)
     {
-        Hashtable h = (Hashtable) namespaceHash.get(namespace);
+        Map h = (Map) namespaceHash.get(namespace);
 
         if (h == null && addIfNew)
         {
@@ -327,9 +335,9 @@
      *  @param namespace name of namespace to add
      *  @return Hash added to namespaces, ready for use
      */
-    private Hashtable addNamespace(final String namespace)
+    private Map addNamespace(final String namespace)
     {
-        Hashtable h = new Hashtable();
+        Map h = MapFactory.create(17, 0.5f, 20, false);
         Object oh;
 
         if ((oh = namespaceHash.put(namespace, h)) != null)
@@ -391,7 +399,7 @@
     {
         if (usingNamespaces(namespace))
         {
-            Hashtable local =  getNamespace(namespace, false);
+            Map local = getNamespace(namespace, false);
 
             /*
              *  if we have this macro defined in this namespace, then
@@ -415,7 +423,7 @@
          * if it's in the global namespace
          */
 
-        MacroEntry me = (MacroEntry) globalNamespace.get(vmName);
+        MacroEntry me = (MacroEntry) getNamespace(GLOBAL_NAMESPACE).get(vmName);
 
         if (me != null)
         {
@@ -424,6 +432,11 @@
 
         return null;
     }
+    
+    public void setInlineReplacesGlobal(boolean is)
+    {
+        inlineReplacesGlobal = is;
+    }
 
 
     /**
@@ -433,21 +446,25 @@
     {
         private final String vmName;
         private final String[] argArray;
-        private final String macroBody;
         private final String sourceTemplate;
-
         private SimpleNode nodeTree = null;
         private boolean fromLibrary = false;
+        private VelocimacroProxy vp;
 
-        private MacroEntry(final String vmName, final String macroBody,
+        private MacroEntry(final String vmName, final Node macro,
                    final String argArray[], final String sourceTemplate)
         {
             this.vmName = vmName;
             this.argArray = argArray;
-            this.macroBody = macroBody;
+            this.nodeTree = (SimpleNode)macro;
             this.sourceTemplate = sourceTemplate;
-        }
 
+            vp = new VelocimacroProxy();
+            vp.setName(this.vmName);
+            vp.setArgArray(this.argArray);
+            vp.setNodeTree(this.nodeTree);
+        }
+        
         /**
          * Has the macro been registered from a library.
          * @param fromLibrary True if the macro was registered from a Library.
@@ -484,45 +501,15 @@
             return sourceTemplate;
         }
 
-        VelocimacroProxy createVelocimacro(final String namespace)
-        {
-            VelocimacroProxy vp = new VelocimacroProxy();
-            vp.setName(this.vmName);
-            vp.setArgArray(this.argArray);
-            vp.setMacrobody(this.macroBody);
-            vp.setNodeTree(this.nodeTree);
-            vp.setNamespace(namespace);
-            return vp;
-        }
-
-        void setup(final InternalContextAdapter ica)
+        VelocimacroProxy getProxy(final String namespace)
         {
             /*
-             *  if not parsed yet, parse!
-             */
-
-            if( nodeTree == null)
-            {
-                parseTree(ica);
-            }
-        }
-
-        void parseTree(final InternalContextAdapter ica)
-        {
-            try
-            {
-                nodeTree = rsvc.parse(macroBody, "VM:" + vmName);
-                nodeTree.init(ica, null);
-            }
-            catch (ParseException pex)
-            {
-                throw new ParseErrorException(pex);
-            }
-            catch (Exception e)
-            {
-                rsvc.getLog().error("VelocimacroManager.parseTree() failed on VM '"
-                                    + vmName + "'", e);
-            }
+             * FIXME: namespace data is omitted, this probably 
+             * breaks some error reporting?
+             */ 
+            return vp;
         }
     }
 }
+
+

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Macro.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Macro.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Macro.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/Macro.java Tue Aug 12 17:07:23 2008
@@ -19,27 +19,18 @@
  * under the License.    
  */
 
-import java.io.Writer;
 import java.io.IOException;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import org.apache.commons.lang.text.StrBuilder;
+import java.io.Writer;
 
 import org.apache.velocity.context.InternalContextAdapter;
 import org.apache.velocity.exception.TemplateInitException;
-
-import org.apache.velocity.runtime.parser.node.Node;
-import org.apache.velocity.runtime.parser.node.NodeUtils;
-import org.apache.velocity.runtime.parser.Token;
+import org.apache.velocity.runtime.RuntimeServices;
 import org.apache.velocity.runtime.parser.ParseException;
 import org.apache.velocity.runtime.parser.ParserTreeConstants;
-import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.parser.Token;
+import org.apache.velocity.runtime.parser.node.Node;
 
 /**
- *   Macro.java
- *
  *  Macro implements the macro definition directive of VTL.
  *
  *  example :
@@ -177,35 +168,20 @@
                     + ParserTreeConstants.jjtNodeName[firstType], sourceTemplate, t);
         }
 
-        /*
-         *  get the arguments to the use of the VM
-         */
-
+        // get the arguments to the use of the VM - element 0 contains the macro name
         String argArray[] = getArgArray(node, rs);
 
-        /*
-         *  Now, try and eat the code block. Pass the root.
-         *  Make a big string out of our macro.
-         */
-
-        String macroString = getASTAsString(node.jjtGetChild(numArgs - 1));
-
-        /*
-         * now, try to add it.  The Factory controls permissions,
-         * so just give it a whack...
-         */
-
-        boolean macroAdded = rs.addVelocimacro(argArray[0],
-                                               macroString,
-                                               argArray, sourceTemplate);
-
-        if (!macroAdded && rs.getLog().isErrorEnabled())
-        {
-            StringBuffer msg = new StringBuffer("Failed to add macro: ");
-            macroToString(msg, argArray);
-            msg.append(" : source = ").append(sourceTemplate);
-            rs.getLog().error(msg);
-        }
+        /* 
+         * we already have the macro parsed as AST so there is no point to
+         * transform it into a String again
+         */ 
+        rs.addVelocimacro(argArray[0], node.jjtGetChild(numArgs - 1), argArray, sourceTemplate);
+        
+        /*
+         * Even if the add attempt failed, we don't log anything here.
+         * Logging must be done at VelocimacroFactory or VelocimacroManager because
+         * those classes know the real reason.
+         */ 
     }
 
 
@@ -270,44 +246,6 @@
     }
 
     /**
-     *  Returns an array of the literal rep of the AST
-     *  @param rootNode
-     *  @return String form of the macro
-     */
-    private static String getASTAsString(Node rootNode)
-    {
-        /*
-         *  this assumes that we are passed in the root
-         *  node of the code block
-         */
-
-        Token t = rootNode.getFirstToken();
-        Token tLast = rootNode.getLastToken();
-
-        /*
-         *  now, run down the part of the tree bounded by
-         *  our first and last tokens
-         */
-
-        // guessing that most macros are much longer than
-        // the 32 char default capacity.  let's guess 4x bigger :)
-        StrBuilder str = new StrBuilder(128);
-        while (t != tLast)
-        {
-            str.append(NodeUtils.tokenLiteral(t));
-            t = t.next;
-        }
-
-        /*
-         *  make sure we get the last one...
-         */
-
-        str.append(NodeUtils.tokenLiteral(t));
-
-        return str.toString();
-    }
-
-    /**
      * For debugging purposes.  Formats the arguments from
      * <code>argArray</code> and appends them to <code>buf</code>.
      *

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=685390&r1=685389&r2=685390&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 Tue Aug 12 17:07:23 2008
@@ -20,6 +20,7 @@
  */
 
 import org.apache.commons.lang.text.StrBuilder;
+
 import org.apache.velocity.context.InternalContextAdapter;
 import org.apache.velocity.runtime.parser.node.Node;
 import org.apache.velocity.runtime.parser.Token;
@@ -41,18 +42,17 @@
  * 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 = "";
+    private String macroName;
 
     /**
      * source template name
      */
-    private String sourceTemplate = "";
+    private String sourceTemplate;
 
     /**
      * Internal context adapter of macro caller.
@@ -62,7 +62,7 @@
     /**
      * Literal text of the macro
      */
-    private String literal = "";
+    private String literal = null;
 
     /**
      * Node of the macro call
@@ -124,37 +124,32 @@
         rsvc = rs;
         this.context = context;
         this.node = node;
-
+    }
+    
+    /**
+     * It is probably quite rare that we need to render the macro literal
+     * so do it only on-demand and then cache the value. This tactic helps to
+     * reduce memory usage a bit.
+     */
+    private void makeLiteral()
+    {
+        StrBuilder buffer = new StrBuilder();
         Token t = node.getFirstToken();
-
-        if (t == node.getLastToken())
+        
+        while (t != null && t != node.getLastToken())
         {
-            literal = t.image;
+            buffer.append(t.image);
+            t = t.next;
         }
-        else
-        {
-            // guessing that most macros are much longer than
-            // the 32 char default capacity.  let's guess 4x bigger :)
-            StrBuilder text = new StrBuilder(128);
-            /**
-             * Retrieve the literal text
-             */
-            while (t != null && t != node.getLastToken())
-            {
-                text.append(t.image);
-                t = t.next;
-            }
-            if (t != null)
-            {
-                text.append(t.image);
-            }
 
-            /**
-             * Store the literal text
-             */
-            literal = text.toString();
+        if (t != null)
+        {
+            buffer.append(t.image);
         }
+        
+        literal = buffer.toString();
     }
+    
 
     /**
      * Velocimacro implementation is not known at the init time. So look for
@@ -179,48 +174,67 @@
             throws IOException, ResourceNotFoundException,
             ParseErrorException, MethodInvocationException
     {
-        VelocimacroProxy vmProxy = getProxy(context);
-        if (vmProxy == null)
+        VelocimacroProxy vmProxy = null;
+        String renderingTemplate = context.getCurrentTemplateName();
+        
+        /**
+         * first look in the source template
+         */
+        Object o = rsvc.getVelocimacro(macroName, sourceTemplate, renderingTemplate);
+
+        if( o != null )
         {
-            /**
-             * If we cannot find an implementation write the literal text
-             */
-            writer.write(literal);
-            return true;
+            // getVelocimacro can only return a VelocimacroProxy so we don't need the
+            // costly instanceof check
+            vmProxy = (VelocimacroProxy)o;
         }
 
         /**
-         * init and render the proxy
-         * is the init call always necessary?
-         * if so, why are we using this.context instead of context?
+         * if not found, look in the macro libraries.
          */
-        synchronized (vmProxy)
+        if (vmProxy == null)
+        {
+            List macroLibraries = context.getMacroLibraries();
+            if (macroLibraries != null)
+            {
+                for (int i = macroLibraries.size() - 1; i >= 0; i--)
+                {
+                    o = rsvc.getVelocimacro(macroName,
+                            (String)macroLibraries.get(i), renderingTemplate);
+
+                    // get the first matching macro
+                    if (o != null)
+                    {
+                        vmProxy = (VelocimacroProxy) o;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (vmProxy != null)
         {
             try
             {
+            	// mainly check the number of arguments
                 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);
             }
             return vmProxy.render(context, writer, node);
         }
-    }
 
-    private VelocimacroProxy getProxy(InternalContextAdapter context)
-    {
-        Object vm = rsvc.getVelocimacro(macroName, sourceTemplate);
-        if (vm == null && context.getMacroLibraries() != null)
-        {
-            List libs = context.getMacroLibraries();
-            for (int i = libs.size()-1; vm == null && i >= 0; i--)
-            {
-                vm = rsvc.getVelocimacro(macroName, (String)libs.get(i));
-            }
-        }
-        return (VelocimacroProxy)vm;
+        /**
+         * If we cannot find an implementation write the literal text
+         */
+         if( literal == null )
+            makeLiteral();
+         
+        writer.write(literal);
+        return true;
     }
-
-}
\ No newline at end of file
+}

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VMProxyArg.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VMProxyArg.java?rev=685390&r1=685389&r2=685390&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VMProxyArg.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/directive/VMProxyArg.java Tue Aug 12 17:07:23 2008
@@ -75,7 +75,9 @@
  *  keep any dynamic stuff out of it, relying on trick of having the appropriate
  *  context handed to us, and when a constant argument, letting VMContext punch that
  *  into a local context.
- *
+ *  
+ *  @deprecated ProxyVMContext is used instead
+ *  
  *  @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  *  @version $Id$
  */