blob: 6e24926e992f9d1f2be84552ba4bb54c863b01c6 [file] [log] [blame]
package org.apache.velocity.tools;
/*
* 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.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <p>Instances of this class are typically created by a {@link ToolboxFactory}
* on a one-per-scope basis. So, for each application, there would be one
* application-scoped Toolbox from which you would retrieve tool instances,
* and for each request, there would be one request-scoped Toolbox.
* Of course, none of the above is enforced. There's no reason that you can't
* manually create a Toolbox or have multiple Toolboxes for each scope.
* </p><p>
* When a Toolbox creates a tool instance asked of it (see {@link #get}),
* it will cache that instance for future requests.
* </p>
*
* @author Nathan Bubna
* @version $Id: Toolbox.java 511959 2007-02-26 19:24:39Z nbubna $
*/
public class Toolbox implements java.io.Serializable
{
/**
* The key used to place instances in various scopes.
*/
public static final String KEY = Toolbox.class.getName();
private static final long serialVersionUID = 888081253188664649L;
private Map<String,ToolInfo> infoMap;
private Map<String,Object> properties;
private Map<String,Object> cache;
public Toolbox(Map<String,ToolInfo> toolInfo)
{
this(toolInfo, null);
}
public Toolbox(Map<String,ToolInfo> toolInfo, Map<String,Object> properties)
{
if (toolInfo == null)
{
this.infoMap = Collections.emptyMap();
}
else
{
this.infoMap = toolInfo;
}
this.properties = properties;
}
protected void cacheData(Map<String,Object> data)
{
if (data != null && !data.isEmpty())
{
cache = new HashMap<String,Object>(data);
}
}
public Map<String,Object> getProperties()
{
return properties;
}
public Object get(String key)
{
return get(key, null, null);
}
public Object get(String key, String path)
{
return get(key, path, null);
}
public Object get(String key, Map<String,Object> context)
{
return get(key, null, context);
}
public Object get(String key, String path, Map<String,Object> context)
{
/* try the cache */
Object tool = getFromCache(key, path);
if (tool == null)
{
/* synchronize and try again */
synchronized (this)
{
tool = getFromCache(key, path);
if (tool == null)
{
tool = getFromInfo(key, path, context);
}
}
}
return tool;
}
protected Object getFromCache(String key, String path)
{
if (cache == null)
{
return null;
}
else
{
Object tool = cache.get(key);
if (tool == null)
{
return null;
}
else if (path == null)
{
return tool;
}
else if (hasPermission(infoMap.get(key), path))
{
return tool;
}
else
{
return null;
}
}
}
protected Object getFromInfo(String key, String path,
Map<String,Object> context)
{
ToolInfo info = infoMap.get(key);
if (info != null && (path == null || hasPermission(info, path)))
{
Object tool = info.create(context);
if (cache == null)
{
cache = new HashMap<String,Object>();
}
cache.put(key, tool);
return tool;
}
return null;
}
protected boolean hasPermission(ToolInfo info, String path)
{
if (info == null || path == null)
{
return true;
}
return info.hasPermission(path);
}
public Set<String> getKeys()
{
// add keys for all available tools
Set<String> keys = new HashSet<String>(infoMap.keySet());
// be sure to add cache, which holds data keys
if (cache != null)
{
keys.addAll(cache.keySet());
}
return keys;
}
/**
* Return a new {@link Map} link tools' keys to their {@link Class}es.
* This will not instantiate any tools, it is merely informational.
* This will not include the keys for any cached data. Note that inclusion
* in this map does NOT mean that all these tools will be available for
* all requests, as this map ignores all path restrictions on the tools.
* @return a map of tools classes indexed by key
*/
public Map<String,Class> getToolClassMap()
{
Map<String,Class> classMap = new HashMap<String,Class>(infoMap.size());
for (Map.Entry<String,ToolInfo> entry : infoMap.entrySet())
{
classMap.put(entry.getKey(), entry.getValue().getToolClass());
}
return classMap;
}
public Map<String,Object> getAll(Map<String,Object> context)
{
// request all tools we have info for
for (ToolInfo info : infoMap.values())
{
get(info.getKey(), context);
}
// then return a copy of the cache
return new HashMap<String,Object>(this.cache);
}
/**
* Returns a new {@link Toolbox} that is a combination of
* this Toolbox with one or more specified {@link Toolbox}es.
* Neither this instance nor those specified are modified.
* @param toolboxes Toolboxes to combine
* @return the combined toolbox
*/
public Toolbox combine(Toolbox... toolboxes)
{
Map<String,ToolInfo> info = new HashMap<String,ToolInfo>(this.infoMap);
Map<String,Object> props = new HashMap<String,Object>(this.properties);
Map<String,Object> data = new HashMap<String,Object>(this.cache);
for (Toolbox toolbox : toolboxes)
{
info.putAll(toolbox.infoMap);
props.putAll(toolbox.properties);
data.putAll(toolbox.cache);
}
Toolbox combination = new Toolbox(info, props);
combination.cacheData(data);
return combination;
}
}