org.apache.velocity.runtime.VelocimacroManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.portal.template.velocity
Show all versions of com.liferay.portal.template.velocity
Liferay Portal Template Velocity
The newest version!
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 java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.velocity.runtime.directive.VelocimacroProxy;
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
* supported:
*
*
* - flat - all allowable VMs are in the global namespace
* - local - inline VMs are added to it's own template namespace
*
*
* Thanks to Jose Alberto Fernandez
* for some ideas incorporated here.
*
* @author Geir Magnusson Jr.
* @author Jose Alberto Fernandez
* @version $Id: VelocimacroManager.java 698376 2008-09-23 22:15:49Z nbubna $
*/
public class VelocimacroManager
{
private static String GLOBAL_NAMESPACE = "";
private boolean registerFromLib = false;
/** Hash of namespace hashes. */
private final Map namespaceHash = MapFactory.create(17, 0.5f, 20, false);
/** reference to global namespace hash */
private final Map globalNamespace;
/** set of names of library tempates/namespaces */
private final Set libraries = Collections.synchronizedSet(new HashSet());
/*
* 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 = 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 Node macroBody, final String argArray[],
final String namespace, 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 RuntimeException("Null AST for "+vmName+" in "+namespace);
}
MacroEntry me = new MacroEntry(vmName, macroBody, argArray, namespace);
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 = (MacroEntry) globalNamespace.get(vmName);
if (registerFromLib)
{
libraries.add(namespace);
}
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.contains(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
*/
Map local = getNamespace(namespace, true);
local.put(vmName, me);
return true;
}
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 namespace Namespace in which to look up the macro.
* @return A proxy representing the Macro.
*/
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.
* @since 1.6
*/
public VelocimacroProxy get(final String vmName, final String namespace, final String renderingTemplate)
{
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
*/
Map local = getNamespace(renderingTemplate, false);
if (local != null)
{
MacroEntry me = (MacroEntry) local.get(vmName);
if (me != null)
{
return me.getProxy(namespace);
}
}
}
if (usingNamespaces(namespace))
{
Map local = getNamespace(namespace, false);
/*
* if we have macros defined for this template
*/
if (local != null)
{
MacroEntry me = (MacroEntry) local.get(vmName);
if (me != null)
{
return me.getProxy(namespace);
}
}
}
/*
* if we didn't return from there, we need to simply see
* if it's in the global namespace
*/
MacroEntry me = (MacroEntry) globalNamespace.get(vmName);
if (me != null)
{
return me.getProxy(namespace);
}
return null;
}
/**
* Removes the VMs and the namespace from the manager.
* Used when a template is reloaded to avoid
* losing memory.
*
* @param namespace namespace to dump
* @return boolean representing success
*/
public boolean dumpNamespace(final String namespace)
{
synchronized(this)
{
if (usingNamespaces(namespace))
{
Map h = (Map) namespaceHash.remove(namespace);
if (h == null)
{
return false;
}
h.clear();
return true;
}
return false;
}
}
/**
* 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;
}
/**
* returns the hash for the specified namespace, and if it doesn't exist
* will create a new one and add it to the namespaces
*
* @param namespace name of the namespace :)
* @param addIfNew flag to add a new namespace if it doesn't exist
* @return namespace Map of VMs or null if doesn't exist
*/
private Map getNamespace(final String namespace, final boolean addIfNew)
{
Map h = (Map) namespaceHash.get(namespace);
if (h == null && addIfNew)
{
h = addNamespace(namespace);
}
return h;
}
/**
* adds a namespace to the namespaces
*
* @param namespace name of namespace to add
* @return Hash added to namespaces, ready for use
*/
private Map addNamespace(final String namespace)
{
Map h = MapFactory.create(17, 0.5f, 20, false);
Object oh;
if ((oh = namespaceHash.put(namespace, h)) != null)
{
/*
* There was already an entry on the table, restore it!
* This condition should never occur, given the code
* and the fact that this method is private.
* But just in case, this way of testing for it is much
* more efficient than testing before hand using get().
*/
namespaceHash.put(namespace, oh);
/*
* Should't we be returning the old entry (oh)?
* The previous code was just returning null in this case.
*/
return null;
}
return h;
}
/**
* determines if currently using namespaces.
*
* @param namespace currently ignored
* @return true if using namespaces, false if not
*/
private boolean usingNamespaces(final String namespace)
{
/*
* if the big switch turns of namespaces, then ignore the rules
*/
if (!namespacesOn)
{
return false;
}
/*
* currently, we only support the local template namespace idea
*/
if (inlineLocalMode)
{
return true;
}
return false;
}
/**
* Return the library name for a given macro.
* @param vmName Name of the Macro to look up.
* @param namespace Namespace to look the macro up.
* @return The name of the library which registered this macro in a namespace.
*/
public String getLibraryName(final String vmName, final String namespace)
{
if (usingNamespaces(namespace))
{
Map local = getNamespace(namespace, false);
/*
* if we have this macro defined in this namespace, then
* it is masking the global, library-based one, so
* just return null
*/
if ( local != null)
{
MacroEntry me = (MacroEntry) local.get(vmName);
if (me != null)
{
return null;
}
}
}
/*
* if we didn't return from there, we need to simply see
* if it's in the global namespace
*/
MacroEntry me = (MacroEntry) globalNamespace.get(vmName);
if (me != null)
{
return me.getSourceTemplate();
}
return null;
}
/**
* @since 1.6
*/
public void setInlineReplacesGlobal(boolean is)
{
inlineReplacesGlobal = is;
}
/**
* wrapper class for holding VM information
*/
private static class MacroEntry
{
private final String vmName;
private final String[] argArray;
private final String sourceTemplate;
private SimpleNode nodeTree = null;
private boolean fromLibrary = false;
private VelocimacroProxy vp;
private MacroEntry(final String vmName, final Node macro,
final String argArray[], final String sourceTemplate)
{
this.vmName = vmName;
this.argArray = argArray;
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.
*/
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;
}
/**
* Returns the node tree for this macro.
* @return The node tree for this macro.
*/
public SimpleNode getNodeTree()
{
return nodeTree;
}
/**
* Returns the source template name for this macro.
* @return The source template name for this macro.
*/
public String getSourceTemplate()
{
return sourceTemplate;
}
VelocimacroProxy getProxy(final String namespace)
{
/*
* FIXME: namespace data is omitted, this probably
* breaks some error reporting?
*/
return vp;
}
}
}