| 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.HashMap; |
| import java.util.Map; |
| import org.apache.velocity.tools.config.Data; |
| import org.apache.velocity.tools.config.FactoryConfiguration; |
| import org.apache.velocity.tools.Scope; |
| import org.apache.velocity.tools.config.ToolboxConfiguration; |
| import org.apache.velocity.tools.config.ToolConfiguration; |
| |
| /** |
| * <p>This class is the central point of action for VelocityTools. |
| * It manages the configured and scoped {@link ToolInfo} and {@link Data} |
| * and is meant to stick around for the life of the application. |
| * </p><p> |
| * It works like this: |
| * </p> |
| * <ol> |
| * <li>Build up your {@link FactoryConfiguration}(s)</li> |
| * <li>Create a {@link ToolboxFactory} instance</li> |
| * <li>Pass the configuration to {@link #configure}</li> |
| * <li>When appropriate for each scope, use {@link #createToolbox} |
| * to create the {@link Toolbox} for that scope and put that toolbox |
| * somewhere appropriate to that scope.</li> |
| * <li>When you want a tool, get that {@link Toolbox} and |
| * ask it for the tool you want (e.g. <code>toolbox.get("math")</code>).</li> |
| * </ol> |
| * <p> |
| * Of course, most users will not have to do any of this |
| * as much of it is handled for them by some combination of |
| * {@link ToolManager} or org.apache.velocity.tools.view.VelocityView |
| * and a {@link ToolContext} or org.apache.velocity.tools.view.ViewToolContext. |
| * </p><p> |
| * <strong>NOTE:</strong> While you are free to pass in new configuration info |
| * at any time, that data will only affect {@link Toolbox}es created subsequently. |
| * Any previously created toolboxes will have to be re-created and replaced to |
| * reflect the changes to the configuration. |
| * </p> |
| * |
| * @author Nathan Bubna |
| * @version $Id: ToolboxFactory.java 511959 2007-02-26 19:24:39Z nbubna $ |
| */ |
| public class ToolboxFactory |
| { |
| public static final String DEFAULT_SCOPE = Scope.REQUEST; |
| |
| private final Map<String,Map<String,ToolInfo>> scopedToolInfo; |
| private final Map<String,Map<String,Object>> scopedProperties; |
| private Map<String,Object> data; |
| private Map<String,Object> globalProperties; |
| |
| public ToolboxFactory() |
| { |
| this.scopedToolInfo = new HashMap<String,Map<String,ToolInfo>>(); |
| this.scopedProperties = new HashMap<String,Map<String,Object>>(); |
| } |
| |
| |
| public synchronized void configure(FactoryConfiguration config) |
| { |
| // this will throw a ConfigurationException if there is a problem |
| config.validate(); |
| |
| // first do the easy part and add any data |
| for (Data datum : config.getData()) |
| { |
| putData(datum.getKey(), datum.getConvertedValue()); |
| } |
| |
| // property precedence follows two rules: |
| // newer Foo-level props beat older ones |
| // narrower-scoped props beat broader-scoped ones |
| |
| // next add the toolboxes |
| for (ToolboxConfiguration toolbox : config.getToolboxes()) |
| { |
| String scope = toolbox.getScope(); |
| |
| // starting with the toolinfo |
| for (ToolConfiguration tool : toolbox.getTools()) |
| { |
| addToolInfo(scope, tool.createInfo()); |
| } |
| |
| // then add the properties for this toolbox |
| Map<String,Object> newToolboxProps = toolbox.getPropertyMap(); |
| putProperties(scope, newToolboxProps); |
| |
| // now go thru all toolinfo for this scope old and new |
| for (ToolInfo info : getToolInfo(scope).values()) |
| { |
| // and add these new toolbox properties, which have |
| // lower precedence than the props already in the info |
| info.addProperties(newToolboxProps); |
| } |
| } |
| |
| // now set all the factory-level properties |
| // new ones will override old ones |
| Map<String,Object> newGlobalProps = config.getPropertyMap(); |
| putGlobalProperties(newGlobalProps); |
| |
| // now go thru all toolboxes in this factory |
| for (Map<String,ToolInfo> toolbox : scopedToolInfo.values()) |
| { |
| // iterating over all the toolinfo in them |
| for (ToolInfo info : toolbox.values()) |
| { |
| // and adding the new global properties last |
| // since they have the lowest precedence |
| info.addProperties(newGlobalProps); |
| } |
| } |
| } |
| |
| |
| |
| protected synchronized Object putData(String key, Object value) |
| { |
| if (data == null) |
| { |
| data = new HashMap<String,Object>(); |
| } |
| return data.put(key, value); |
| } |
| |
| protected void addToolInfo(String scope, ToolInfo tool) |
| { |
| //TODO? check the scope against any "ValidScopes" |
| // annotation on the tool class, or do we leave |
| // validation like this to FactoryConfiguration? |
| getToolInfo(scope).put(tool.getKey(), tool); |
| } |
| |
| protected synchronized Map<String,ToolInfo> getToolInfo(String scope) |
| { |
| Map<String,ToolInfo> tools = scopedToolInfo.get(scope); |
| if (tools == null) |
| { |
| tools = new HashMap<String,ToolInfo>(); |
| scopedToolInfo.put(scope, tools); |
| } |
| return tools; |
| } |
| |
| protected synchronized void putGlobalProperties(Map<String,Object> props) |
| { |
| if (props != null && !props.isEmpty()) |
| { |
| if (globalProperties == null) |
| { |
| globalProperties = new HashMap<String,Object>(props); |
| } |
| else |
| { |
| globalProperties.putAll(props); |
| } |
| } |
| } |
| |
| protected synchronized void putProperties(String scope, Map<String,Object> props) |
| { |
| if (props != null && !props.isEmpty()) |
| { |
| Map<String,Object> properties = scopedProperties.get(scope); |
| if (properties == null) |
| { |
| properties = new HashMap<String,Object>(props); |
| scopedProperties.put(scope, properties); |
| } |
| else |
| { |
| properties.putAll(props); |
| } |
| } |
| } |
| |
| |
| |
| public Object getGlobalProperty(String name) |
| { |
| if (globalProperties == null) |
| { |
| return null; |
| } |
| return globalProperties.get(name); |
| } |
| |
| public Map<String,Object> getData() |
| { |
| return data; |
| } |
| |
| public boolean hasTools(String scope) |
| { |
| Map<String,ToolInfo> tools = scopedToolInfo.get(scope); |
| if (tools != null && !tools.isEmpty()) |
| { |
| return true; |
| } |
| else if (data != null && Scope.APPLICATION.equals(scope)) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| public Toolbox createToolbox(String scope) |
| { |
| Map<String,ToolInfo> tools = scopedToolInfo.get(scope); |
| Map properties = scopedProperties.get(scope); |
| |
| Toolbox toolbox; |
| if (properties == null) |
| { |
| if (globalProperties == null) |
| { |
| toolbox = new Toolbox(tools); |
| } |
| else |
| { |
| toolbox = new Toolbox(tools, globalProperties); |
| } |
| } |
| else |
| { |
| //TODO: this will waste cycles on subsequent retrievals |
| // of the same toolbox. consider improving... |
| if (globalProperties != null) |
| { |
| properties.putAll(globalProperties); |
| } |
| toolbox = new Toolbox(tools, properties); |
| } |
| |
| // if application scoped or if there's only one toolbox, |
| // then automatically include data, if we have any. |
| if (data != null && |
| (scopedToolInfo.size() == 1 || scope.equals(Scope.APPLICATION))) |
| { |
| toolbox.cacheData(getData()); |
| } |
| return toolbox; |
| } |
| |
| } |