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 java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.Reader;
26 import java.io.StringReader;
27 import java.io.Writer;
28 import java.util.Enumeration;
29 import java.util.HashMap;
30 import java.util.Hashtable;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Properties;
34
35 import org.apache.commons.collections.ExtendedProperties;
36 import org.apache.velocity.Template;
37 import org.apache.velocity.app.event.EventCartridge;
38 import org.apache.velocity.app.event.EventHandler;
39 import org.apache.velocity.app.event.IncludeEventHandler;
40 import org.apache.velocity.app.event.InvalidReferenceEventHandler;
41 import org.apache.velocity.app.event.MethodExceptionEventHandler;
42 import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
43 import org.apache.velocity.context.Context;
44 import org.apache.velocity.context.InternalContextAdapterImpl;
45 import org.apache.velocity.exception.MethodInvocationException;
46 import org.apache.velocity.exception.ParseErrorException;
47 import org.apache.velocity.exception.ResourceNotFoundException;
48 import org.apache.velocity.exception.TemplateInitException;
49 import org.apache.velocity.exception.VelocityException;
50 import org.apache.velocity.runtime.directive.Directive;
51 import org.apache.velocity.runtime.directive.Macro;
52 import org.apache.velocity.runtime.directive.VelocimacroProxy;
53 import org.apache.velocity.runtime.directive.Scope;
54 import org.apache.velocity.runtime.directive.StopCommand;
55 import org.apache.velocity.runtime.log.Log;
56 import org.apache.velocity.runtime.log.LogManager;
57 import org.apache.velocity.runtime.parser.ParseException;
58 import org.apache.velocity.runtime.parser.Parser;
59 import org.apache.velocity.runtime.parser.node.Node;
60 import org.apache.velocity.runtime.parser.node.SimpleNode;
61 import org.apache.velocity.runtime.resource.ContentResource;
62 import org.apache.velocity.runtime.resource.ResourceManager;
63 import org.apache.velocity.util.ClassUtils;
64 import org.apache.velocity.util.RuntimeServicesAware;
65 import org.apache.velocity.util.StringUtils;
66 import org.apache.velocity.util.introspection.ChainableUberspector;
67 import org.apache.velocity.util.introspection.Introspector;
68 import org.apache.velocity.util.introspection.LinkingUberspector;
69 import org.apache.velocity.util.introspection.Uberspect;
70 import org.apache.velocity.util.introspection.UberspectLoggable;
71
72 /**
73 * This is the Runtime system for Velocity. It is the
74 * single access point for all functionality in Velocity.
75 * It adheres to the mediator pattern and is the only
76 * structure that developers need to be familiar with
77 * in order to get Velocity to perform.
78 *
79 * The Runtime will also cooperate with external
80 * systems like Turbine. Runtime properties can
81 * set and then the Runtime is initialized.
82 *
83 * Turbine, for example, knows where the templates
84 * are to be loaded from, and where the Velocity
85 * log file should be placed.
86 *
87 * So in the case of Velocity cooperating with Turbine
88 * the code might look something like the following:
89 *
90 * <blockquote><code><pre>
91 * ri.setProperty(Runtime.FILE_RESOURCE_LOADER_PATH, templatePath);
92 * ri.setProperty(Runtime.RUNTIME_LOG, pathToVelocityLog);
93 * ri.init();
94 * </pre></code></blockquote>
95 *
96 * <pre>
97 * -----------------------------------------------------------------------
98 * N O T E S O N R U N T I M E I N I T I A L I Z A T I O N
99 * -----------------------------------------------------------------------
100 * init()
101 *
102 * If init() is called by itself the RuntimeInstance will initialize
103 * with a set of default values.
104 * -----------------------------------------------------------------------
105 * init(String/Properties)
106 *
107 * In this case the default velocity properties are layed down
108 * first to provide a solid base, then any properties provided
109 * in the given properties object will override the corresponding
110 * default property.
111 * -----------------------------------------------------------------------
112 * </pre>
113 *
114 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
115 * @author <a href="mailto:jlb@houseofdistraction.com">Jeff Bowden</a>
116 * @author <a href="mailto:geirm@optonline.net">Geir Magusson Jr.</a>
117 * @version $Id: RuntimeInstance.java 1040976 2010-12-01 12:15:39Z apetrelli $
118 */
119 public class RuntimeInstance implements RuntimeConstants, RuntimeServices
120 {
121 /**
122 * VelocimacroFactory object to manage VMs
123 */
124 private VelocimacroFactory vmFactory = null;
125
126 /**
127 * The Runtime logger. We start with an instance of
128 * a 'primordial logger', which just collects log messages
129 * then, when the log system is initialized, all the
130 * messages get dumpted out of the primordial one into the real one.
131 */
132 private Log log = new Log();
133
134 /**
135 * The Runtime parser pool
136 */
137 private ParserPool parserPool;
138
139 /**
140 * Indicate whether the Runtime is in the midst of initialization.
141 */
142 private boolean initializing = false;
143
144 /**
145 * Indicate whether the Runtime has been fully initialized.
146 */
147 private volatile boolean initialized = false;
148
149 /**
150 * These are the properties that are laid down over top
151 * of the default properties when requested.
152 */
153 private ExtendedProperties overridingProperties = null;
154
155 /**
156 * This is a hashtable of initialized directives.
157 * The directives that populate this hashtable are
158 * taken from the RUNTIME_DEFAULT_DIRECTIVES
159 * property file.
160 */
161 private Map runtimeDirectives = new Hashtable();
162 /**
163 * Copy of the actual runtimeDirectives that is shared between
164 * parsers. Whenever directives are updated, the synchronized
165 * runtimeDirectives is first updated and then an unsynchronized
166 * copy of it is passed to parsers.
167 */
168 private Map runtimeDirectivesShared;
169
170 /**
171 * Object that houses the configuration options for
172 * the velocity runtime. The ExtendedProperties object allows
173 * the convenient retrieval of a subset of properties.
174 * For example all the properties for a resource loader
175 * can be retrieved from the main ExtendedProperties object
176 * using something like the following:
177 *
178 * ExtendedProperties loaderConfiguration =
179 * configuration.subset(loaderID);
180 *
181 * And a configuration is a lot more convenient to deal
182 * with then conventional properties objects, or Maps.
183 */
184 private ExtendedProperties configuration = new ExtendedProperties();
185
186 private ResourceManager resourceManager = null;
187
188 /**
189 * This stores the engine-wide set of event handlers. Event handlers for
190 * each specific merge are stored in the context.
191 */
192 private EventCartridge eventCartridge = null;
193
194 /*
195 * Each runtime instance has it's own introspector
196 * to ensure that each instance is completely separate.
197 */
198 private Introspector introspector = null;
199
200 /*
201 * Settings for provision of root scope for evaluate(...) calls.
202 */
203 private String evaluateScopeName = "evaluate";
204 private boolean provideEvaluateScope = false;
205
206 /*
207 * Opaque reference to something specificed by the
208 * application for use in application supplied/specified
209 * pluggable components
210 */
211 private Map applicationAttributes = null;
212 private Uberspect uberSpect;
213 private String encoding;
214
215 /**
216 * Creates a new RuntimeInstance object.
217 */
218 public RuntimeInstance()
219 {
220 reset();
221 }
222
223 /**
224 * This is the primary initialization method in the Velocity
225 * Runtime. The systems that are setup/initialized here are
226 * as follows:
227 *
228 * <ul>
229 * <li>Logging System</li>
230 * <li>ResourceManager</li>
231 * <li>EventHandler</li>
232 * <li>Parser Pool</li>
233 * <li>Global Cache</li>
234 * <li>Static Content Include System</li>
235 * <li>Velocimacro System</li>
236 * </ul>
237 */
238 public synchronized void init()
239 {
240 if (!initialized && !initializing)
241 {
242 log.debug("Initializing Velocity, Calling init()...");
243 initializing = true;
244
245 log.trace("*******************************************************************");
246 log.debug("Starting Apache Velocity v2.0-dev (compiled: 2009-02-05 07:40:05)");
247 log.trace("RuntimeInstance initializing.");
248
249 initializeProperties();
250 initializeLog();
251 initializeResourceManager();
252 initializeDirectives();
253 initializeEventHandlers();
254 initializeParserPool();
255
256 initializeIntrospection();
257 initializeEvaluateScopeSettings();
258 /*
259 * initialize the VM Factory. It will use the properties
260 * accessable from Runtime, so keep this here at the end.
261 */
262 vmFactory.initVelocimacro();
263
264 log.trace("RuntimeInstance successfully initialized.");
265
266 initialized = true;
267 initializing = false;
268 }
269 }
270
271 /**
272 * Resets the instance, so Velocity can be re-initialized again.
273 *
274 * @since 2.0.0
275 */
276 public synchronized void reset()
277 {
278 this.configuration = new ExtendedProperties();
279 this.encoding = null;
280 this.evaluateScopeName = "evaluate";
281 this.eventCartridge = null;
282 this.initialized = false;
283 this.initializing = false;
284 this.log = new Log();
285 this.overridingProperties = null;
286 this.parserPool = null;
287 this.provideEvaluateScope = false;
288 this.resourceManager = null;
289 this.runtimeDirectives = new Hashtable();
290 this.runtimeDirectivesShared = null;
291 this.uberSpect = null;
292
293 /*
294 * create a VM factory, introspector, and application attributes
295 */
296 vmFactory = new VelocimacroFactory( this );
297
298 /*
299 * make a new introspector and initialize it
300 */
301 introspector = new Introspector(getLog());
302
303 /*
304 * and a store for the application attributes
305 */
306 applicationAttributes = new HashMap();
307 }
308
309 /**
310 * Returns true if the RuntimeInstance has been successfully initialized.
311 * @return True if the RuntimeInstance has been successfully initialized.
312 * @since 1.5
313 */
314 public boolean isInitialized()
315 {
316 return initialized;
317 }
318
319 /**
320 * Init or die! (with some log help, of course)
321 */
322 private void requireInitialization()
323 {
324 if (!initialized)
325 {
326 try
327 {
328 init();
329 }
330 catch (Exception e)
331 {
332 getLog().error("Could not auto-initialize Velocity", e);
333 throw new RuntimeException("Velocity could not be initialized!", e);
334 }
335 }
336 }
337
338 /**
339 * Gets the classname for the Uberspect introspection package and
340 * instantiates an instance.
341 */
342 private void initializeIntrospection()
343 {
344 String[] uberspectors = configuration.getStringArray(RuntimeConstants.UBERSPECT_CLASSNAME);
345 for (int i=0; i <uberspectors.length;i++)
346 {
347 String rm = uberspectors[i];
348 Object o = null;
349
350 try
351 {
352 o = ClassUtils.getNewInstance( rm );
353 }
354 catch (ClassNotFoundException cnfe)
355 {
356 String err = "The specified class for Uberspect (" + rm
357 + ") does not exist or is not accessible to the current classloader.";
358 log.error(err);
359 throw new VelocityException(err, cnfe);
360 }
361 catch (InstantiationException ie)
362 {
363 throw new VelocityException("Could not instantiate class '" + rm + "'", ie);
364 }
365 catch (IllegalAccessException ae)
366 {
367 throw new VelocityException("Cannot access class '" + rm + "'", ae);
368 }
369
370 if (!(o instanceof Uberspect))
371 {
372 String err = "The specified class for Uberspect ("
373 + rm + ") does not implement " + Uberspect.class.getName()
374 + "; Velocity is not initialized correctly.";
375
376 log.error(err);
377 throw new VelocityException(err);
378 }
379
380 Uberspect u = (Uberspect)o;
381
382 if (u instanceof UberspectLoggable)
383 {
384 ((UberspectLoggable)u).setLog(getLog());
385 }
386
387 if (u instanceof RuntimeServicesAware)
388 {
389 ((RuntimeServicesAware)u).setRuntimeServices(this);
390 }
391
392 if (uberSpect == null)
393 {
394 uberSpect = u;
395 }
396 else
397 {
398 if (u instanceof ChainableUberspector)
399 {
400 ((ChainableUberspector)u).wrap(uberSpect);
401 uberSpect = u;
402 }
403 else
404 {
405 uberSpect = new LinkingUberspector(uberSpect,u);
406 }
407 }
408 }
409
410 if(uberSpect != null)
411 {
412 uberSpect.init();
413 }
414 else
415 {
416 /*
417 * someone screwed up. Lets not fool around...
418 */
419
420 String err = "It appears that no class was specified as the"
421 + " Uberspect. Please ensure that all configuration"
422 + " information is correct.";
423
424 log.error(err);
425 throw new VelocityException(err);
426 }
427 }
428
429 /**
430 * Initializes the Velocity Runtime with properties file.
431 * The properties file may be in the file system proper,
432 * or the properties file may be in the classpath.
433 */
434 private void setDefaultProperties()
435 {
436 InputStream inputStream = null;
437 try
438 {
439 inputStream = getClass()
440 .getResourceAsStream('/' + DEFAULT_RUNTIME_PROPERTIES);
441
442 configuration.load( inputStream );
443
444 if (log.isDebugEnabled())
445 {
446 log.debug("Default Properties File: " +
447 new File(DEFAULT_RUNTIME_PROPERTIES).getPath());
448 }
449
450
451 }
452 catch (IOException ioe)
453 {
454 String msg = "Cannot get Velocity Runtime default properties!";
455 log.error(msg, ioe);
456 throw new RuntimeException(msg, ioe);
457 }
458 finally
459 {
460 try
461 {
462 if (inputStream != null)
463 {
464 inputStream.close();
465 }
466 }
467 catch (IOException ioe)
468 {
469 String msg = "Cannot close Velocity Runtime default properties!";
470 log.error(msg, ioe);
471 throw new RuntimeException(msg, ioe);
472 }
473 }
474 }
475
476 /**
477 * Allows an external system to set a property in
478 * the Velocity Runtime.
479 *
480 * @param key property key
481 * @param value property value
482 */
483 public void setProperty(String key, Object value)
484 {
485 if (overridingProperties == null)
486 {
487 overridingProperties = new ExtendedProperties();
488 }
489
490 overridingProperties.setProperty(key, value);
491 }
492
493
494 /**
495 * Add all properties contained in the file fileName to the RuntimeInstance properties
496 */
497 public void setProperties(String fileName)
498 {
499 ExtendedProperties props = null;
500 try
501 {
502 props = new ExtendedProperties(fileName);
503 }
504 catch (IOException e)
505 {
506 throw new VelocityException("Error reading properties from '"
507 + fileName + "'", e);
508 }
509
510 Enumeration en = props.keys();
511 while (en.hasMoreElements())
512 {
513 String key = en.nextElement().toString();
514 setProperty(key, props.get(key));
515 }
516 }
517
518
519 /**
520 * Add all the properties in props to the RuntimeInstance properties
521 */
522 public void setProperties(Properties props)
523 {
524 Enumeration en = props.keys();
525 while (en.hasMoreElements())
526 {
527 String key = en.nextElement().toString();
528 setProperty(key, props.get(key));
529 }
530 }
531
532 /**
533 * Allow an external system to set an ExtendedProperties
534 * object to use. This is useful where the external
535 * system also uses the ExtendedProperties class and
536 * the velocity configuration is a subset of
537 * parent application's configuration. This is
538 * the case with Turbine.
539 *
540 * @param configuration
541 */
542 public void setConfiguration( ExtendedProperties configuration)
543 {
544 if (overridingProperties == null)
545 {
546 overridingProperties = configuration;
547 }
548 else
549 {
550 // Avoid possible ConcurrentModificationException
551 if (overridingProperties != configuration)
552 {
553 overridingProperties.combine(configuration);
554 }
555 }
556 }
557
558 /**
559 * Add a property to the configuration. If it already
560 * exists then the value stated here will be added
561 * to the configuration entry. For example, if
562 *
563 * resource.loader = file
564 *
565 * is already present in the configuration and you
566 *
567 * addProperty("resource.loader", "classpath")
568 *
569 * Then you will end up with a Vector like the
570 * following:
571 *
572 * ["file", "classpath"]
573 *
574 * @param key
575 * @param value
576 */
577 public void addProperty(String key, Object value)
578 {
579 if (overridingProperties == null)
580 {
581 overridingProperties = new ExtendedProperties();
582 }
583
584 overridingProperties.addProperty(key, value);
585 }
586
587 /**
588 * Clear the values pertaining to a particular
589 * property.
590 *
591 * @param key of property to clear
592 */
593 public void clearProperty(String key)
594 {
595 if (overridingProperties != null)
596 {
597 overridingProperties.clearProperty(key);
598 }
599 }
600
601 /**
602 * Allows an external caller to get a property. The calling
603 * routine is required to know the type, as this routine
604 * will return an Object, as that is what properties can be.
605 *
606 * @param key property to return
607 * @return Value of the property or null if it does not exist.
608 */
609 public Object getProperty(String key)
610 {
611 Object o = null;
612
613 /**
614 * Before initialization, check the user-entered properties first.
615 */
616 if (!initialized && overridingProperties != null)
617 {
618 o = overridingProperties.get(key);
619 }
620
621 /**
622 * After initialization, configuration will hold all properties.
623 */
624 if (o == null)
625 {
626 o = configuration.getProperty(key);
627 }
628 if (o instanceof String)
629 {
630 return StringUtils.nullTrim((String) o);
631 }
632 else
633 {
634 return o;
635 }
636 }
637
638 /**
639 * Initialize Velocity properties, if the default
640 * properties have not been laid down first then
641 * do so. Then proceed to process any overriding
642 * properties. Laying down the default properties
643 * gives a much greater chance of having a
644 * working system.
645 */
646 private void initializeProperties()
647 {
648 /*
649 * Always lay down the default properties first as
650 * to provide a solid base.
651 */
652 if (configuration.isInitialized() == false)
653 {
654 setDefaultProperties();
655 }
656
657 if( overridingProperties != null)
658 {
659 configuration.combine(overridingProperties);
660 }
661 }
662
663 /**
664 * Initialize the Velocity Runtime with a Properties
665 * object.
666 *
667 * @param p Velocity properties for initialization
668 */
669 public void init(Properties p)
670 {
671 setProperties(ExtendedProperties.convertProperties(p));
672 init();
673 }
674
675 private void setProperties(ExtendedProperties p)
676 {
677 if (overridingProperties == null)
678 {
679 overridingProperties = p;
680 }
681 else
682 {
683 overridingProperties.combine(p);
684 }
685 }
686
687 /**
688 * Initialize the Velocity Runtime with the name of
689 * ExtendedProperties object.
690 *
691 * @param configurationFile
692 */
693 public void init(String configurationFile)
694 {
695 try
696 {
697 setProperties(new ExtendedProperties(configurationFile));
698 }
699 catch (IOException e)
700 {
701 throw new VelocityException("Error reading properties from '"
702 + configurationFile + "'", e);
703 }
704 init();
705 }
706
707 private void initializeResourceManager()
708 {
709 /*
710 * Which resource manager?
711 */
712 String rm = getString(RuntimeConstants.RESOURCE_MANAGER_CLASS);
713
714 if (rm != null && rm.length() > 0)
715 {
716 /*
717 * if something was specified, then make one.
718 * if that isn't a ResourceManager, consider
719 * this a huge error and throw
720 */
721
722 Object o = null;
723
724 try
725 {
726 o = ClassUtils.getNewInstance( rm );
727 }
728 catch (ClassNotFoundException cnfe )
729 {
730 String err = "The specified class for ResourceManager (" + rm
731 + ") does not exist or is not accessible to the current classloader.";
732 log.error(err);
733 throw new VelocityException(err, cnfe);
734 }
735 catch (InstantiationException ie)
736 {
737 throw new VelocityException("Could not instantiate class '" + rm + "'", ie);
738 }
739 catch (IllegalAccessException ae)
740 {
741 throw new VelocityException("Cannot access class '" + rm + "'", ae);
742 }
743
744 if (!(o instanceof ResourceManager))
745 {
746 String err = "The specified class for ResourceManager (" + rm
747 + ") does not implement " + ResourceManager.class.getName()
748 + "; Velocity is not initialized correctly.";
749
750 log.error(err);
751 throw new VelocityException(err);
752 }
753
754 resourceManager = (ResourceManager) o;
755
756 resourceManager.initialize(this);
757 }
758 else
759 {
760 /*
761 * someone screwed up. Lets not fool around...
762 */
763
764 String err = "It appears that no class was specified as the"
765 + " ResourceManager. Please ensure that all configuration"
766 + " information is correct.";
767
768 log.error(err);
769 throw new VelocityException( err );
770 }
771 }
772
773 private void initializeEventHandlers()
774 {
775
776 eventCartridge = new EventCartridge();
777
778 /**
779 * For each type of event handler, get the class name, instantiate it, and store it.
780 */
781
782 String[] referenceinsertion = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION);
783 if ( referenceinsertion != null )
784 {
785 for ( int i=0; i < referenceinsertion.length; i++ )
786 {
787 EventHandler ev = initializeSpecificEventHandler(referenceinsertion[i],RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION,ReferenceInsertionEventHandler.class);
788 if (ev != null)
789 eventCartridge.addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev);
790 }
791 }
792
793 String[] methodexception = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION);
794 if ( methodexception != null )
795 {
796 for ( int i=0; i < methodexception.length; i++ )
797 {
798 EventHandler ev = initializeSpecificEventHandler(methodexception[i],RuntimeConstants.EVENTHANDLER_METHODEXCEPTION,MethodExceptionEventHandler.class);
799 if (ev != null)
800 eventCartridge.addMethodExceptionHandler((MethodExceptionEventHandler) ev);
801 }
802 }
803
804 String[] includeHandler = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INCLUDE);
805 if ( includeHandler != null )
806 {
807 for ( int i=0; i < includeHandler.length; i++ )
808 {
809 EventHandler ev = initializeSpecificEventHandler(includeHandler[i],RuntimeConstants.EVENTHANDLER_INCLUDE,IncludeEventHandler.class);
810 if (ev != null)
811 eventCartridge.addIncludeEventHandler((IncludeEventHandler) ev);
812 }
813 }
814
815 String[] invalidReferenceSet = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES);
816 if ( invalidReferenceSet != null )
817 {
818 for ( int i=0; i < invalidReferenceSet.length; i++ )
819 {
820 EventHandler ev = initializeSpecificEventHandler(invalidReferenceSet[i],RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES,InvalidReferenceEventHandler.class);
821 if (ev != null)
822 {
823 eventCartridge.addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev);
824 }
825 }
826 }
827
828
829 }
830
831 private EventHandler initializeSpecificEventHandler(String classname, String paramName, Class EventHandlerInterface)
832 {
833 if ( classname != null && classname.length() > 0)
834 {
835 Object o = null;
836 try
837 {
838 o = ClassUtils.getNewInstance(classname);
839 }
840 catch (ClassNotFoundException cnfe )
841 {
842 String err = "The specified class for "
843 + paramName + " (" + classname
844 + ") does not exist or is not accessible to the current classloader.";
845 log.error(err);
846 throw new VelocityException(err, cnfe);
847 }
848 catch (InstantiationException ie)
849 {
850 throw new VelocityException("Could not instantiate class '" + classname + "'", ie);
851 }
852 catch (IllegalAccessException ae)
853 {
854 throw new VelocityException("Cannot access class '" + classname + "'", ae);
855 }
856
857 if (!EventHandlerInterface.isAssignableFrom(EventHandlerInterface))
858 {
859 String err = "The specified class for " + paramName + " ("
860 + classname + ") does not implement "
861 + EventHandlerInterface.getName()
862 + "; Velocity is not initialized correctly.";
863
864 log.error(err);
865 throw new VelocityException(err);
866 }
867
868 EventHandler ev = (EventHandler) o;
869 if ( ev instanceof RuntimeServicesAware )
870 ((RuntimeServicesAware) ev).setRuntimeServices(this);
871 return ev;
872
873 } else
874 return null;
875 }
876
877 /**
878 * Initialize the Velocity logging system.
879 */
880 private void initializeLog()
881 {
882 // since the Log we started with was just placeholding,
883 // let's update it with the real LogChute settings.
884 try
885 {
886 LogManager.updateLog(this.log, this);
887 }
888 catch (Exception e)
889 {
890 throw new VelocityException("Error initializing log: " + e.getMessage(), e);
891 }
892 }
893
894
895 /**
896 * This methods initializes all the directives
897 * that are used by the Velocity Runtime. The
898 * directives to be initialized are listed in
899 * the RUNTIME_DEFAULT_DIRECTIVES properties
900 * file.
901 */
902 private void initializeDirectives()
903 {
904 Properties directiveProperties = new Properties();
905
906 /*
907 * Grab the properties file with the list of directives
908 * that we should initialize.
909 */
910
911 InputStream inputStream = null;
912
913 try
914 {
915 inputStream = getClass().getResourceAsStream('/' + DEFAULT_RUNTIME_DIRECTIVES);
916
917 if (inputStream == null)
918 {
919 throw new VelocityException("Error loading directive.properties! " +
920 "Something is very wrong if these properties " +
921 "aren't being located. Either your Velocity " +
922 "distribution is incomplete or your Velocity " +
923 "jar file is corrupted!");
924 }
925
926 directiveProperties.load(inputStream);
927
928 }
929 catch (IOException ioe)
930 {
931 String msg = "Error while loading directive properties!";
932 log.error(msg, ioe);
933 throw new RuntimeException(msg, ioe);
934 }
935 finally
936 {
937 try
938 {
939 if (inputStream != null)
940 {
941 inputStream.close();
942 }
943 }
944 catch (IOException ioe)
945 {
946 String msg = "Cannot close directive properties!";
947 log.error(msg, ioe);
948 throw new RuntimeException(msg, ioe);
949 }
950 }
951
952
953 /*
954 * Grab all the values of the properties. These
955 * are all class names for example:
956 *
957 * org.apache.velocity.runtime.directive.Foreach
958 */
959 Enumeration directiveClasses = directiveProperties.elements();
960
961 while (directiveClasses.hasMoreElements())
962 {
963 String directiveClass = (String) directiveClasses.nextElement();
964 loadDirective(directiveClass);
965 log.debug("Loaded System Directive: " + directiveClass);
966 }
967
968 /*
969 * now the user's directives
970 */
971
972 String[] userdirective = configuration.getStringArray("userdirective");
973
974 for( int i = 0; i < userdirective.length; i++)
975 {
976 loadDirective(userdirective[i]);
977 if (log.isDebugEnabled())
978 {
979 log.debug("Loaded User Directive: " + userdirective[i]);
980 }
981 }
982
983 }
984
985 /**
986 * Programatically add a directive.
987 * @param directive
988 */
989 public synchronized void addDirective(Directive directive)
990 {
991 runtimeDirectives.put(directive.getName(), directive);
992 updateSharedDirectivesMap();
993 }
994
995 /**
996 * Retrieve a previously instantiated directive.
997 * @param name name of the directive
998 * @return the {@link Directive} for that name
999 */
1000 public Directive getDirective(String name)
1001 {
1002 return (Directive) runtimeDirectivesShared.get(name);
1003 }
1004
1005 /**
1006 * Remove a directive.
1007 * @param name name of the directive.
1008 */
1009 public synchronized void removeDirective(String name)
1010 {
1011 runtimeDirectives.remove(name);
1012 updateSharedDirectivesMap();
1013 }
1014
1015 /**
1016 * Makes an unsynchronized copy of the directives map
1017 * that is used for Directive lookups by all parsers.
1018 *
1019 * This follows Copy-on-Write pattern. The cost of creating
1020 * a new map is acceptable since directives are typically
1021 * set and modified only during Velocity setup phase.
1022 */
1023 private void updateSharedDirectivesMap()
1024 {
1025 Map tmp = new HashMap(runtimeDirectives);
1026 runtimeDirectivesShared = tmp;
1027 }
1028
1029 /**
1030 * instantiates and loads the directive with some basic checks
1031 *
1032 * @param directiveClass classname of directive to load
1033 */
1034 public void loadDirective(String directiveClass)
1035 {
1036 try
1037 {
1038 Object o = ClassUtils.getNewInstance( directiveClass );
1039
1040 if (o instanceof Directive)
1041 {
1042 Directive directive = (Directive) o;
1043 addDirective(directive);
1044 }
1045 else
1046 {
1047 String msg = directiveClass + " does not implement "
1048 + Directive.class.getName() + "; it cannot be loaded.";
1049 log.error(msg);
1050 throw new VelocityException(msg);
1051 }
1052 }
1053 // The ugly threesome: ClassNotFoundException,
1054 // IllegalAccessException, InstantiationException.
1055 // Ignore Findbugs complaint for now.
1056 catch (Exception e)
1057 {
1058 String msg = "Failed to load Directive: " + directiveClass;
1059 log.error(msg, e);
1060 throw new VelocityException(msg, e);
1061 }
1062 }
1063
1064
1065 /**
1066 * Initializes the Velocity parser pool.
1067 */
1068 private void initializeParserPool()
1069 {
1070 /*
1071 * Which parser pool?
1072 */
1073 String pp = getString(RuntimeConstants.PARSER_POOL_CLASS);
1074
1075 if (pp != null && pp.length() > 0)
1076 {
1077 /*
1078 * if something was specified, then make one.
1079 * if that isn't a ParserPool, consider
1080 * this a huge error and throw
1081 */
1082
1083 Object o = null;
1084
1085 try
1086 {
1087 o = ClassUtils.getNewInstance( pp );
1088 }
1089 catch (ClassNotFoundException cnfe )
1090 {
1091 String err = "The specified class for ParserPool ("
1092 + pp
1093 + ") does not exist (or is not accessible to the current classloader.";
1094 log.error(err);
1095 throw new VelocityException(err, cnfe);
1096 }
1097 catch (InstantiationException ie)
1098 {
1099 throw new VelocityException("Could not instantiate class '" + pp + "'", ie);
1100 }
1101 catch (IllegalAccessException ae)
1102 {
1103 throw new VelocityException("Cannot access class '" + pp + "'", ae);
1104 }
1105
1106 if (!(o instanceof ParserPool))
1107 {
1108 String err = "The specified class for ParserPool ("
1109 + pp + ") does not implement " + ParserPool.class
1110 + " Velocity not initialized correctly.";
1111
1112 log.error(err);
1113 throw new VelocityException(err);
1114 }
1115
1116 parserPool = (ParserPool) o;
1117
1118 parserPool.initialize(this);
1119 }
1120 else
1121 {
1122 /*
1123 * someone screwed up. Lets not fool around...
1124 */
1125
1126 String err = "It appears that no class was specified as the"
1127 + " ParserPool. Please ensure that all configuration"
1128 + " information is correct.";
1129
1130 log.error(err);
1131 throw new VelocityException( err );
1132 }
1133
1134 }
1135
1136 /**
1137 * Returns a JavaCC generated Parser.
1138 *
1139 * @return Parser javacc generated parser
1140 */
1141 public Parser createNewParser()
1142 {
1143 requireInitialization();
1144
1145 Parser parser = new Parser(this);
1146 return parser;
1147 }
1148
1149 /**
1150 * Parse the input and return the root of
1151 * AST node structure.
1152 * <br><br>
1153 * In the event that it runs out of parsers in the
1154 * pool, it will create and let them be GC'd
1155 * dynamically, logging that it has to do that. This
1156 * is considered an exceptional condition. It is
1157 * expected that the user will set the
1158 * PARSER_POOL_SIZE property appropriately for their
1159 * application. We will revisit this.
1160 *
1161 * @param string String to be parsed
1162 * @param templateName name of the template being parsed
1163 * @return A root node representing the template as an AST tree.
1164 * @throws ParseException When the string could not be parsed as a template.
1165 * @since 1.6
1166 */
1167 public SimpleNode parse(String string, String templateName)
1168 throws ParseException
1169 {
1170 return parse(new StringReader(string), templateName);
1171 }
1172
1173 /**
1174 * Parse the input and return the root of
1175 * AST node structure.
1176 * <br><br>
1177 * In the event that it runs out of parsers in the
1178 * pool, it will create and let them be GC'd
1179 * dynamically, logging that it has to do that. This
1180 * is considered an exceptional condition. It is
1181 * expected that the user will set the
1182 * PARSER_POOL_SIZE property appropriately for their
1183 * application. We will revisit this.
1184 *
1185 * @param reader Reader retrieved by a resource loader
1186 * @param templateName name of the template being parsed
1187 * @return A root node representing the template as an AST tree.
1188 * @throws ParseException When the template could not be parsed.
1189 */
1190 public SimpleNode parse(Reader reader, String templateName)
1191 throws ParseException
1192 {
1193 /*
1194 * do it and dump the VM namespace for this template
1195 */
1196 return parse(reader, templateName, true);
1197 }
1198
1199 /**
1200 * Parse the input and return the root of the AST node structure.
1201 *
1202 * @param reader Reader retrieved by a resource loader
1203 * @param templateName name of the template being parsed
1204 * @param dumpNamespace flag to dump the Velocimacro namespace for this template
1205 * @return A root node representing the template as an AST tree.
1206 * @throws ParseException When the template could not be parsed.
1207 */
1208 public SimpleNode parse(Reader reader, String templateName, boolean dumpNamespace)
1209 throws ParseException
1210 {
1211 requireInitialization();
1212
1213 Parser parser = (Parser) parserPool.get();
1214 boolean keepParser = true;
1215 if (parser == null)
1216 {
1217 /*
1218 * if we couldn't get a parser from the pool make one and log it.
1219 */
1220 if (log.isInfoEnabled())
1221 {
1222 log.info("Runtime : ran out of parsers. Creating a new one. "
1223 + " Please increment the parser.pool.size property."
1224 + " The current value is too small.");
1225 }
1226 parser = createNewParser();
1227 keepParser = false;
1228 }
1229
1230 try
1231 {
1232 /*
1233 * dump namespace if we are told to. Generally, you want to
1234 * do this - you don't in special circumstances, such as
1235 * when a VM is getting init()-ed & parsed
1236 */
1237 if (dumpNamespace)
1238 {
1239 dumpVMNamespace(templateName);
1240 }
1241 return parser.parse(reader, templateName);
1242 }
1243 finally
1244 {
1245 if (keepParser)
1246 {
1247 parserPool.put(parser);
1248 }
1249
1250 }
1251 }
1252
1253 private void initializeEvaluateScopeSettings()
1254 {
1255 String property = evaluateScopeName+'.'+PROVIDE_SCOPE_CONTROL;
1256 provideEvaluateScope = getBoolean(property, provideEvaluateScope);
1257 }
1258
1259 /**
1260 * Renders the input string using the context into the output writer.
1261 * To be used when a template is dynamically constructed, or want to use
1262 * Velocity as a token replacer.
1263 *
1264 * @param context context to use in rendering input string
1265 * @param out Writer in which to render the output
1266 * @param logTag string to be used as the template name for log
1267 * messages in case of error
1268 * @param instring input string containing the VTL to be rendered
1269 *
1270 * @return true if successful, false otherwise. If false, see
1271 * Velocity runtime log
1272 * @throws ParseErrorException The template could not be parsed.
1273 * @throws MethodInvocationException A method on a context object could not be invoked.
1274 * @throws ResourceNotFoundException A referenced resource could not be loaded.
1275 * @since Velocity 1.6
1276 */
1277 public boolean evaluate(Context context, Writer out,
1278 String logTag, String instring)
1279 {
1280 return evaluate(context, out, logTag, new StringReader(instring));
1281 }
1282
1283 /**
1284 * Renders the input reader using the context into the output writer.
1285 * To be used when a template is dynamically constructed, or want to
1286 * use Velocity as a token replacer.
1287 *
1288 * @param context context to use in rendering input string
1289 * @param writer Writer in which to render the output
1290 * @param logTag string to be used as the template name for log messages
1291 * in case of error
1292 * @param reader Reader containing the VTL to be rendered
1293 *
1294 * @return true if successful, false otherwise. If false, see
1295 * Velocity runtime log
1296 * @throws ParseErrorException The template could not be parsed.
1297 * @throws MethodInvocationException A method on a context object could not be invoked.
1298 * @throws ResourceNotFoundException A referenced resource could not be loaded.
1299 * @since Velocity 1.6
1300 */
1301 public boolean evaluate(Context context, Writer writer,
1302 String logTag, Reader reader)
1303 {
1304 if (logTag == null)
1305 {
1306 throw new NullPointerException("logTag (i.e. template name) cannot be null, you must provide an identifier for the content being evaluated");
1307 }
1308
1309 SimpleNode nodeTree = null;
1310 try
1311 {
1312 nodeTree = parse(reader, logTag);
1313 }
1314 catch (ParseException pex)
1315 {
1316 throw new ParseErrorException(pex, null);
1317 }
1318 catch (TemplateInitException pex)
1319 {
1320 throw new ParseErrorException(pex, null);
1321 }
1322
1323 if (nodeTree == null)
1324 {
1325 return false;
1326 }
1327 else
1328 {
1329 return render(context, writer, logTag, nodeTree);
1330 }
1331 }
1332
1333
1334 /**
1335 * Initializes and renders the AST {@link SimpleNode} using the context
1336 * into the output writer.
1337 *
1338 * @param context context to use in rendering input string
1339 * @param writer Writer in which to render the output
1340 * @param logTag string to be used as the template name for log messages
1341 * in case of error
1342 * @param nodeTree SimpleNode which is the root of the AST to be rendered
1343 *
1344 * @return true if successful, false otherwise. If false, see
1345 * Velocity runtime log for errors
1346 * @throws ParseErrorException The template could not be parsed.
1347 * @throws MethodInvocationException A method on a context object could not be invoked.
1348 * @throws ResourceNotFoundException A referenced resource could not be loaded.
1349 * @since Velocity 1.6
1350 */
1351 public boolean render(Context context, Writer writer,
1352 String logTag, SimpleNode nodeTree)
1353 {
1354 /*
1355 * we want to init then render
1356 */
1357 InternalContextAdapterImpl ica =
1358 new InternalContextAdapterImpl(context);
1359
1360 ica.pushCurrentTemplateName(logTag);
1361
1362 try
1363 {
1364 try
1365 {
1366 nodeTree.init(ica, this);
1367 }
1368 catch (TemplateInitException pex)
1369 {
1370 throw new ParseErrorException(pex, null);
1371 }
1372 /**
1373 * pass through application level runtime exceptions
1374 */
1375 catch(RuntimeException e)
1376 {
1377 throw e;
1378 }
1379 catch(Exception e)
1380 {
1381 String msg = "RuntimeInstance.render(): init exception for tag = "+logTag;
1382 getLog().error(msg, e);
1383 throw new VelocityException(msg, e);
1384 }
1385
1386 try
1387 {
1388 if (provideEvaluateScope)
1389 {
1390 Object previous = ica.get(evaluateScopeName);
1391 context.put(evaluateScopeName, new Scope(this, previous));
1392 }
1393 nodeTree.render(ica, writer);
1394 }
1395 catch (StopCommand stop)
1396 {
1397 if (!stop.isFor(this))
1398 {
1399 throw stop;
1400 }
1401 else if (getLog().isDebugEnabled())
1402 {
1403 getLog().debug(stop.getMessage());
1404 }
1405 }
1406 catch (IOException e)
1407 {
1408 throw new VelocityException("IO Error in writer: " + e.getMessage(), e);
1409 }
1410 }
1411 finally
1412 {
1413 ica.popCurrentTemplateName();
1414 if (provideEvaluateScope)
1415 {
1416 Object obj = ica.get(evaluateScopeName);
1417 if (obj instanceof Scope)
1418 {
1419 Scope scope = (Scope)obj;
1420 if (scope.getParent() != null)
1421 {
1422 ica.put(evaluateScopeName, scope.getParent());
1423 }
1424 else if (scope.getReplaced() != null)
1425 {
1426 ica.put(evaluateScopeName, scope.getReplaced());
1427 }
1428 else
1429 {
1430 ica.remove(evaluateScopeName);
1431 }
1432 }
1433 }
1434 }
1435
1436 return true;
1437 }
1438
1439 /**
1440 * Invokes a currently registered Velocimacro with the params provided
1441 * and places the rendered stream into the writer.
1442 * <br>
1443 * Note : currently only accepts args to the VM if they are in the context.
1444 *
1445 * @param vmName name of Velocimacro to call
1446 * @param logTag string to be used for template name in case of error. if null,
1447 * the vmName will be used
1448 * @param params keys for args used to invoke Velocimacro, in java format
1449 * rather than VTL (eg "foo" or "bar" rather than "$foo" or "$bar")
1450 * @param context Context object containing data/objects used for rendering.
1451 * @param writer Writer for output stream
1452 * @return true if Velocimacro exists and successfully invoked, false otherwise.
1453 * @since 1.6
1454 */
1455 public boolean invokeVelocimacro(final String vmName, String logTag,
1456 String[] params, final Context context,
1457 final Writer writer)
1458 {
1459 /* check necessary parameters */
1460 if (vmName == null || context == null || writer == null)
1461 {
1462 String msg = "RuntimeInstance.invokeVelocimacro() : invalid call : vmName, context, and writer must not be null";
1463 getLog().error(msg);
1464 throw new NullPointerException(msg);
1465 }
1466
1467 /* handle easily corrected parameters */
1468 if (logTag == null)
1469 {
1470 logTag = vmName;
1471 }
1472 if (params == null)
1473 {
1474 params = new String[0];
1475 }
1476
1477 /* does the VM exist? */
1478 if (!isVelocimacro(vmName, logTag))
1479 {
1480 String msg = "RuntimeInstance.invokeVelocimacro() : VM '" + vmName
1481 + "' is not registered.";
1482 getLog().error(msg);
1483 throw new VelocityException(msg);
1484 }
1485
1486 /* now just create the VM call, and use evaluate */
1487 StringBuilder template = new StringBuilder("#");
1488 template.append(vmName);
1489 template.append("(");
1490 for( int i = 0; i < params.length; i++)
1491 {
1492 template.append(" $");
1493 template.append(params[i]);
1494 }
1495 template.append(" )");
1496
1497 return evaluate(context, writer, logTag, template.toString());
1498 }
1499
1500 /**
1501 * Retrieves and caches the configured default encoding
1502 * for better performance. (VELOCITY-606)
1503 */
1504 private String getDefaultEncoding()
1505 {
1506 if (encoding == null)
1507 {
1508 encoding = getString(INPUT_ENCODING, ENCODING_DEFAULT);
1509 }
1510 return encoding;
1511 }
1512
1513 /**
1514 * Returns a <code>Template</code> from the resource manager.
1515 * This method assumes that the character encoding of the
1516 * template is set by the <code>input.encoding</code>
1517 * property. The default is "ISO-8859-1"
1518 *
1519 * @param name The file name of the desired template.
1520 * @return The template.
1521 * @throws ResourceNotFoundException if template not found
1522 * from any available source.
1523 * @throws ParseErrorException if template cannot be parsed due
1524 * to syntax (or other) error.
1525 */
1526 public Template getTemplate(String name)
1527 throws ResourceNotFoundException, ParseErrorException
1528 {
1529 return getTemplate(name, getDefaultEncoding());
1530 }
1531
1532 /**
1533 * Returns a <code>Template</code> from the resource manager
1534 *
1535 * @param name The name of the desired template.
1536 * @param encoding Character encoding of the template
1537 * @return The template.
1538 * @throws ResourceNotFoundException if template not found
1539 * from any available source.
1540 * @throws ParseErrorException if template cannot be parsed due
1541 * to syntax (or other) error.
1542 */
1543 public Template getTemplate(String name, String encoding)
1544 throws ResourceNotFoundException, ParseErrorException
1545 {
1546 requireInitialization();
1547
1548 return (Template)
1549 resourceManager.getResource(name,
1550 ResourceManager.RESOURCE_TEMPLATE, encoding);
1551 }
1552
1553 /**
1554 * Returns a static content resource from the
1555 * resource manager. Uses the current value
1556 * if INPUT_ENCODING as the character encoding.
1557 *
1558 * @param name Name of content resource to get
1559 * @return parsed ContentResource object ready for use
1560 * @throws ResourceNotFoundException if template not found
1561 * from any available source.
1562 * @throws ParseErrorException When the template could not be parsed.
1563 */
1564 public ContentResource getContent(String name)
1565 throws ResourceNotFoundException, ParseErrorException
1566 {
1567 /*
1568 * the encoding is irrelvant as we don't do any converstion
1569 * the bytestream should be dumped to the output stream
1570 */
1571
1572 return getContent(name, getDefaultEncoding());
1573 }
1574
1575 /**
1576 * Returns a static content resource from the
1577 * resource manager.
1578 *
1579 * @param name Name of content resource to get
1580 * @param encoding Character encoding to use
1581 * @return parsed ContentResource object ready for use
1582 * @throws ResourceNotFoundException if template not found
1583 * from any available source.
1584 * @throws ParseErrorException When the template could not be parsed.
1585 */
1586 public ContentResource getContent(String name, String encoding)
1587 throws ResourceNotFoundException, ParseErrorException
1588 {
1589 requireInitialization();
1590
1591 return (ContentResource)
1592 resourceManager.getResource(name,
1593 ResourceManager.RESOURCE_CONTENT, encoding);
1594 }
1595
1596
1597 /**
1598 * Determines if a template exists and returns name of the loader that
1599 * provides it. This is a slightly less hokey way to support
1600 * the Velocity.resourceExists() utility method, which was broken
1601 * when per-template encoding was introduced. We can revisit this.
1602 *
1603 * @param resourceName Name of template or content resource
1604 * @return class name of loader than can provide it
1605 */
1606 public String getLoaderNameForResource(String resourceName)
1607 {
1608 requireInitialization();
1609
1610 return resourceManager.getLoaderNameForResource(resourceName);
1611 }
1612
1613 /**
1614 * Returns a convenient Log instance that wraps the current LogChute.
1615 * Use this to log error messages. It has the usual methods.
1616 *
1617 * @return A convenience Log instance that wraps the current LogChute.
1618 * @since 1.5
1619 */
1620 public Log getLog()
1621 {
1622 return log;
1623 }
1624
1625 /**
1626 * String property accessor method with default to hide the
1627 * configuration implementation.
1628 *
1629 * @param key property key
1630 * @param defaultValue default value to return if key not
1631 * found in resource manager.
1632 * @return value of key or default
1633 */
1634 public String getString( String key, String defaultValue)
1635 {
1636 return configuration.getString(key, defaultValue);
1637 }
1638
1639 /**
1640 * Returns the appropriate VelocimacroProxy object if vmName
1641 * is a valid current Velocimacro.
1642 *
1643 * @param vmName Name of velocimacro requested
1644 * @param templateName Name of the template that contains the velocimacro.
1645 * @return The requested VelocimacroProxy.
1646 * @since 1.6
1647 */
1648 public Directive getVelocimacro(String vmName, String templateName)
1649 {
1650 return vmFactory.getVelocimacro( vmName, templateName );
1651 }
1652
1653 /**
1654 * Returns the appropriate VelocimacroProxy object if vmName
1655 * is a valid current Velocimacro.
1656 *
1657 * @param vmName Name of velocimacro requested
1658 * @param templateName Name of the namespace.
1659 * @param renderingTemplate Name of the template we are currently rendering. This
1660 * information is needed when VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL setting is true
1661 * and template contains a macro with the same name as the global macro library.
1662 *
1663 * @since Velocity 1.6
1664 *
1665 * @return VelocimacroProxy
1666 */
1667 public Directive getVelocimacro(String vmName, String templateName, String renderingTemplate)
1668 {
1669 return vmFactory.getVelocimacro( vmName, templateName, renderingTemplate );
1670 }
1671
1672 /**
1673 * Return a list of VelocimacroProxies that are defined by the given
1674 * template name.
1675 */
1676 public List<VelocimacroProxy> getVelocimacros(String templateName)
1677 {
1678 return vmFactory.getVelocimacros(templateName);
1679 }
1680
1681
1682 /**
1683 * Adds a new Velocimacro. Usually called by Macro only while parsing.
1684 *
1685 * Called by org.apache.velocity.runtime.directive.processAndRegister
1686 *
1687 * @param name Name of velocimacro
1688 * @param macro root AST node of the parsed macro
1689 * @param macroArgs Array of macro arguments, containing the
1690 * #macro() arguments and default values. the 0th is the name.
1691 * @param sourceTemplate
1692 *
1693 * @since Velocity 1.6
1694 *
1695 * @return boolean True if added, false if rejected for some
1696 * reason (either parameters or permission settings)
1697 */
1698 public boolean addVelocimacro( String name,
1699 Node macro,
1700 List<Macro.MacroArg> macroArgs,
1701 String sourceTemplate )
1702 {
1703 return vmFactory.addVelocimacro(name.intern(), macro, macroArgs, sourceTemplate);
1704 }
1705
1706
1707 /**
1708 * Checks to see if a VM exists
1709 *
1710 * @param vmName Name of the Velocimacro.
1711 * @param templateName Template on which to look for the Macro.
1712 * @return True if VM by that name exists, false if not
1713 */
1714 public boolean isVelocimacro( String vmName, String templateName )
1715 {
1716 return vmFactory.isVelocimacro(vmName.intern(), templateName);
1717 }
1718
1719 /**
1720 * tells the vmFactory to dump the specified namespace. This is to support
1721 * clearing the VM list when in inline-VM-local-scope mode
1722 * @param namespace Namespace to dump.
1723 * @return True if namespace was dumped successfully.
1724 */
1725 public boolean dumpVMNamespace(String namespace)
1726 {
1727 return vmFactory.dumpVMNamespace( namespace );
1728 }
1729
1730 /* --------------------------------------------------------------------
1731 * R U N T I M E A C C E S S O R M E T H O D S
1732 * --------------------------------------------------------------------
1733 * These are the getXXX() methods that are a simple wrapper
1734 * around the configuration object. This is an attempt
1735 * to make a the Velocity Runtime the single access point
1736 * for all things Velocity, and allow the Runtime to
1737 * adhere as closely as possible the the Mediator pattern
1738 * which is the ultimate goal.
1739 * --------------------------------------------------------------------
1740 */
1741
1742 /**
1743 * String property accessor method to hide the configuration implementation
1744 * @param key property key
1745 * @return value of key or null
1746 */
1747 public String getString(String key)
1748 {
1749 return StringUtils.nullTrim(configuration.getString(key));
1750 }
1751
1752 /**
1753 * Int property accessor method to hide the configuration implementation.
1754 *
1755 * @param key Property key
1756 * @return value
1757 */
1758 public int getInt(String key)
1759 {
1760 return configuration.getInt(key);
1761 }
1762
1763 /**
1764 * Int property accessor method to hide the configuration implementation.
1765 *
1766 * @param key property key
1767 * @param defaultValue The default value.
1768 * @return value
1769 */
1770 public int getInt(String key, int defaultValue)
1771 {
1772 return configuration.getInt(key, defaultValue);
1773 }
1774
1775 /**
1776 * Boolean property accessor method to hide the configuration implementation.
1777 *
1778 * @param key property key
1779 * @param def The default value if property not found.
1780 * @return value of key or default value
1781 */
1782 public boolean getBoolean(String key, boolean def)
1783 {
1784 return configuration.getBoolean(key, def);
1785 }
1786
1787 /**
1788 * Return the velocity runtime configuration object.
1789 *
1790 * @return Configuration object which houses the Velocity runtime
1791 * properties.
1792 */
1793 public ExtendedProperties getConfiguration()
1794 {
1795 return configuration;
1796 }
1797
1798 /**
1799 * Return the Introspector for this instance
1800 * @return The Introspector for this instance
1801 */
1802 public Introspector getIntrospector()
1803 {
1804 return introspector;
1805 }
1806
1807 /**
1808 * Returns the event handlers for the application.
1809 * @return The event handlers for the application.
1810 * @since 1.5
1811 */
1812 public EventCartridge getApplicationEventCartridge()
1813 {
1814 return eventCartridge;
1815 }
1816
1817
1818 /**
1819 * Gets the application attribute for the given key
1820 *
1821 * @param key
1822 * @return The application attribute for the given key.
1823 */
1824 public Object getApplicationAttribute(Object key)
1825 {
1826 return applicationAttributes.get(key);
1827 }
1828
1829 /**
1830 * Sets the application attribute for the given key
1831 *
1832 * @param key
1833 * @param o The new application attribute.
1834 * @return The old value of this attribute or null if it hasn't been set before.
1835 */
1836 public Object setApplicationAttribute(Object key, Object o)
1837 {
1838 return applicationAttributes.put(key, o);
1839 }
1840
1841 /**
1842 * Returns the Uberspect object for this Instance.
1843 *
1844 * @return The Uberspect object for this Instance.
1845 */
1846 public Uberspect getUberspect()
1847 {
1848 return uberSpect;
1849 }
1850
1851 }