diff options
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/runtime/VelocimacroManager.java')
-rw-r--r-- | velocity-engine-core/src/main/java/org/apache/velocity/runtime/VelocimacroManager.java | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/VelocimacroManager.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/VelocimacroManager.java new file mode 100644 index 00000000..63dc92b9 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/VelocimacroManager.java @@ -0,0 +1,355 @@ +package org.apache.velocity.runtime; + +/* + * 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.Template; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.directive.Macro; +import org.apache.velocity.runtime.directive.VelocimacroProxy; +import org.apache.velocity.runtime.parser.node.Node; +import org.apache.velocity.runtime.parser.node.SimpleNode; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Manages VMs in namespaces. Currently, two namespace modes are + * supported: + * + * <ul> + * <li>flat - all allowable VMs are in the global namespace</li> + * <li>local - inline VMs are added to it's own template namespace</li> + * </ul> + * + * Thanks to <a href="mailto:JFernandez@viquity.com">Jose Alberto Fernandez</a> + * for some ideas incorporated here. + * + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> + * @author <a href="mailto:JFernandez@viquity.com">Jose Alberto Fernandez</a> + * @version $Id$ + */ +public class VelocimacroManager +{ + private boolean registerFromLib = false; + + /** reference to global namespace hash */ + private final Map<String, MacroEntry> globalNamespace; + + /** set of names of library templates/namespaces */ + private final Map<String, Template> libraries = new ConcurrentHashMap<>(17, 0.5f, 20); + + private RuntimeServices rsvc = null; + + /* + * big switch for namespaces. If true, then properties control + * usage. If false, no. + */ + private boolean namespacesOn = true; + private boolean inlineLocalMode = false; + private boolean inlineReplacesGlobal = false; + + /** + * Adds the global namespace to the hash. + */ + VelocimacroManager(RuntimeServices rsvc) + { + /* + * add the global namespace to the namespace hash. We always have that. + */ + + globalNamespace = new ConcurrentHashMap<>(101, 0.5f, 20); + this.rsvc = rsvc; + } + + /** + * 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 macroArgs Array of macro arguments, containing the + * #macro() arguments and default values. the 0th is the name. + * @param definingTemplate The template from which this macro has been loaded. + * @param canReplaceGlobalMacro whether this macro can replace a global macro + * @return Whether everything went okay. + */ + public boolean addVM(final String vmName, final Node macroBody, List<Macro.MacroArg> macroArgs, + final Template definingTemplate, boolean canReplaceGlobalMacro) + { + if (macroBody == null) + { + // happens only if someone uses this class without the Macro directive + // and provides a null value as an argument + throw new VelocityException("Null AST for "+vmName+" in " + definingTemplate.getName()); + } + + MacroEntry me = new MacroEntry(vmName, macroBody, macroArgs, definingTemplate.getName(), rsvc); + + me.setFromLibrary(registerFromLib); + + /* + * the client (VMFactory) will signal to us via + * registerFromLib that we are in startup mode registering + * new VMs from libraries. Therefore, we want to + * addto the library map for subsequent auto reloads + */ + + boolean isLib = true; + + MacroEntry exist = globalNamespace.get(vmName); + + if (registerFromLib) + { + libraries.put(definingTemplate.getName(), definingTemplate); + } + else + { + /* + * now, we first want to check to see if this namespace (template) + * is actually a library - if so, we need to use the global namespace + * we don't have to do this when registering, as namespaces should + * be shut off. If not, the default value is true, so we still go + * global + */ + + isLib = libraries.containsKey(definingTemplate.getName()); + } + + if ( !isLib && usingNamespaces() ) + { + definingTemplate.getMacros().put(vmName, me); + } + else + { + /* + * otherwise, add to global template. First, check if we + * already have it to preserve some of the autoload information + */ + + + if (exist != null) + { + me.setFromLibrary(exist.getFromLibrary()); + } + + /* + * now add it + */ + + globalNamespace.put(vmName, me); + + } + return true; + } + + /** + * Gets a VelocimacroProxy object by the name / source template duple. + * + * @param vmName Name of the VelocityMacro to look up. + * @param renderingTemplate Template we are currently rendering. + * @param template Source Template. + * @return A proxy representing the Macro. + */ + public VelocimacroProxy get(final String vmName, final Template renderingTemplate, final Template template) + { + if( inlineReplacesGlobal && renderingTemplate != null ) + { + /* + * 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 + */ + + MacroEntry me = (MacroEntry)renderingTemplate.getMacros().get(vmName); + if( me != null ) + { + return me.getProxy(); + } + } + + if( usingNamespaces() && template != null ) + { + MacroEntry me = (MacroEntry)template.getMacros().get(vmName); + if( template.getMacros().size() > 0 && me != null ) + { + return me.getProxy(); + } + } + + MacroEntry me = globalNamespace.get(vmName); + + if (me != null) + { + return me.getProxy(); + } + + return null; + } + + /** + * public switch to let external user of manager to control namespace + * usage indep of properties. That way, for example, at startup the + * library files are loaded into global namespace + * + * @param namespaceOn True if namespaces should be used. + */ + public void setNamespaceUsage(final boolean namespaceOn) + { + this.namespacesOn = namespaceOn; + } + + /** + * Should macros registered from Libraries be marked special? + * @param registerFromLib True if macros from Libs should be marked. + */ + public void setRegisterFromLib(final boolean registerFromLib) + { + this.registerFromLib = registerFromLib; + } + + /** + * Should macros from the same template be inlined? + * + * @param inlineLocalMode True if macros should be inlined on the same template. + */ + public void setTemplateLocalInlineVM(final boolean inlineLocalMode) + { + this.inlineLocalMode = inlineLocalMode; + } + + /** + * determines if currently using namespaces. + * + * @return true if using namespaces, false if not + */ + private boolean usingNamespaces() + { + /* + * if the big switch turns of namespaces, then ignore the rules + */ + + if (!namespacesOn) + { + return false; + } + + /* + * currently, we only support the local template namespace idea + */ + + return inlineLocalMode; + + } + + /** + * Return the library name for a given macro. + * @param vmName Name of the Macro to look up. + * @param template Template + * @return The name of the library which registered this macro in a namespace. + */ + public String getLibraryName(final String vmName, Template template) + { + if (usingNamespaces()) + { + /* + * if we have this macro defined in this namespace, then + * it is masking the global, library-based one, so + * just return null + */ + MacroEntry me = (MacroEntry)template.getMacros().get(vmName); + if( me != null ) + return null; + } + + MacroEntry me = globalNamespace.get(vmName); + + if (me != null) + { + return me.getSourceTemplate(); + } + + return null; + } + + /** + * @param is + * @since 1.6 + */ + public void setInlineReplacesGlobal(boolean is) + { + inlineReplacesGlobal = is; + } + + + /** + * wrapper class for holding VM information + */ + private static class MacroEntry + { + private final String sourceTemplate; + private boolean fromLibrary = false; + private VelocimacroProxy vp; + + private MacroEntry(final String vmName, final Node macro, + List<Macro.MacroArg> macroArgs, final String sourceTemplate, + RuntimeServices rsvc) + { + this.sourceTemplate = sourceTemplate; + + vp = new VelocimacroProxy(); + vp.init(rsvc); + vp.setName(vmName); + vp.setMacroArgs(macroArgs); + vp.setNodeTree((SimpleNode)macro); + vp.setLocation(macro.getLine(), macro.getColumn(), macro.getTemplate()); + } + + /** + * Has the macro been registered from a library. + * @param fromLibrary True if the macro was registered from a Library. + */ + public void setFromLibrary(final boolean fromLibrary) + { + this.fromLibrary = fromLibrary; + } + + /** + * Returns true if the macro was registered from a library. + * @return True if the macro was registered from a library. + */ + public boolean getFromLibrary() + { + return fromLibrary; + } + + public String getSourceTemplate() + { + return sourceTemplate; + } + + VelocimacroProxy getProxy() + { + return vp; + } + } +} |