• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.apache.velocity.runtime;
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 org.apache.velocity.Template;
23 import org.apache.velocity.exception.VelocityException;
24 import org.apache.velocity.runtime.directive.Macro;
25 import org.apache.velocity.runtime.directive.VelocimacroProxy;
26 import org.apache.velocity.runtime.parser.node.Node;
27 import org.apache.velocity.runtime.parser.node.SimpleNode;
28 
29 import java.util.List;
30 import java.util.Map;
31 import java.util.concurrent.ConcurrentHashMap;
32 
33 /**
34  * Manages VMs in namespaces.  Currently, two namespace modes are
35  * supported:
36  *
37  * <ul>
38  * <li>flat - all allowable VMs are in the global namespace</li>
39  * <li>local - inline VMs are added to it's own template namespace</li>
40  * </ul>
41  *
42  * Thanks to <a href="mailto:JFernandez@viquity.com">Jose Alberto Fernandez</a>
43  * for some ideas incorporated here.
44  *
45  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
46  * @author <a href="mailto:JFernandez@viquity.com">Jose Alberto Fernandez</a>
47  * @version $Id$
48  */
49 public class VelocimacroManager
50 {
51     private boolean registerFromLib = false;
52 
53     /** reference to global namespace hash */
54     private final Map<String, MacroEntry> globalNamespace;
55 
56     /** set of names of library templates/namespaces */
57     private final Map<String, Template> libraries = new ConcurrentHashMap<>(17, 0.5f, 20);
58 
59     private RuntimeServices rsvc = null;
60 
61     /*
62      * big switch for namespaces.  If true, then properties control
63      * usage. If false, no.
64      */
65     private boolean namespacesOn = true;
66     private boolean inlineLocalMode = false;
67     private boolean inlineReplacesGlobal = false;
68 
69     /**
70      * Adds the global namespace to the hash.
71      */
VelocimacroManager(RuntimeServices rsvc)72     VelocimacroManager(RuntimeServices rsvc)
73     {
74         /*
75          *  add the global namespace to the namespace hash. We always have that.
76          */
77 
78         globalNamespace = new ConcurrentHashMap<>(101, 0.5f, 20);
79         this.rsvc = rsvc;
80     }
81 
82     /**
83      * Adds a VM definition to the cache.
84      *
85      * Called by VelocimacroFactory.addVelociMacro (after parsing and discovery in Macro directive)
86      *
87      * @param vmName Name of the new VelociMacro.
88      * @param macroBody String representation of the macro body.
89      * @param macroArgs  Array of macro arguments, containing the
90      *        #macro() arguments and default values.  the 0th is the name.
91      * @param definingTemplate The template from which this macro has been loaded.
92      * @param canReplaceGlobalMacro whether this macro can replace a global macro
93      * @return Whether everything went okay.
94      */
addVM(final String vmName, final Node macroBody, List<Macro.MacroArg> macroArgs, final Template definingTemplate, boolean canReplaceGlobalMacro)95     public boolean addVM(final String vmName, final Node macroBody, List<Macro.MacroArg> macroArgs,
96                          final Template definingTemplate, boolean canReplaceGlobalMacro)
97     {
98         if (macroBody == null)
99         {
100             // happens only if someone uses this class without the Macro directive
101             // and provides a null value as an argument
102             throw new VelocityException("Null AST for "+vmName+" in " + definingTemplate.getName());
103         }
104 
105         MacroEntry me = new MacroEntry(vmName, macroBody, macroArgs, definingTemplate.getName(), rsvc);
106 
107         me.setFromLibrary(registerFromLib);
108 
109         /*
110          *  the client (VMFactory) will signal to us via
111          *  registerFromLib that we are in startup mode registering
112          *  new VMs from libraries.  Therefore, we want to
113          *  addto the library map for subsequent auto reloads
114          */
115 
116         boolean isLib = true;
117 
118         MacroEntry exist = globalNamespace.get(vmName);
119 
120         if (registerFromLib)
121         {
122            libraries.put(definingTemplate.getName(), definingTemplate);
123         }
124         else
125         {
126             /*
127              *  now, we first want to check to see if this namespace (template)
128              *  is actually a library - if so, we need to use the global namespace
129              *  we don't have to do this when registering, as namespaces should
130              *  be shut off. If not, the default value is true, so we still go
131              *  global
132              */
133 
134             isLib = libraries.containsKey(definingTemplate.getName());
135         }
136 
137         if ( !isLib && usingNamespaces() )
138         {
139             definingTemplate.getMacros().put(vmName, me);
140         }
141         else
142         {
143             /*
144              *  otherwise, add to global template.  First, check if we
145              *  already have it to preserve some of the autoload information
146              */
147 
148 
149             if (exist != null)
150             {
151                 me.setFromLibrary(exist.getFromLibrary());
152             }
153 
154             /*
155              *  now add it
156              */
157 
158             globalNamespace.put(vmName, me);
159 
160         }
161         return true;
162     }
163 
164     /**
165      * Gets a VelocimacroProxy object by the name / source template duple.
166      *
167      * @param vmName Name of the VelocityMacro to look up.
168      * @param renderingTemplate Template we are currently rendering.
169      * @param template Source Template.
170      * @return A proxy representing the Macro.
171      */
get(final String vmName, final Template renderingTemplate, final Template template)172     public VelocimacroProxy get(final String vmName, final Template renderingTemplate, final Template template)
173     {
174         if( inlineReplacesGlobal && renderingTemplate != null )
175         {
176             /*
177              * if VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL is true (local macros can
178              * override global macros) and we know which template we are rendering at the
179              * moment, check if local namespace contains a macro we are looking for
180              * if so, return it instead of the global one
181              */
182 
183             MacroEntry me = (MacroEntry)renderingTemplate.getMacros().get(vmName);
184             if( me != null )
185             {
186                 return me.getProxy();
187             }
188         }
189 
190         if( usingNamespaces() && template != null )
191         {
192             MacroEntry me = (MacroEntry)template.getMacros().get(vmName);
193             if( template.getMacros().size() > 0 && me != null )
194             {
195                 return me.getProxy();
196             }
197         }
198 
199         MacroEntry me = globalNamespace.get(vmName);
200 
201         if (me != null)
202         {
203             return me.getProxy();
204         }
205 
206         return null;
207     }
208 
209     /**
210      *  public switch to let external user of manager to control namespace
211      *  usage indep of properties.  That way, for example, at startup the
212      *  library files are loaded into global namespace
213      *
214      * @param namespaceOn True if namespaces should be used.
215      */
setNamespaceUsage(final boolean namespaceOn)216     public void setNamespaceUsage(final boolean namespaceOn)
217     {
218         this.namespacesOn = namespaceOn;
219     }
220 
221     /**
222      * Should macros registered from Libraries be marked special?
223      * @param registerFromLib True if macros from Libs should be marked.
224      */
setRegisterFromLib(final boolean registerFromLib)225     public void setRegisterFromLib(final boolean registerFromLib)
226     {
227         this.registerFromLib = registerFromLib;
228     }
229 
230     /**
231      * Should macros from the same template be inlined?
232      *
233      * @param inlineLocalMode True if macros should be inlined on the same template.
234      */
setTemplateLocalInlineVM(final boolean inlineLocalMode)235     public void setTemplateLocalInlineVM(final boolean inlineLocalMode)
236     {
237         this.inlineLocalMode = inlineLocalMode;
238     }
239 
240     /**
241      *  determines if currently using namespaces.
242      *
243      *  @return true if using namespaces, false if not
244      */
usingNamespaces()245     private boolean usingNamespaces()
246     {
247         /*
248          *  if the big switch turns of namespaces, then ignore the rules
249          */
250 
251         if (!namespacesOn)
252         {
253             return false;
254         }
255 
256         /*
257          *  currently, we only support the local template namespace idea
258          */
259 
260         return inlineLocalMode;
261 
262     }
263 
264     /**
265      * Return the library name for a given macro.
266      * @param vmName Name of the Macro to look up.
267      * @param template Template
268      * @return The name of the library which registered this macro in a namespace.
269      */
getLibraryName(final String vmName, Template template)270     public String getLibraryName(final String vmName, Template template)
271     {
272         if (usingNamespaces())
273         {
274             /*
275              *  if we have this macro defined in this namespace, then
276              *  it is masking the global, library-based one, so
277              *  just return null
278              */
279             MacroEntry me = (MacroEntry)template.getMacros().get(vmName);
280             if( me != null )
281                 return null;
282         }
283 
284         MacroEntry me = globalNamespace.get(vmName);
285 
286         if (me != null)
287         {
288             return me.getSourceTemplate();
289         }
290 
291         return null;
292     }
293 
294     /**
295      * @param is
296      * @since 1.6
297      */
setInlineReplacesGlobal(boolean is)298     public void setInlineReplacesGlobal(boolean is)
299     {
300         inlineReplacesGlobal = is;
301     }
302 
303 
304     /**
305      *  wrapper class for holding VM information
306      */
307     private static class MacroEntry
308     {
309         private final String sourceTemplate;
310         private boolean fromLibrary = false;
311         private VelocimacroProxy vp;
312 
MacroEntry(final String vmName, final Node macro, List<Macro.MacroArg> macroArgs, final String sourceTemplate, RuntimeServices rsvc)313         private MacroEntry(final String vmName, final Node macro,
314                    List<Macro.MacroArg> macroArgs, final String sourceTemplate,
315                    RuntimeServices rsvc)
316         {
317             this.sourceTemplate = sourceTemplate;
318 
319             vp = new VelocimacroProxy();
320             vp.init(rsvc);
321             vp.setName(vmName);
322             vp.setMacroArgs(macroArgs);
323             vp.setNodeTree((SimpleNode)macro);
324             vp.setLocation(macro.getLine(), macro.getColumn(), macro.getTemplate());
325         }
326 
327         /**
328          * Has the macro been registered from a library.
329          * @param fromLibrary True if the macro was registered from a Library.
330          */
setFromLibrary(final boolean fromLibrary)331         public void setFromLibrary(final boolean fromLibrary)
332         {
333             this.fromLibrary = fromLibrary;
334         }
335 
336         /**
337          * Returns true if the macro was registered from a library.
338          * @return True if the macro was registered from a library.
339          */
getFromLibrary()340         public boolean getFromLibrary()
341         {
342             return fromLibrary;
343         }
344 
getSourceTemplate()345         public String getSourceTemplate()
346         {
347             return sourceTemplate;
348         }
349 
getProxy()350         VelocimacroProxy getProxy()
351         {
352             return vp;
353         }
354     }
355 }
356