package org.apache.flex.forks.velocity.runtime;
/*
* Copyright 2000-2002,2004 The Apache Software Foundation.
*
* Licensed 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.
*/
/**
* Modified by Adobe Flex.
*/
import java.util.Hashtable;
import java.io.StringReader;
import java.io.BufferedReader;
import org.apache.flex.forks.velocity.runtime.directive.VelocimacroProxy;
import org.apache.flex.forks.velocity.runtime.parser.node.Node;
import org.apache.flex.forks.velocity.runtime.parser.node.SimpleNode;
import org.apache.flex.forks.velocity.util.StringUtils;
import org.apache.flex.forks.velocity.context.InternalContextAdapter;
/**
* 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: VelocimacroManager.java,v 1.17.4.1 2004/03/03 23:22:55 geirm Exp $
*/
public class VelocimacroManager
{
private RuntimeServices rsvc = null;
private static String GLOBAL_NAMESPACE = "";
private boolean registerFromLib = false;
/** Hash of namespace hashes. */
private Hashtable namespaceHash = new Hashtable();
/** map of names of library tempates/namespaces */
private Hashtable libraryMap = new Hashtable();
/*
* big switch for namespaces. If true, then properties control
* usage. If false, no.
*/
private boolean namespacesOn = true;
private boolean inlineLocalMode = false;
/**
* Adds the global namespace to the hash.
*/
VelocimacroManager(RuntimeServices rs)
{
this.rsvc = rs;
/*
* add the global namespace to the namespace hash. We always have that.
*/
addNamespace( GLOBAL_NAMESPACE );
}
/**
* Adds a VM definition to the cache.
* @return Whether everything went okay.
*/
public boolean addVM(String vmName, Node macroBody, String argArray[],
String namespace)
{
MacroEntry me = new MacroEntry(this, 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;
if (registerFromLib)
{
libraryMap.put(namespace, 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 = libraryMap.containsKey(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);
local.put((String) vmName, me);
return true;
}
else
{
/*
* otherwise, add to global template. First, check if we
* already have it to preserve some of the autoload information
*/
MacroEntry exist = (MacroEntry) getNamespace(GLOBAL_NAMESPACE).get(vmName);
if (exist != null)
{
me.setFromLibrary(exist.getFromLibrary());
}
/*
* now add it
*/
getNamespace(GLOBAL_NAMESPACE).put(vmName, me);
return true;
}
}
/**
* gets a new living VelocimacroProxy object by the
* name / source template duple
*/
public VelocimacroProxy get(String vmName, String namespace)
{
if (usingNamespaces(namespace))
{
Hashtable 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.createVelocimacro(namespace);
}
}
}
/*
* if we didn't return from there, we need to simply see
* if it's in the global namespace
*/
MacroEntry me = (MacroEntry) getNamespace(GLOBAL_NAMESPACE).get( vmName );
if (me != null)
{
return me.createVelocimacro(namespace);
}
return null;
}
/**
* Removes the VMs and the namespace from the manager.
* Used when a template is reloaded to avoid
* accumulating drek
*
* @param namespace namespace to dump
* @return boolean representing success
*/
public boolean dumpNamespace(String namespace)
{
synchronized(this)
{
if (usingNamespaces(namespace))
{
Hashtable h = (Hashtable) 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
*/
public void setNamespaceUsage(boolean b)
{
namespacesOn = b;
}
public void setRegisterFromLib(boolean b)
{
registerFromLib = b;
}
public void setTemplateLocalInlineVM(boolean b)
{
inlineLocalMode = b;
}
/**
* returns the hash for the specified namespace. Will not create a new one
* if it doesn't exist
*
* @param namespace name of the namespace :)
* @return namespace Hashtable of VMs or null if doesn't exist
*/
private Hashtable getNamespace(String namespace)
{
return getNamespace(namespace, false);
}
/**
* 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 Hashtable of VMs or null if doesn't exist
*/
private Hashtable getNamespace(String namespace, boolean addIfNew)
{
Hashtable h = (Hashtable) 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 Hashtable addNamespace(String namespace)
{
Hashtable h = new Hashtable();
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(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;
}
public String getLibraryName(String vmName, String namespace)
{
if (usingNamespaces(namespace))
{
Hashtable 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) getNamespace(GLOBAL_NAMESPACE).get(vmName);
if (me != null)
{
return me.getSourceTemplate();
}
return null;
}
/**
* wrapper class for holding VM information
*/
protected class MacroEntry
{
String macroname;
String[] argarray;
String sourcetemplate;
Node nodeTree;
VelocimacroManager manager = null;
boolean fromLibrary = false;
MacroEntry(VelocimacroManager vmm, String vmName, Node nodeTree,
String argArray[], String sourceTemplate)
{
this.macroname = vmName;
this.argarray = argArray;
this.nodeTree = nodeTree;
this.sourcetemplate = sourceTemplate;
this.manager = vmm;
}
public void setFromLibrary(boolean b)
{
fromLibrary = b;
}
public boolean getFromLibrary()
{
return fromLibrary;
}
public Node getNodeTree()
{
return nodeTree;
}
public String getSourceTemplate()
{
return sourcetemplate;
}
VelocimacroProxy createVelocimacro(String namespace)
{
VelocimacroProxy vp = new VelocimacroProxy();
vp.setName(this.macroname);
vp.setArgArray(this.argarray);
vp.setNodeTree(this.nodeTree);
vp.setNamespace(namespace);
return vp;
}
}
}