View Javadoc

1   package org.apache.velocity.tools;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.HashMap;
23  import java.util.Map;
24  import org.apache.velocity.tools.config.Data;
25  import org.apache.velocity.tools.config.FactoryConfiguration;
26  import org.apache.velocity.tools.Scope;
27  import org.apache.velocity.tools.config.ToolboxConfiguration;
28  import org.apache.velocity.tools.config.ToolConfiguration;
29  
30  /**
31   * <p>This class is the central point of action for VelocityTools.
32   * It manages the configured and scoped {@link ToolInfo} and {@link Data}
33   * and is meant to stick around for the life of the application.
34   * </p><p>
35   * It works like this:
36   * <ol>
37   * <li>Build up your {@link FactoryConfiguration}(s)</li>
38   * <li>Create a {@link ToolboxFactory} instance</li>
39   * <li>Pass the configuration to {@link #configure}</li>
40   * <li>When appropriate for each scope, use {@link #createToolbox}
41   * to create the {@link Toolbox} for that scope and put that toolbox
42   * somewhere appropriate to that scope.</li>
43   * <li>When you want a tool, get that {@link Toolbox} and
44   * ask it for the tool you want (e.g. <code>toolbox.get("math")</code>).</li>
45   * </ol>
46   * </p><p>
47   * Of course, most users will not have to do any of this
48   * as much of it is handled for them by some combination of 
49   * {@link ToolManager} or {@link org.apache.velocity.tools.view.VelocityView}
50   * and a {@link ToolContext} or {@link org.apache.velocity.tools.view.ViewToolContext}.
51   * </p><p>
52   * <strong>NOTE:</strong> While you are free to pass in new configuration info
53   * at any time, that data will only affect {@link Toolbox}es created subsequently.
54   * Any previously created toolboxes will have to be re-created and replaced to
55   * reflect the changes to the configuration.
56   * </p>
57   *
58   * @author Nathan Bubna
59   * @version $Id: ToolboxFactory.java 511959 2007-02-26 19:24:39Z nbubna $
60   */
61  public class ToolboxFactory
62  {
63      public static final String DEFAULT_SCOPE = Scope.REQUEST;
64  
65      private final Map<String,Map<String,ToolInfo>> scopedToolInfo;
66      private final Map<String,Map<String,Object>> scopedProperties;
67      private Map<String,Object> data;
68      private Map<String,Object> globalProperties;
69      
70      public ToolboxFactory()
71      {
72          this.scopedToolInfo = new HashMap<String,Map<String,ToolInfo>>();
73          this.scopedProperties = new HashMap<String,Map<String,Object>>();
74      }
75  
76  
77      public synchronized void configure(FactoryConfiguration config)
78      {
79          // this will throw a ConfigurationException if there is a problem
80          config.validate();
81  
82          // first do the easy part and add any data
83          for (Data datum : config.getData())
84          {
85              putData(datum.getKey(), datum.getConvertedValue());
86          }
87  
88          // property precedence follows two rules:
89          //      newer Foo-level props beat older ones
90          //      narrower-scoped props beat broader-scoped ones
91  
92          // next add the toolboxes
93          for (ToolboxConfiguration toolbox : config.getToolboxes())
94          {
95              String scope = toolbox.getScope();
96  
97              // starting with the toolinfo
98              for (ToolConfiguration tool : toolbox.getTools())
99              {
100                 addToolInfo(scope, tool.createInfo());
101             }
102 
103             // then add the properties for this toolbox
104             Map<String,Object> newToolboxProps = toolbox.getPropertyMap();
105             putProperties(scope, newToolboxProps);
106 
107             // now go thru all toolinfo for this scope old and new
108             for (ToolInfo info : getToolInfo(scope).values())
109             {
110                 // and add these new toolbox properties, which have
111                 // lower precedence than the props already in the info
112                 info.addProperties(newToolboxProps);
113             }
114         }
115 
116         // now set all the factory-level properties
117         // new ones will override old ones
118         Map<String,Object> newGlobalProps = config.getPropertyMap();
119         putGlobalProperties(newGlobalProps);
120 
121         // now go thru all toolboxes in this factory
122         for (Map<String,ToolInfo> toolbox : scopedToolInfo.values())
123         {
124             // iterating over all the toolinfo in them
125             for (ToolInfo info : toolbox.values())
126             {
127                 // and adding the new global properties last
128                 // since they have the lowest precedence
129                 info.addProperties(newGlobalProps);
130             }
131         }
132     }
133 
134 
135 
136     protected synchronized Object putData(String key, Object value)
137     {
138         if (data == null)
139         {
140             data = new HashMap<String,Object>();
141         }
142         return data.put(key, value);
143     }
144 
145     protected void addToolInfo(String scope, ToolInfo tool)
146     {
147         //TODO? check the scope against any "ValidScopes"
148         //      annotation on the tool class, or do we leave
149         //      validation like this to FactoryConfiguration?
150         getToolInfo(scope).put(tool.getKey(), tool);
151     }
152 
153     protected synchronized Map<String,ToolInfo> getToolInfo(String scope)
154     {
155         Map<String,ToolInfo> tools = scopedToolInfo.get(scope);
156         if (tools == null)
157         {
158             tools = new HashMap<String,ToolInfo>();
159             scopedToolInfo.put(scope, tools);
160         }
161         return tools;
162     }
163 
164     protected synchronized void putGlobalProperties(Map<String,Object> props)
165     {
166         if (props != null && !props.isEmpty())
167         {
168             if (globalProperties == null)
169             {
170                 globalProperties = new HashMap<String,Object>(props);
171             }
172             else
173             {
174                 globalProperties.putAll(props);
175             }
176         }
177     }
178 
179     protected synchronized void putProperties(String scope, Map<String,Object> props)
180     {
181         if (props != null && !props.isEmpty())
182         {
183             Map<String,Object> properties = scopedProperties.get(scope);
184             if (properties == null)
185             {
186                 properties = new HashMap<String,Object>(props);
187                 scopedProperties.put(scope, properties);
188             }
189             else
190             {
191                 properties.putAll(props);
192             }
193         }
194     }
195 
196 
197 
198     public Object getGlobalProperty(String name)
199     {
200         if (globalProperties == null)
201         {
202             return null;
203         }
204         return globalProperties.get(name);
205     }
206 
207     public Map<String,Object> getData()
208     {
209         return data;
210     }
211 
212     public boolean hasTools(String scope)
213     {
214         Map<String,ToolInfo> tools = scopedToolInfo.get(scope);
215         if (tools != null && !tools.isEmpty())
216         {
217             return true;
218         }
219         else if (data != null && Scope.APPLICATION.equals(scope))
220         {
221             return true;
222         }
223         return false;
224     }
225 
226     public Toolbox createToolbox(String scope)
227     {
228         Map<String,ToolInfo> tools = scopedToolInfo.get(scope);
229         Map properties = scopedProperties.get(scope);
230 
231         Toolbox toolbox;
232         if (properties == null)
233         {
234             if (globalProperties == null)
235             {
236                 toolbox = new Toolbox(tools);
237             }
238             else
239             {
240                 toolbox = new Toolbox(tools, globalProperties);
241             }
242         }
243         else
244         {
245             //TODO: this will waste cycles on subsequent retrievals
246             //      of the same toolbox. consider improving...
247             if (globalProperties != null)
248             {
249                 properties.putAll(globalProperties);
250             }
251             toolbox = new Toolbox(tools, properties);
252         }
253 
254         // if application scoped or if there's only one toolbox,
255         // then automatically include data, if we have any.
256         if (data != null &&
257             (scopedToolInfo.size() == 1 || scope.equals(Scope.APPLICATION)))
258         {
259             toolbox.cacheData(getData());
260         }
261         return toolbox;
262     }
263 
264 }