aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-core/src/main/java/org/apache/velocity/app
diff options
context:
space:
mode:
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/app')
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java185
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java387
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java431
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java445
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java31
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java279
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java52
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java88
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java52
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java53
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java59
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java59
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java163
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java52
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java58
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java124
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java72
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java64
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java121
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java193
20 files changed, 2968 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java
new file mode 100644
index 00000000..3428d1c6
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java
@@ -0,0 +1,185 @@
+package org.apache.velocity.app;
+
+/*
+ * 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 org.apache.velocity.util.ClassUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>
+ * This is a small utility class allow easy access to static fields in a class,
+ * such as string constants. Velocity will not introspect for class
+ * fields (and won't in the future :), but writing setter/getter methods to do
+ * this really is a pain, so use this if you really have
+ * to access fields.
+ *
+ * <p>
+ * The idea it so enable access to the fields just like you would in Java.
+ * For example, in Java, you would access a static field like
+ * <blockquote><pre>
+ * MyClass.STRING_CONSTANT
+ * </pre></blockquote>
+ * and that is the same thing we are trying to allow here.
+ *
+ * <p>
+ * So to use in your Java code, do something like this :
+ * <blockquote><pre>
+ * context.put("runtime", new FieldMethodizer( "org.apache.velocity.runtime.Runtime" ));
+ * </pre></blockquote>
+ * and then in your template, you can access any of your static fields in this way :
+ * <blockquote><pre>
+ * $runtime.COUNTER_NAME
+ * </pre></blockquote>
+ *
+ * <p>
+ * Right now, this class only methodizes <code>public static</code> fields. It seems
+ * that anything else is too dangerous. This class is for convenience accessing
+ * 'constants'. If you have fields that aren't <code>static</code> it may be better
+ * to handle them by explicitly placing them into the context.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class FieldMethodizer
+{
+ /** Hold the field objects by field name */
+ private Map<String, Field> fieldHash = new HashMap<>();
+ private Logger logger = LoggerFactory.getLogger(FieldMethodizer.class);
+
+ /**
+ * Allow object to be initialized without any data. You would use
+ * addObject() to add data later.
+ */
+ public FieldMethodizer()
+ {
+ }
+
+ /**
+ * Constructor that takes as it's arg the name of the class
+ * to methodize.
+ *
+ * @param s Name of class to methodize.
+ */
+ public FieldMethodizer( String s )
+ {
+ try
+ {
+ addObject(s);
+ }
+ catch( Exception e )
+ {
+ logger.error("[FieldMethodizer] Could not add {} for field methodizing", s, e);
+ }
+ }
+
+ /**
+ * Constructor that takes as it's arg a living
+ * object to methodize. Note that it will still
+ * only methodized the public static fields of
+ * the class.
+ *
+ * @param o Name of class to methodize.
+ */
+ public FieldMethodizer( Object o )
+ {
+ try
+ {
+ addObject(o);
+ }
+ catch( Exception e )
+ {
+ logger.error("[FieldMethodizer] Could not add {} for field methodizing", o, e);
+ }
+ }
+
+ /**
+ * Add the Name of the class to methodize
+ * @param s
+ * @throws Exception
+ */
+ public void addObject ( String s )
+ throws Exception
+ {
+ inspect(ClassUtils.getClass(s));
+ }
+
+ /**
+ * Add an Object to methodize
+ * @param o
+ * @throws Exception
+ */
+ public void addObject ( Object o )
+ throws Exception
+ {
+ inspect(o.getClass());
+ }
+
+ /**
+ * Accessor method to get the fields by name.
+ *
+ * @param fieldName Name of static field to retrieve
+ *
+ * @return The value of the given field.
+ */
+ public Object get( String fieldName )
+ {
+ Object value = null;
+ try
+ {
+ Field f = fieldHash.get( fieldName );
+ if (f != null)
+ {
+ value = f.get(null);
+ }
+ }
+ catch( IllegalAccessException e )
+ {
+ System.err.println("IllegalAccessException while trying to access " + fieldName
+ + ": " + e.getMessage());
+ }
+ return value;
+ }
+
+ /**
+ * Method that retrieves all public static fields
+ * in the class we are methodizing.
+ */
+ private void inspect(Class<?> clas)
+ {
+ Field[] fields = clas.getFields();
+ for (Field field : fields)
+ {
+ /*
+ * only if public and static
+ */
+ int mod = field.getModifiers();
+ if (Modifier.isStatic(mod) && Modifier.isPublic(mod))
+ {
+ fieldHash.put(field.getName(), field);
+ }
+ }
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java
new file mode 100644
index 00000000..351ecf45
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java
@@ -0,0 +1,387 @@
+package org.apache.velocity.app;
+
+/*
+ * 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 org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.slf4j.Logger;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Properties;
+
+/**
+ * This class provides services to the application
+ * developer, such as:
+ * <ul>
+ * <li> Simple Velocity Runtime engine initialization methods.
+ * <li> Functions to apply the template engine to streams and strings
+ * to allow embedding and dynamic template generation.
+ * <li> Methods to access Velocimacros directly.
+ * </ul>
+ *
+ * <br><br>
+ * While the most common way to use Velocity is via templates, as
+ * Velocity is a general-purpose template engine, there are other
+ * uses that Velocity is well suited for, such as processing dynamically
+ * created templates, or processing content streams.
+ *
+ * <br><br>
+ * The methods herein were developed to allow easy access to the Velocity
+ * facilities without direct spelunking of the internals. If there is
+ * something you feel is necessary to add here, please, send a patch.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class Velocity implements RuntimeConstants
+{
+ /**
+ * initialize the Velocity runtime engine, using the default
+ * properties of the Velocity distribution
+ */
+ public static void init()
+ {
+ RuntimeSingleton.init();
+ }
+
+ /**
+ * Resets the instance, so Velocity can be re-initialized again.
+ *
+ * @since 2.0.0
+ */
+ public static void reset()
+ {
+ RuntimeSingleton.reset();
+ }
+
+ /**
+ * initialize the Velocity runtime engine, using default properties
+ * plus the properties in the properties file passed in as the arg
+ *
+ * @param propsFilename file containing properties to use to initialize
+ * the Velocity runtime
+ */
+ public static void init( String propsFilename )
+ {
+ RuntimeSingleton.init(propsFilename);
+ }
+
+ /**
+ * initialize the Velocity runtime engine, using default properties
+ * plus the properties in the passed in java.util.Properties object
+ *
+ * @param p Properties object containing initialization properties
+ */
+ public static void init( Properties p )
+ {
+ RuntimeSingleton.init( p );
+ }
+
+ /**
+ * Set a Velocity Runtime property.
+ *
+ * @param key The property key.
+ * @param value The property value.
+ */
+ public static void setProperty(String key, Object value)
+ {
+ RuntimeSingleton.setProperty(key, value);
+ }
+
+ /**
+ * Add a Velocity Runtime property.
+ *
+ * @param key The property key.
+ * @param value The property value.
+ */
+ public static void addProperty(String key, Object value)
+ {
+ RuntimeSingleton.addProperty(key, value);
+ }
+
+ /**
+ * Clear a Velocity Runtime property.
+ *
+ * @param key of property to clear
+ */
+ public static void clearProperty(String key)
+ {
+ RuntimeSingleton.clearProperty(key);
+ }
+
+ /**
+ * Set an entire configuration at once.
+ *
+ * @param configuration A configuration object.
+ * @since 2.0
+ *
+ */
+ public static void setProperties( Properties configuration)
+ {
+ RuntimeSingleton.setProperties(configuration);
+ }
+
+ /**
+ * Get a Velocity Runtime property.
+ *
+ * @param key property to retrieve
+ * @return property value or null if the property
+ * not currently set
+ */
+ public static Object getProperty( String key )
+ {
+ return RuntimeSingleton.getProperty( key );
+ }
+
+ /**
+ * renders the input string using the context into the output writer.
+ * To be used when a template is dynamically constructed, or want to use
+ * Velocity as a token replacer.
+ *
+ * @param context context to use in rendering input string
+ * @param out Writer in which to render the output
+ * @param logTag string to be used as the template name for log
+ * messages in case of error
+ * @param instring input string containing the VTL to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see
+ * Velocity runtime log
+ * @throws ParseErrorException The template could not be parsed.
+ * @throws MethodInvocationException A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException A referenced resource could not be loaded.
+ */
+ public static boolean evaluate( Context context, Writer out,
+ String logTag, String instring )
+ throws ParseErrorException, MethodInvocationException,
+ ResourceNotFoundException
+ {
+ return RuntimeSingleton.getRuntimeServices()
+ .evaluate(context, out, logTag, instring);
+ }
+
+ /**
+ * Renders the input reader using the context into the output writer.
+ * To be used when a template is dynamically constructed, or want to
+ * use Velocity as a token replacer.
+ *
+ * @param context context to use in rendering input string
+ * @param writer Writer in which to render the output
+ * @param logTag string to be used as the template name for log messages
+ * in case of error
+ * @param reader Reader containing the VTL to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see
+ * Velocity runtime log
+ * @throws ParseErrorException The template could not be parsed.
+ * @throws MethodInvocationException A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException A referenced resource could not be loaded.
+ * @since Velocity v1.1
+ */
+ public static boolean evaluate( Context context, Writer writer,
+ String logTag, Reader reader )
+ throws ParseErrorException, MethodInvocationException,
+ ResourceNotFoundException
+ {
+ return RuntimeSingleton.getRuntimeServices().evaluate(context, writer,
+ logTag, reader);
+ }
+
+ /**
+ * Invokes a currently registered Velocimacro with the params provided
+ * and places the rendered stream into the writer.
+ * <br>
+ * Note : currently only accepts args to the VM if they are in the context.
+ *
+ * @param vmName name of Velocimacro to call
+ * @param logTag string to be used for template name in case of error. if null,
+ * the vmName will be used
+ * @param params keys for args used to invoke Velocimacro, in java format
+ * rather than VTL (eg "foo" or "bar" rather than "$foo" or "$bar")
+ * @param context Context object containing data/objects used for rendering.
+ * @param writer Writer for output stream
+ * @return true if Velocimacro exists and successfully invoked, false otherwise.
+ */
+ public static boolean invokeVelocimacro( String vmName, String logTag,
+ String params[], Context context,
+ Writer writer )
+ {
+ return RuntimeSingleton.getRuntimeServices()
+ .invokeVelocimacro(vmName, logTag, params, context, writer);
+ }
+
+ /**
+ * Merges a template and puts the rendered stream into the writer
+ *
+ * @param templateName name of template to be used in merge
+ * @param encoding encoding used in template
+ * @param context filled context to be used in merge
+ * @param writer writer to write template into
+ *
+ * @return true if successful, false otherwise. Errors
+ * logged to velocity log
+ *
+ * @throws ParseErrorException The template could not be parsed.
+ * @throws MethodInvocationException A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException A referenced resource could not be loaded.
+ *
+ * @since Velocity v1.1
+ */
+ public static boolean mergeTemplate( String templateName, String encoding,
+ Context context, Writer writer )
+ throws ResourceNotFoundException, ParseErrorException, MethodInvocationException
+ {
+ Template template = RuntimeSingleton.getTemplate(templateName, encoding);
+
+ if ( template == null )
+ {
+ String msg = "Velocity.mergeTemplate() was unable to load template '"
+ + templateName + "'";
+ getLog().error(msg);
+ throw new ResourceNotFoundException(msg);
+ }
+ else
+ {
+ template.merge(context, writer);
+ return true;
+ }
+ }
+
+ /**
+ * Returns a <code>Template</code> from the Velocity
+ * resource management system.
+ *
+ * @param name The file name of the desired template.
+ * @return The template.
+ * @throws ResourceNotFoundException if template not found
+ * from any available source.
+ * @throws ParseErrorException if template cannot be parsed due
+ * to syntax (or other) error.
+ */
+ public static Template getTemplate(String name)
+ throws ResourceNotFoundException, ParseErrorException
+ {
+ return RuntimeSingleton.getTemplate( name );
+ }
+
+ /**
+ * Returns a <code>Template</code> from the Velocity
+ * resource management system.
+ *
+ * @param name The file name of the desired template.
+ * @param encoding The character encoding to use for the template.
+ * @return The template.
+ * @throws ResourceNotFoundException if template not found
+ * from any available source.
+ * @throws ParseErrorException if template cannot be parsed due
+ * to syntax (or other) error.
+ *
+ * @since Velocity v1.1
+ */
+ public static Template getTemplate(String name, String encoding)
+ throws ResourceNotFoundException, ParseErrorException
+ {
+ return RuntimeSingleton.getTemplate( name, encoding );
+ }
+
+ /**
+ * <p>Determines whether a resource is accessible via the
+ * currently configured resource loaders. {@link
+ * org.apache.velocity.runtime.resource.Resource} is the generic
+ * description of templates, static content, etc.</p>
+ *
+ * <p>Note that the current implementation will <b>not</b> change
+ * the state of the system in any real way - so this cannot be
+ * used to pre-load the resource cache, as the previous
+ * implementation did as a side-effect.</p>
+ *
+ * @param resourceName The name of the resource to search for.
+ * @return Whether the resource was located.
+ */
+ public static boolean resourceExists(String resourceName)
+ {
+ return (RuntimeSingleton.getLoaderNameForResource(resourceName) != null);
+ }
+
+ /**
+ * Returns the current logger.
+ *
+ * @return A convenience Logger instance.
+ * @since 1.5
+ */
+ public static Logger getLog()
+ {
+ return RuntimeSingleton.getLog();
+ }
+
+ /**
+ * <p>
+ * Set the an ApplicationAttribue, which is an Object
+ * set by the application which is accessible from
+ * any component of the system that gets a RuntimeServices.
+ * This allows communication between the application
+ * environment and custom pluggable components of the
+ * Velocity engine, such as loaders and loggers.
+ * </p>
+ *
+ * <p>
+ * Note that there is no enforcement or rules for the key
+ * used - it is up to the application developer. However, to
+ * help make the intermixing of components possible, using
+ * the target Class name (e.g. com.foo.bar ) as the key
+ * might help avoid collision.
+ * </p>
+ *
+ * @param key object 'name' under which the object is stored
+ * @param value object to store under this key
+ */
+ public static void setApplicationAttribute( Object key, Object value )
+ {
+ RuntimeSingleton.getRuntimeServices().setApplicationAttribute( key, value);
+ }
+
+
+ /**
+ * Remove a directive.
+ *
+ * @param name name of the directive.
+ */
+ public void removeDirective(String name)
+ {
+ RuntimeSingleton.removeDirective(name);
+ }
+
+ /**
+ * Instantiates and loads the directive with some basic checks.
+ *
+ * @param directiveClass classname of directive to load
+ */
+ public void loadDirective(String directiveClass)
+ {
+ RuntimeSingleton.loadDirective(directiveClass);
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java
new file mode 100644
index 00000000..93751f3a
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java
@@ -0,0 +1,431 @@
+package org.apache.velocity.app;
+
+/*
+ * 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 org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.slf4j.Logger;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Properties;
+
+/**
+ * <p>
+ * This class provides a separate new-able instance of the
+ * Velocity template engine. The alternative model for use
+ * is using the Velocity class which employs the singleton
+ * model.
+ * </p>
+ * <p>Velocity will call
+ * the parameter-less init() at the first use of this class
+ * if the init() wasn't explicitly called. While this will
+ * ensure that Velocity functions, it probably won't
+ * function in the way you intend, so it is strongly recommended that
+ * you call an init() method yourself if you use the default constructor.
+ * </p>
+ *
+ * @version $Id$
+ */
+public class VelocityEngine implements RuntimeConstants
+{
+ private RuntimeInstance ri = new RuntimeInstance();
+
+ /**
+ * Init-less CTOR
+ */
+ public VelocityEngine()
+ {
+ // do nothing
+ }
+
+ /**
+ * Construct a VelocityEngine with the initial properties defined in the file
+ * propsFilename
+ * @param propsFilename properties filename
+ */
+ public VelocityEngine(String propsFilename)
+ {
+ ri.setProperties(propsFilename);
+ }
+
+ /**
+ * Construct a VelocityEngine instance with the specified initial properties.
+ * @param p properties
+ */
+ public VelocityEngine(Properties p)
+ {
+ ri.setProperties(p);
+ }
+
+ /**
+ * initialize the Velocity runtime engine, using the default
+ * properties of the Velocity distribution
+ */
+ public void init()
+ {
+ ri.init();
+ }
+
+ /**
+ * Resets the instance, so Velocity can be re-initialized again.
+ *
+ * @since 2.0.0
+ */
+ public void reset()
+ {
+ ri.reset();
+ }
+
+ /**
+ * initialize the Velocity runtime engine, using default properties
+ * plus the properties in the properties file passed in as the arg
+ *
+ * @param propsFilename file containing properties to use to initialize
+ * the Velocity runtime
+ */
+ public void init(String propsFilename)
+ {
+ ri.init(propsFilename);
+ }
+
+ /**
+ * initialize the Velocity runtime engine, using default properties
+ * plus the properties in the passed in java.util.Properties object
+ *
+ * @param p Properties object containing initialization properties
+ */
+ public void init(Properties p)
+ {
+ ri.init(p);
+ }
+
+ /**
+ * Set a Velocity Runtime property.
+ *
+ * @param key
+ * @param value
+ */
+ public void setProperty(String key, Object value)
+ {
+ ri.setProperty(key, value);
+ }
+
+ /**
+ * Add a Velocity Runtime property.
+ *
+ * @param key
+ * @param value
+ */
+ public void addProperty(String key, Object value)
+ {
+ ri.addProperty(key, value);
+ }
+
+ /**
+ * Clear a Velocity Runtime property.
+ *
+ * @param key of property to clear
+ */
+ public void clearProperty(String key)
+ {
+ ri.clearProperty(key);
+ }
+
+ /**
+ * Set an entire configuration at once from a Properties configuration
+ *
+ * @param configuration
+ * @since 2.0
+ */
+ public void setProperties(Properties configuration)
+ {
+ ri.setProperties( configuration );
+ }
+
+ /**
+ * Set an entire configuration at once from a named properties file
+ *
+ * @param propsFilename properties filename
+ * @since 2.1
+ */
+ public void setProperties(String propsFilename)
+ {
+ ri.setProperties(propsFilename);
+ }
+
+ /**
+ * Get a Velocity Runtime property.
+ *
+ * @param key property to retrieve
+ * @return property value or null if the property
+ * not currently set
+ */
+ public Object getProperty( String key )
+ {
+ return ri.getProperty( key );
+ }
+
+ /**
+ * renders the input string using the context into the output writer.
+ * To be used when a template is dynamically constructed, or want to use
+ * Velocity as a token replacer.
+ *
+ * @param context context to use in rendering input string
+ * @param out Writer in which to render the output
+ * @param logTag string to be used as the template name for log
+ * messages in case of error
+ * @param instring input string containing the VTL to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see
+ * Velocity runtime log
+ * @throws ParseErrorException The template could not be parsed.
+ * @throws MethodInvocationException A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException A referenced resource could not be loaded.
+ */
+ public boolean evaluate( Context context, Writer out,
+ String logTag, String instring )
+ throws ParseErrorException, MethodInvocationException,
+ ResourceNotFoundException
+ {
+ return ri.evaluate(context, out, logTag, instring);
+ }
+
+ /**
+ * Renders the input reader using the context into the output writer.
+ * To be used when a template is dynamically constructed, or want to
+ * use Velocity as a token replacer.
+ *
+ * @param context context to use in rendering input string
+ * @param writer Writer in which to render the output
+ * @param logTag string to be used as the template name for log messages
+ * in case of error
+ * @param reader Reader containing the VTL to be rendered
+ *
+ * @return true if successful, false otherwise. If false, see
+ * Velocity runtime log
+ * @throws ParseErrorException The template could not be parsed.
+ * @throws MethodInvocationException A method on a context object could not be invoked.
+ * @throws ResourceNotFoundException A referenced resource could not be loaded.
+ * @since Velocity v1.1
+ */
+ public boolean evaluate(Context context, Writer writer,
+ String logTag, Reader reader)
+ throws ParseErrorException, MethodInvocationException,
+ ResourceNotFoundException
+ {
+ return ri.evaluate(context, writer, logTag, reader);
+ }
+
+
+ /**
+ * Invokes a currently registered Velocimacro with the params provided
+ * and places the rendered stream into the writer.
+ * <br>
+ * Note : currently only accepts args to the VM if they are in the context.
+ *
+ * @param vmName name of Velocimacro to call
+ * @param logTag string to be used for template name in case of error. if null,
+ * the vmName will be used
+ * @param params keys for args used to invoke Velocimacro, in java format
+ * rather than VTL (eg "foo" or "bar" rather than "$foo" or "$bar")
+ * @param context Context object containing data/objects used for rendering.
+ * @param writer Writer for output stream
+ * @return true if Velocimacro exists and successfully invoked, false otherwise.
+ */
+ public boolean invokeVelocimacro( String vmName, String logTag,
+ String params[], Context context,
+ Writer writer )
+ {
+ return ri.invokeVelocimacro(vmName, logTag, params, context, writer);
+ }
+
+ /**
+ * merges a template and puts the rendered stream into the writer
+ *
+ * @param templateName name of template to be used in merge
+ * @param encoding encoding used in template
+ * @param context filled context to be used in merge
+ * @param writer writer to write template into
+ *
+ * @return true if successful, false otherwise. Errors
+ * logged to velocity log
+ * @throws ResourceNotFoundException
+ * @throws ParseErrorException
+ * @throws MethodInvocationException
+ * @since Velocity v1.1
+ */
+ public boolean mergeTemplate( String templateName, String encoding,
+ Context context, Writer writer )
+ throws ResourceNotFoundException, ParseErrorException, MethodInvocationException
+ {
+ Template template = ri.getTemplate(templateName, encoding);
+
+ if ( template == null )
+ {
+ String msg = "VelocityEngine.mergeTemplate() was unable to load template '"
+ + templateName + "'";
+ getLog().error(msg);
+ throw new ResourceNotFoundException(msg);
+ }
+ else
+ {
+ template.merge(context, writer);
+ return true;
+ }
+ }
+
+ /**
+ * Returns a <code>Template</code> from the Velocity
+ * resource management system.
+ *
+ * @param name The file name of the desired template.
+ * @return The template.
+ * @throws ResourceNotFoundException if template not found
+ * from any available source.
+ * @throws ParseErrorException if template cannot be parsed due
+ * to syntax (or other) error.
+ */
+ public Template getTemplate(String name)
+ throws ResourceNotFoundException, ParseErrorException
+ {
+ return ri.getTemplate( name );
+ }
+
+ /**
+ * Returns a <code>Template</code> from the Velocity
+ * resource management system.
+ *
+ * @param name The file name of the desired template.
+ * @param encoding The character encoding to use for the template.
+ * @return The template.
+ * @throws ResourceNotFoundException if template not found
+ * from any available source.
+ * @throws ParseErrorException if template cannot be parsed due
+ * to syntax (or other) error.
+ * @since Velocity v1.1
+ */
+ public Template getTemplate(String name, String encoding)
+ throws ResourceNotFoundException, ParseErrorException
+ {
+ return ri.getTemplate( name, encoding );
+ }
+
+ /**
+ * Determines if a resource is accessible via the currently
+ * configured resource loaders.
+ * <br><br>
+ * Note that the current implementation will <b>not</b>
+ * change the state of the system in any real way - so this
+ * cannot be used to pre-load the resource cache, as the
+ * previous implementation did as a side-effect.
+ * <br><br>
+ * The previous implementation exhibited extreme laziness and
+ * sloth, and the author has been flogged.
+ *
+ * @param resourceName name of the resource to search for
+ * @return true if found, false otherwise
+ * @since 1.5
+ */
+ public boolean resourceExists(String resourceName)
+ {
+ return (ri.getLoaderNameForResource(resourceName) != null);
+ }
+
+ /**
+ * Returns the current slf4j logger. Its namespace defaults to <code>org.apache.velocity</code>
+ * if it hasn't been configured using the property <code>runtime.log.name</code> or the property
+ * <code>runtime.log.instance</code>.
+ * @return A Logger object.
+ * @since 1.5
+ */
+ public Logger getLog()
+ {
+ return ri.getLog();
+ }
+
+ /**
+ * <p>
+ * Sets an application attribute (which can be any Object) that will be
+ * accessible from any component of the system that gets a
+ * RuntimeServices. This allows communication between the application
+ * environment and custom pluggable components of the Velocity engine,
+ * such as ResourceLoaders and Loggers.
+ * </p>
+ *
+ * <p>
+ * Note that there is no enforcement or rules for the key
+ * used - it is up to the application developer. However, to
+ * help make the intermixing of components possible, using
+ * the target Class name (e.g. com.foo.bar ) as the key
+ * might help avoid collision.
+ * </p>
+ *
+ * @param key object 'name' under which the object is stored
+ * @param value object to store under this key
+ */
+ public void setApplicationAttribute( Object key, Object value )
+ {
+ ri.setApplicationAttribute(key, value);
+ }
+
+ /**
+ * <p>
+ * Return an application attribute (which can be any Object)
+ * that was set by the application in order to be accessible from
+ * any component of the system that gets a RuntimeServices.
+ * This allows communication between the application
+ * environment and custom pluggable components of the
+ * Velocity engine, such as ResourceLoaders and Loggers.
+ * </p>
+ *
+ * @param key object 'name' under which the object is stored
+ * @return value object to store under this key
+ * @since 1.5
+ */
+ public Object getApplicationAttribute( Object key )
+ {
+ return ri.getApplicationAttribute(key);
+ }
+
+ /**
+ * Remove a directive.
+ * @param name name of the directive.
+ */
+ public void removeDirective(String name)
+ {
+ ri.removeDirective(name);
+ }
+
+ /**
+ * Instantiates and loads the directive with some basic checks.
+ *
+ * @param directiveClass classname of directive to load
+ */
+ public void loadDirective(String directiveClass)
+ {
+ ri.loadDirective(directiveClass);
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java
new file mode 100644
index 00000000..1bbb49f3
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java
@@ -0,0 +1,445 @@
+package org.apache.velocity.app.event;
+
+/*
+ * 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 org.apache.velocity.context.Context;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.context.InternalEventContext;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.RuntimeServicesAware;
+import org.apache.velocity.util.introspection.Info;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * <p>Stores the event handlers. Event handlers can be assigned on a per
+ * VelocityEngine instance basis by specifying the class names in the
+ * velocity.properties file. Event handlers may also be assigned on a per-page
+ * basis by creating a new instance of EventCartridge, adding the event
+ * handlers, and then calling attachToContext. For clarity, it's recommended
+ * that one approach or the other be followed, as the second method is primarily
+ * presented for backwards compatibility.</p>
+ * <p>Note that Event Handlers follow a filter pattern, with multiple event
+ * handlers allowed for each event. When the appropriate event occurs, all the
+ * appropriate event handlers are called in the sequence they were added to the
+ * Event Cartridge. See the javadocs of the specific event handler interfaces
+ * for more details.</p>
+ *
+ * @author <a href="mailto:wglass@wglass@forio.com">Will Glass-Husain </a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr. </a>
+ * @author <a href="mailto:j_a_fernandez@yahoo.com">Jose Alberto Fernandez </a>
+ * @version $Id$
+ */
+public class EventCartridge
+{
+ private List<ReferenceInsertionEventHandler> referenceHandlers = new ArrayList<>();
+ private MethodExceptionEventHandler methodExceptionHandler = null;
+ private List<IncludeEventHandler> includeHandlers = new ArrayList<>();
+ private List<InvalidReferenceEventHandler> invalidReferenceHandlers = new ArrayList<>();
+
+ /**
+ * Ensure that handlers are not initialized more than once.
+ */
+ Set<EventHandler> initializedHandlers = new HashSet<>();
+
+ protected RuntimeServices rsvc = null;
+
+ protected Logger getLog()
+ {
+ return rsvc == null ? LoggerFactory.getLogger(EventCartridge.class) : rsvc.getLog();
+ }
+
+ /**
+ * runtime services setter, called during initialization
+ *
+ * @param rs runtime services
+ * @since 2.0
+ */
+ public synchronized void setRuntimeServices(RuntimeServices rs)
+ {
+ if (rsvc == null)
+ {
+ rsvc = rs;
+ /* allow for this method to be called *after* adding event handlers */
+ for (EventHandler handler : referenceHandlers)
+ {
+ if (handler instanceof RuntimeServicesAware && !initializedHandlers.contains(handler))
+ {
+ ((RuntimeServicesAware) handler).setRuntimeServices(rs);
+ initializedHandlers.add(handler);
+ }
+ }
+ if (methodExceptionHandler != null &&
+ methodExceptionHandler instanceof RuntimeServicesAware &&
+ !initializedHandlers.contains(methodExceptionHandler))
+ {
+ ((RuntimeServicesAware) methodExceptionHandler).setRuntimeServices(rs);
+ initializedHandlers.add(methodExceptionHandler);
+ }
+ for (EventHandler handler : includeHandlers)
+ {
+ if (handler instanceof RuntimeServicesAware && !initializedHandlers.contains(handler))
+ {
+ ((RuntimeServicesAware) handler).setRuntimeServices(rs);
+ initializedHandlers.add(handler);
+ }
+ }
+ for (EventHandler handler : invalidReferenceHandlers)
+ {
+ if (handler instanceof RuntimeServicesAware && !initializedHandlers.contains(handler))
+ {
+ ((RuntimeServicesAware) handler).setRuntimeServices(rs);
+ initializedHandlers.add(handler);
+ }
+ }
+ }
+ else if (rsvc != rs)
+ {
+ throw new VelocityException("an event cartridge cannot be used by several different runtime services instances");
+ }
+ }
+
+ /**
+ * Adds an event handler(s) to the Cartridge. This method
+ * will find all possible event handler interfaces supported
+ * by the passed in object.
+ *
+ * @param ev object implementing a valid EventHandler-derived interface
+ * @return true if a supported interface, false otherwise or if null
+ */
+ public boolean addEventHandler(EventHandler ev)
+ {
+ if (ev == null)
+ {
+ return false;
+ }
+
+ boolean found = false;
+
+ if (ev instanceof ReferenceInsertionEventHandler)
+ {
+ addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev);
+ found = true;
+ }
+
+ if (ev instanceof MethodExceptionEventHandler)
+ {
+ addMethodExceptionHandler((MethodExceptionEventHandler) ev);
+ found = true;
+ }
+
+ if (ev instanceof IncludeEventHandler)
+ {
+ addIncludeEventHandler((IncludeEventHandler) ev);
+ found = true;
+ }
+
+ if (ev instanceof InvalidReferenceEventHandler)
+ {
+ addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev);
+ found = true;
+ }
+
+ if (found && rsvc != null && ev instanceof RuntimeServicesAware && !initializedHandlers.contains(ev))
+ {
+ ((RuntimeServicesAware) ev).setRuntimeServices(rsvc);
+ initializedHandlers.add(ev);
+ }
+
+ return found;
+ }
+
+ /**
+ * Add a reference insertion event handler to the Cartridge.
+ *
+ * @param ev ReferenceInsertionEventHandler
+ * @since 1.5
+ */
+ public void addReferenceInsertionEventHandler(ReferenceInsertionEventHandler ev)
+ {
+ referenceHandlers.add(ev);
+ }
+
+ /**
+ * Add a method exception event handler to the Cartridge.
+ *
+ * @param ev MethodExceptionEventHandler
+ * @since 1.5
+ */
+ public void addMethodExceptionHandler(MethodExceptionEventHandler ev)
+ {
+ if (methodExceptionHandler == null)
+ {
+ methodExceptionHandler = ev;
+ }
+ else
+ {
+ getLog().warn("ignoring extra method exception handler");
+ }
+ }
+
+ /**
+ * Add an include event handler to the Cartridge.
+ *
+ * @param ev IncludeEventHandler
+ * @since 1.5
+ */
+ public void addIncludeEventHandler(IncludeEventHandler ev)
+ {
+ includeHandlers.add(ev);
+ }
+
+ /**
+ * Add an invalid reference event handler to the Cartridge.
+ *
+ * @param ev InvalidReferenceEventHandler
+ * @since 1.5
+ */
+ public void addInvalidReferenceEventHandler(InvalidReferenceEventHandler ev)
+ {
+ invalidReferenceHandlers.add(ev);
+ }
+
+
+ /**
+ * Removes an event handler(s) from the Cartridge. This method will find all
+ * possible event handler interfaces supported by the passed in object and
+ * remove them.
+ *
+ * @param ev object impementing a valid EventHandler-derived interface
+ * @return true if event handler was previously registered, false if not
+ * found
+ */
+ public boolean removeEventHandler(EventHandler ev)
+ {
+ if (ev == null)
+ {
+ return false;
+ }
+
+ if (ev instanceof ReferenceInsertionEventHandler)
+ {
+ return referenceHandlers.remove(ev);
+ }
+
+ if (ev instanceof MethodExceptionEventHandler)
+ {
+ if (ev == methodExceptionHandler)
+ {
+ methodExceptionHandler = null;
+ return true;
+ }
+ }
+
+ if (ev instanceof IncludeEventHandler)
+ {
+ return includeHandlers.remove(ev);
+ }
+
+ if (ev instanceof InvalidReferenceEventHandler)
+ {
+ return invalidReferenceHandlers.remove(ev);
+ }
+
+ return false;
+ }
+
+ /**
+ * Call reference insertion handlers
+ * @param context
+ * @param reference
+ * @param value
+ * @return value returned by handlers
+ * @since 2.0
+ */
+ public Object referenceInsert(InternalContextAdapter context, String reference, Object value)
+ {
+ for (ReferenceInsertionEventHandler handler : referenceHandlers)
+ {
+ value = handler.referenceInsert(context, reference, value);
+ }
+ return value;
+ }
+
+ /**
+ * Check whether this event cartridge has a method exception event handler
+ *
+ * @return true if a method exception event handler has been registered
+ * @since 2.0
+ */
+ boolean hasMethodExceptionEventHandler()
+ {
+ return methodExceptionHandler != null;
+ }
+
+ /**
+ * Call method exception event handler
+ * @param context
+ * @param claz
+ * @param method
+ * @param e exception
+ * @param info template name, line and column infos
+ * @return value returned by handler
+ * @since 2.0
+ */
+ public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info)
+ {
+ if (methodExceptionHandler != null)
+ {
+ return methodExceptionHandler.methodException(context, claz, method, e, info);
+ }
+ return null;
+ }
+
+ /**
+ * Call include event handlers
+ *
+ * @param context
+ * @param includeResourcePath
+ * @param currentResourcePath
+ * @param directiveName
+ * @return include path
+ * @since 2.0
+ */
+ public String includeEvent(Context context, String includeResourcePath, String currentResourcePath, String directiveName)
+ {
+ for (IncludeEventHandler handler : includeHandlers)
+ {
+ includeResourcePath = handler.includeEvent(context, includeResourcePath, currentResourcePath, directiveName);
+ /* reflect 1.x behavior: exit after at least one execution whenever a null include path has been found */
+ if (includeResourcePath == null)
+ {
+ break;
+ }
+ }
+ return includeResourcePath;
+ }
+
+ /**
+ * Call invalid reference handlers for an invalid getter
+ *
+ * @param context
+ * @param reference
+ * @param object
+ * @param property
+ * @param info
+ * @return value returned by handlers
+ * @since 2.0
+ */
+ public Object invalidGetMethod(Context context, String reference, Object object, String property, Info info)
+ {
+ Object result = null;
+ for (InvalidReferenceEventHandler handler : invalidReferenceHandlers)
+ {
+ result = handler.invalidGetMethod(context, reference, object, property, info);
+ /* reflect 1.x behavior: exit after at least one execution whenever a non-null value has been found */
+ if (result != null)
+ {
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Call invalid reference handlers for an invalid setter
+ *
+ * @param context
+ * @param leftreference
+ * @param rightreference
+ * @param info
+ * @return whether to stop further chaining in the next cartridge
+ * @since 2.0
+ */
+ public boolean invalidSetMethod(Context context, String leftreference, String rightreference, Info info)
+ {
+ for (InvalidReferenceEventHandler handler : invalidReferenceHandlers)
+ {
+ if (handler.invalidSetMethod(context, leftreference, rightreference, info))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Call invalid reference handlers for an invalid method call
+ *
+ * @param context
+ * @param reference
+ * @param object
+ * @param method
+ * @param info
+ * @return value returned by handlers
+ * @since 2.0
+ */
+ public Object invalidMethod(Context context, String reference, Object object, String method, Info info)
+ {
+ Object result = null;
+ for (InvalidReferenceEventHandler handler : invalidReferenceHandlers)
+ {
+ result = handler.invalidMethod(context, reference, object, method, info);
+ /* reflect 1.x behavior: exit after at least one execution whenever a non-null value has been found */
+ if (result != null)
+ {
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Attached the EventCartridge to the context
+ *
+ * Final because not something one should mess with lightly :)
+ *
+ * @param context context to attach to
+ * @return true if successful, false otherwise
+ */
+ public final boolean attachToContext(Context context)
+ {
+ if (context instanceof InternalEventContext)
+ {
+ InternalEventContext iec = (InternalEventContext) context;
+
+ iec.attachEventCartridge(this);
+
+ /*
+ * while it's tempting to call setContext on each handler from here,
+ * this needs to be done before each method call. This is
+ * because the specific context will change as inner contexts
+ * are linked in through macros, foreach, or directly by the user.
+ */
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java
new file mode 100644
index 00000000..9adbb075
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java
@@ -0,0 +1,31 @@
+package org.apache.velocity.app.event;
+
+/*
+ * 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.
+ */
+
+/**
+ * Base interface for all event handlers
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface EventHandler
+{
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java
new file mode 100644
index 00000000..e5390020
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java
@@ -0,0 +1,279 @@
+package org.apache.velocity.app.event;
+
+/*
+ * 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 org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.introspection.Info;
+
+
+/**
+ * Calls on request all registered event handlers for a particular event. Each
+ * method accepts two event cartridges (typically one from the application and
+ * one from the context). All appropriate event handlers are executed in order
+ * until a stopping condition is met. See the docs for the individual methods to
+ * see what the stopping condition is for that method.
+ *
+ * @author <a href="mailto:wglass@wglass@forio.com">Will Glass-Husain </a>
+ * @version $Id$
+ * @since 1.5
+ */
+public class EventHandlerUtil {
+
+
+ /**
+ * Called before a reference is inserted. All event handlers are called in
+ * sequence. The default implementation inserts the reference as is.
+ *
+ * This is a major hotspot method called by ASTReference render.
+ *
+ * @param reference reference from template about to be inserted
+ * @param value value about to be inserted (after toString() )
+ * @param rsvc current instance of RuntimeServices
+ * @param context The internal context adapter.
+ * @return Object on which toString() should be called for output.
+ */
+ public static Object referenceInsert(RuntimeServices rsvc,
+ InternalContextAdapter context, String reference, Object value)
+ {
+ try
+ {
+ value = rsvc.getApplicationEventCartridge().referenceInsert(context, reference, value);
+ EventCartridge contextCartridge = context.getEventCartridge();
+ if (contextCartridge != null)
+ {
+ contextCartridge.setRuntimeServices(rsvc);
+ value = contextCartridge.referenceInsert(context, reference, value);
+ }
+ return value;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new VelocityException("Exception in event handler.",e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+
+ /**
+ * Called when a method exception is generated during Velocity merge. Only
+ * the first valid event handler in the sequence is called. The default
+ * implementation simply rethrows the exception.
+ *
+ * @param claz
+ * Class that is causing the exception
+ * @param method
+ * method called that causes the exception
+ * @param e
+ * Exception thrown by the method
+ * @param rsvc current instance of RuntimeServices
+ * @param context The internal context adapter.
+ * @param info exception location informations
+ * @return Object to return as method result
+ * @throws Exception
+ * to be wrapped and propagated to app
+ */
+ public static Object methodException(RuntimeServices rsvc,
+ InternalContextAdapter context, Class<?> claz, String method,
+ Exception e, Info info) throws Exception
+ {
+ try
+ {
+ EventCartridge ev = rsvc.getApplicationEventCartridge();
+ if (ev.hasMethodExceptionEventHandler())
+ {
+ return ev.methodException(context, claz, method, e, info);
+ }
+ EventCartridge contextCartridge = context.getEventCartridge();
+ if (contextCartridge != null)
+ {
+ contextCartridge.setRuntimeServices(rsvc);
+ return contextCartridge.methodException(context, claz, method, e, info);
+ }
+ }
+ catch (RuntimeException re)
+ {
+ throw re;
+ }
+ catch (Exception ex)
+ {
+ throw new VelocityException("Exception in event handler.", ex, rsvc.getLogContext().getStackTrace());
+ }
+
+ /* default behaviour is to re-throw exception */
+ throw e;
+ }
+
+ /**
+ * Called when an include-type directive is encountered (#include or
+ * #parse). All the registered event handlers are called unless null is
+ * returned. The default implementation always processes the included
+ * resource.
+ *
+ * @param includeResourcePath
+ * the path as given in the include directive.
+ * @param currentResourcePath
+ * the path of the currently rendering template that includes the
+ * include directive.
+ * @param directiveName
+ * name of the directive used to include the resource. (With the
+ * standard directives this is either "parse" or "include").
+ * @param rsvc current instance of RuntimeServices
+ * @param context The internal context adapter.
+ *
+ * @return a new resource path for the directive, or null to block the
+ * include from occurring.
+ */
+ public static String includeEvent(RuntimeServices rsvc,
+ InternalContextAdapter context, String includeResourcePath,
+ String currentResourcePath, String directiveName)
+ {
+ try
+ {
+ includeResourcePath = rsvc.getApplicationEventCartridge().includeEvent(context, includeResourcePath, currentResourcePath, directiveName);
+ EventCartridge contextCartridge = context.getEventCartridge();
+ if (contextCartridge != null)
+ {
+ contextCartridge.setRuntimeServices(rsvc);
+ includeResourcePath = contextCartridge.includeEvent(context, includeResourcePath, currentResourcePath, directiveName);
+ }
+ return includeResourcePath;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+
+
+ /**
+ * Called when an invalid get method is encountered.
+ *
+ * @param rsvc current instance of RuntimeServices
+ * @param context the context when the reference was found invalid
+ * @param reference complete invalid reference
+ * @param object object from reference, or null if not available
+ * @param property name of property, or null if not relevant
+ * @param info contains info on template, line, col
+ * @return substitute return value for missing reference, or null if no substitute
+ */
+ public static Object invalidGetMethod(RuntimeServices rsvc,
+ InternalContextAdapter context, String reference,
+ Object object, String property, Info info)
+ {
+ try
+ {
+ Object result = rsvc.getApplicationEventCartridge().invalidGetMethod(context, reference, object, property, info);
+ EventCartridge contextCartridge = context.getEventCartridge();
+ if (contextCartridge != null)
+ {
+ contextCartridge.setRuntimeServices(rsvc);
+ result = contextCartridge.invalidGetMethod(context, reference, object, property, info);
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+
+ /**
+ * Called when an invalid set method is encountered.
+ *
+ * @param rsvc current instance of RuntimeServices
+ * @param context the context when the reference was found invalid
+ * @param leftreference left reference being assigned to
+ * @param rightreference invalid reference on the right
+ * @param info contains info on template, line, col
+ */
+ public static void invalidSetMethod(RuntimeServices rsvc,
+ InternalContextAdapter context, String leftreference,
+ String rightreference, Info info)
+ {
+ try
+ {
+ if (!rsvc.getApplicationEventCartridge().invalidSetMethod(context, leftreference, rightreference, info))
+ {
+ EventCartridge contextCartridge = context.getEventCartridge();
+ if (contextCartridge != null)
+ {
+ contextCartridge.setRuntimeServices(rsvc);
+ contextCartridge.invalidSetMethod(context, leftreference, rightreference, info);
+ }
+ }
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+
+ /**
+ * Called when an invalid method is encountered.
+ *
+ * @param rsvc current instance of RuntimeServices
+ * @param context the context when the reference was found invalid
+ * @param reference complete invalid reference
+ * @param object object from reference, or null if not available
+ * @param method name of method, or null if not relevant
+ * @param info contains info on template, line, col
+ * @return substitute return value for missing reference, or null if no substitute
+ */
+ public static Object invalidMethod(RuntimeServices rsvc,
+ InternalContextAdapter context, String reference,
+ Object object, String method, Info info)
+ {
+ try
+ {
+ Object result = rsvc.getApplicationEventCartridge().invalidMethod(context, reference, object, method, info);
+ EventCartridge contextCartridge = context.getEventCartridge();
+ if (contextCartridge != null)
+ {
+ contextCartridge.setRuntimeServices(rsvc);
+ result = contextCartridge.invalidMethod(context, reference, object, method, info);
+ }
+ return result;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java
new file mode 100644
index 00000000..30726696
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java
@@ -0,0 +1,52 @@
+package org.apache.velocity.app.event;
+
+import org.apache.velocity.context.Context;
+
+/*
+ * 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.
+ */
+
+/**
+ * Event handler for include type directives (e.g. <code>#include()</code>, <code>#parse()</code>)
+ * Allows the developer to modify the path of the resource returned.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ * @since 1.5
+ */
+public interface IncludeEventHandler extends EventHandler
+{
+ /**
+ * Called when an include-type directive is encountered (
+ * <code>#include</code> or <code>#parse</code>). May modify the path
+ * of the resource to be included or may block the include entirely. All the
+ * registered IncludeEventHandlers are called unless null is returned. If
+ * none are registered the template at the includeResourcePath is retrieved.
+ *
+ * @param context current context
+ * @param includeResourcePath the path as given in the include directive.
+ * @param currentResourcePath the path of the currently rendering template that includes the
+ * include directive.
+ * @param directiveName name of the directive used to include the resource. (With the
+ * standard directives this is either "parse" or "include").
+ *
+ * @return a new resource path for the directive, or null to block the
+ * include from occurring.
+ */
+ String includeEvent(Context context, String includeResourcePath, String currentResourcePath, String directiveName);
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java
new file mode 100644
index 00000000..f02e09b6
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java
@@ -0,0 +1,88 @@
+package org.apache.velocity.app.event;
+
+/*
+ * 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 org.apache.velocity.context.Context;
+import org.apache.velocity.util.introspection.Info;
+
+/**
+ * Event handler called when an invalid reference is encountered. Allows
+ * the application to report errors or substitute return values. May be chained
+ * in sequence; the behavior will differ per method.
+ *
+ * <p>This feature should be regarded as experimental.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ * @since 1.5
+ */
+public interface InvalidReferenceEventHandler extends EventHandler
+{
+
+ /**
+ * Called when object is null or there is no getter for the given
+ * property. Also called for invalid references without properties.
+ * invalidGetMethod() will be called in sequence for
+ * each link in the chain until the first non-null value is
+ * returned.
+ *
+ * @param context the context when the reference was found invalid
+ * @param reference string with complete invalid reference
+ * @param object the object referred to, or null if not found
+ * @param property the property name from the reference
+ * @param info contains template, line, column details
+ * @return substitute return value for missing reference, or null if no substitute
+ */
+ Object invalidGetMethod(Context context, String reference,
+ Object object, String property, Info info);
+
+ /**
+ * Called when object is null or there is no setter for the given
+ * property. invalidSetMethod() will be called in sequence for
+ * each link in the chain until a true value is returned. It's
+ * recommended that false be returned as a default to allow
+ * for easy chaining.
+ *
+ * @param context the context when the reference was found invalid
+ * @param leftreference left reference being assigned to
+ * @param rightreference invalid reference on the right
+ * @param info contains info on template, line, col
+ *
+ * @return if true then stop calling invalidSetMethod along the
+ * chain.
+ */
+ boolean invalidSetMethod(Context context, String leftreference,
+ String rightreference, Info info);
+
+ /**
+ * Called when object is null or the given method does not exist.
+ * invalidMethod() will be called in sequence for each link in
+ * the chain until the first non-null value is returned.
+ *
+ * @param context the context when the reference was found invalid
+ * @param reference string with complete invalid reference
+ * @param object the object referred to, or null if not found
+ * @param method the name of the (non-existent) method
+ * @param info contains template, line, column details
+ * @return substitute return value for missing reference, or null if no substitute
+ */
+ Object invalidMethod(Context context, String reference,
+ Object object, String method, Info info);
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java
new file mode 100644
index 00000000..9661a964
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java
@@ -0,0 +1,52 @@
+package org.apache.velocity.app.event;
+
+import org.apache.velocity.context.Context;
+import org.apache.velocity.util.introspection.Info;
+
+/*
+ * 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.
+ */
+
+/**
+ * Event handler called when a method throws an exception. This gives the
+ * application a chance to deal with it and either
+ * return something nice, or throw.
+ *
+ * Please return what you want rendered into the output stream.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface MethodExceptionEventHandler extends EventHandler
+{
+ /**
+ * Called when a method throws an exception.
+ * Only the first registered MethodExceptionEventHandler is called. If
+ * none are registered a MethodInvocationException is thrown.
+ *
+ * @param context current context
+ * @param claz the class of the object the method is being applied to
+ * @param method the method
+ * @param e the thrown exception
+ * @param info contains template, line, column details
+ * @return an object to insert in the page
+ * @throws RuntimeException an exception to be thrown instead inserting an object
+ */
+ Object methodException(Context context, Class<?> claz, String method, Exception e, Info info);
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java
new file mode 100644
index 00000000..a9e1d660
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java
@@ -0,0 +1,53 @@
+package org.apache.velocity.app.event;
+
+/*
+ * 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 org.apache.velocity.context.Context;
+
+/**
+ * Reference 'Stream insertion' event handler. Called with object
+ * that will be inserted into stream via value.toString().
+ *
+ * Please return an Object that will toString() nicely :)
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface ReferenceInsertionEventHandler extends EventHandler
+{
+ /**
+ * A call-back which is executed during Velocity merge before a reference
+ * value is inserted into the output stream. All registered
+ * ReferenceInsertionEventHandlers are called in sequence. If no
+ * ReferenceInsertionEventHandlers are are registered then reference value
+ * is inserted into the output stream as is.
+ *
+ *
+ * @param context current context
+ * @param reference Reference from template about to be inserted.
+ * @param value Value about to be inserted (after its <code>toString()</code>
+ * method is called).
+ * @return Object on which <code>toString()</code> should be called for
+ * output.
+ */
+ Object referenceInsert(Context context, String reference, Object value);
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java
new file mode 100644
index 00000000..4bdcff25
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java
@@ -0,0 +1,59 @@
+package org.apache.velocity.app.event.implement;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/*
+ * 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.
+ */
+
+/**
+ * <p>Escape all HTML entities.</p>
+ * <p>Warning: escaping references this way, without knowing if they land inside plain text, inside an attribute value or elsewhere, is not usable in production.</p>
+ *
+ * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeHtml4%28java.lang.String%29">StringEscapeUtils</a>
+ * @author wglass
+ * @since 1.5
+ * @deprecated impractical use
+ */
+@Deprecated
+public class EscapeHtmlReference extends EscapeReference
+{
+
+ /**
+ * Escape all HTML entities.
+ *
+ * @param text
+ * @return An escaped String.
+ * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeHtml4%28java.lang.String%29">StringEscapeUtils</a>
+ */
+ @Override
+ protected String escape(Object text)
+ {
+ return StringEscapeUtils.escapeHtml4(text.toString());
+ }
+
+ /**
+ * @return attribute "eventhandler.escape.html.match"
+ */
+ @Override
+ protected String getMatchAttribute()
+ {
+ return "eventhandler.escape.html.match";
+ }
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java
new file mode 100644
index 00000000..f6468743
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java
@@ -0,0 +1,59 @@
+package org.apache.velocity.app.event.implement;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/*
+ * 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.
+ */
+
+/**
+ * <p>Escapes the characters in a String to be suitable for use in JavaScript.</p>
+ * <p>Warning: escaping references this way, without knowing if they land inside or outside Javascript simple-quoted or double-quoted strings, is not usable in production.</p>
+ *
+ * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeEcmaScript%28java.lang.String%29">StringEscapeUtils</a>
+ * @author wglass
+ * @since 1.5
+ * @deprecated impractical use
+ */
+@Deprecated
+public class EscapeJavaScriptReference extends EscapeReference
+{
+
+ /**
+ * Escapes the characters in a String to be suitable for use in JavaScript.
+ *
+ * @param text
+ * @return An escaped String.
+ * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeEcmaScript%28java.lang.String%29">StringEscapeUtils</a>
+ */
+ @Override
+ protected String escape(Object text)
+ {
+ return StringEscapeUtils.escapeEcmaScript(text.toString());
+ }
+
+ /**
+ * @return attribute "eventhandler.escape.javascript.match"
+ */
+ @Override
+ protected String getMatchAttribute()
+ {
+ return "eventhandler.escape.javascript.match";
+ }
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java
new file mode 100644
index 00000000..17738a04
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java
@@ -0,0 +1,163 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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 org.apache.velocity.app.event.ReferenceInsertionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.RuntimeServicesAware;
+
+import org.apache.commons.lang3.StringUtils;
+
+import org.slf4j.Logger;
+
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Base class for escaping references. To use it, override the following methods:
+ * <DL>
+ * <DT><code>String escape(String text)</code></DT>
+ * <DD>escape the provided text</DD>
+ * <DT><code>String getMatchAttribute()</code></DT>
+ * <DD>retrieve the configuration attribute used to match references (see below)</DD>
+ * </DL>
+ *
+ * <P>By default, all references are escaped. However, by setting the match attribute
+ * in the configuration file to a regular expression, users can specify which references
+ * to escape. For example the following configuration property tells the EscapeSqlReference
+ * event handler to only escape references that start with "sql".
+ * (e.g. <code>$sql</code>, <code>$sql.toString(),</code>, etc).
+ *
+ * <PRE>
+ * <CODE>eventhandler.escape.sql.match = sql.*</CODE>
+ * </PRE>
+ *
+ * Regular expressions should follow the format used by the Java language. More info in the
+ * <a href="http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">Pattern class documentation</a>.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain </a>
+ * @version $Id$
+ * @since 1.5
+ */
+public abstract class EscapeReference implements ReferenceInsertionEventHandler,RuntimeServicesAware {
+
+ private RuntimeServices rs;
+
+ private String matchRegExp = null;
+
+ protected Logger log;
+
+ /**
+ * Escape the given text. Override this in a subclass to do the actual
+ * escaping.
+ *
+ * @param text the text to escape
+ * @return the escaped text
+ */
+ protected abstract String escape(Object text);
+
+ /**
+ * Specify the configuration attribute that specifies the
+ * regular expression. Ideally should be in a form
+ * <pre><code>eventhandler.escape.XYZ.match</code></pre>
+ *
+ * <p>where <code>XYZ</code> is the type of escaping being done.
+ * @return configuration attribute
+ */
+ protected abstract String getMatchAttribute();
+
+ /**
+ * Escape the provided text if it matches the configured regular expression.
+ *
+ * @param reference
+ * @param value
+ * @return Escaped text.
+ */
+ @Override
+ public Object referenceInsert(Context context, String reference, Object value)
+ {
+ if(value == null)
+ {
+ return value;
+ }
+
+ if (matchRegExp == null)
+ {
+ return escape(value);
+ }
+
+ else if (reference.matches(matchRegExp))
+ {
+ return escape(value);
+ }
+
+ else
+ {
+ return value;
+ }
+ }
+
+ /**
+ * Called automatically when event cartridge is initialized.
+ *
+ * @param rs instance of RuntimeServices
+ */
+ @Override
+ public void setRuntimeServices(RuntimeServices rs)
+ {
+ this.rs = rs;
+ log = rs.getLog("event");
+
+ // Get the regular expression pattern.
+ matchRegExp = StringUtils.trim(rs.getString(getMatchAttribute()));
+ if (org.apache.commons.lang3.StringUtils.isEmpty(matchRegExp))
+ {
+ matchRegExp = null;
+ }
+
+ // Test the regular expression for a well formed pattern
+ if (matchRegExp != null)
+ {
+ try
+ {
+ "".matches(matchRegExp);
+ }
+ catch (PatternSyntaxException E)
+ {
+ log.error("Invalid regular expression '{}'. No escaping will be performed.",
+ matchRegExp, E);
+ matchRegExp = null;
+ }
+ }
+
+ }
+
+ /**
+ * Retrieve a reference to RuntimeServices. Use this for checking additional
+ * configuration properties.
+ *
+ * @return The current runtime services object.
+ */
+ protected RuntimeServices getRuntimeServices()
+ {
+ return rs;
+ }
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java
new file mode 100644
index 00000000..46be4c69
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java
@@ -0,0 +1,52 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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.
+ */
+
+/**
+ * Escapes the characters in a String to be suitable to pass to an SQL query.
+ *
+ * @author wglass
+ * @since 1.5
+ */
+public class EscapeSqlReference extends EscapeReference
+{
+
+ /**
+ * Escapes the characters in a String to be suitable to pass to an SQL query.
+ *
+ * @param text
+ * @return An escaped string.
+ */
+ @Override
+ protected String escape(Object text)
+ {
+ return text.toString().replaceAll("'", "''");
+ }
+
+ /**
+ * @return attribute "eventhandler.escape.sql.match"
+ */
+ @Override
+ protected String getMatchAttribute()
+ {
+ return "eventhandler.escape.sql.match";
+ }
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java
new file mode 100644
index 00000000..0905c513
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java
@@ -0,0 +1,58 @@
+package org.apache.velocity.app.event.implement;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/*
+ * 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.
+ */
+
+/**
+ * <p>Escape all XML entities, suitable for placing the output inside an XML (1.0) text node or attribute value.</p>
+ * <p>Warning: escaping references this way, without knowing if they land inside plain text, inside an attribute value or elsewhere, is not usable in production.</p>
+ *
+ * @see <a href="http://jakarta.apache.org/commons/lang/api/org/apache/commons/lang/StringEscapeUtils.html#escapeSql(java.lang.String)">StringEscapeUtils</a>
+ * @author wglass
+ * @since 1.5
+ * @deprecated impractical use
+ */
+public class EscapeXmlReference extends EscapeReference
+{
+
+ /**
+ * Escape all XML entities.
+ *
+ * @param text
+ * @return An escaped String.
+ * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeXml10%28java.lang.String%29">StringEscapeUtils</a>
+ */
+ @Override
+ protected String escape(Object text)
+ {
+ return StringEscapeUtils.escapeXml10(text.toString());
+ }
+
+ /**
+ * @return attribute "eventhandler.escape.xml.match"
+ */
+ @Override
+ protected String getMatchAttribute()
+ {
+ return "eventhandler.escape.xml.match";
+ }
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java
new file mode 100644
index 00000000..40fcf5d6
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java
@@ -0,0 +1,124 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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 org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.ContextAware;
+import org.apache.velocity.util.RuntimeServicesAware;
+
+import org.apache.commons.lang3.StringUtils;
+
+import org.slf4j.Logger;
+
+/**
+ * Simple event handler that checks to see if an included page is available.
+ * If not, it includes a designated replacement page instead.
+ *
+ * <P>By default, the name of the replacement page is "notfound.vm", however this
+ * page name can be changed by setting the Velocity property
+ * <code>eventhandler.include.notfound</code>, for example:</p>
+ * <pre><code>
+ * eventhandler.include.notfound = error.vm
+ * </code></pre>
+ * <p>The name of the missing resource is put into the Velocity context, under the
+ * key "missingResource", so that the "notfound" template can report the missing
+ * resource with a Velocity reference, like:
+ * <code>$missingResource</code>
+ * </p>
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ * @since 1.5
+ */
+public class IncludeNotFound implements IncludeEventHandler, RuntimeServicesAware, ContextAware {
+
+ private static final String DEFAULT_NOT_FOUND = "notfound.vm";
+ private static final String PROPERTY_NOT_FOUND = "eventhandler.include.notfound";
+ private RuntimeServices rs = null;
+ String notfound;
+ Context context;
+
+ protected Logger log;
+
+ /**
+ * Check to see if included file exists, and display "not found" page if it
+ * doesn't. If "not found" page does not exist, log an error and return
+ * null.
+ *
+ * @param includeResourcePath
+ * @param currentResourcePath
+ * @param directiveName
+ * @return message.
+ */
+ @Override
+ public String includeEvent(
+ Context context,
+ String includeResourcePath,
+ String currentResourcePath,
+ String directiveName)
+ {
+
+ /*
+ * check to see if page exists
+ */
+ boolean exists = (rs.getLoaderNameForResource(includeResourcePath) != null);
+ if (!exists)
+ {
+ context.put("missingResource", includeResourcePath);
+ if (rs.getLoaderNameForResource(notfound) != null)
+ {
+ return notfound;
+ }
+ else
+ {
+ /*
+ * can't find not found, so display nothing
+ */
+ log.error("Can't find include not found page: {}", notfound);
+ return null;
+ }
+ }
+ else
+ return includeResourcePath;
+ }
+
+
+ /**
+ * @see org.apache.velocity.util.RuntimeServicesAware#setRuntimeServices(org.apache.velocity.runtime.RuntimeServices)
+ */
+ @Override
+ public void setRuntimeServices(RuntimeServices rs)
+ {
+ this.rs = rs;
+ log = rs.getLog("event");
+ notfound = StringUtils.trim(rs.getString(PROPERTY_NOT_FOUND, DEFAULT_NOT_FOUND));
+ }
+
+ /**
+ * @see org.apache.velocity.util.ContextAware#setContext(org.apache.velocity.context.Context)
+ */
+ @Override
+ public void setContext(Context context)
+ {
+ this.context = context;
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java
new file mode 100644
index 00000000..1c7e0b2d
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java
@@ -0,0 +1,72 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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 org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.context.Context;
+
+/**
+ * <p>Event handler that looks for included files relative to the path of the
+ * current template. The handler assumes that paths are separated by a forward
+ * slash "/" or backwards slash "\".
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain </a>
+ * @version $Id$
+ * @since 1.5
+ */
+public class IncludeRelativePath implements IncludeEventHandler {
+
+ /**
+ * Return path relative to the current template's path.
+ *
+ * @param includeResourcePath the path as given in the include directive.
+ * @param currentResourcePath the path of the currently rendering template that includes the
+ * include directive.
+ * @param directiveName name of the directive used to include the resource. (With the
+ * standard directives this is either "parse" or "include").
+
+ * @return new path relative to the current template's path
+ */
+ @Override
+ public String includeEvent(
+ Context context,
+ String includeResourcePath,
+ String currentResourcePath,
+ String directiveName)
+ {
+ // if the resource name starts with a slash, it's not a relative path
+ if (includeResourcePath.startsWith("/") || includeResourcePath.startsWith("\\") ) {
+ return includeResourcePath;
+ }
+
+ int lastslashpos = Math.max(
+ currentResourcePath.lastIndexOf("/"),
+ currentResourcePath.lastIndexOf("\\")
+ );
+
+ // root of resource tree
+ if (lastslashpos == -1) {
+ return includeResourcePath;
+ }
+
+ // prepend path to the include path
+ return currentResourcePath.substring(0,lastslashpos) + "/" + includeResourcePath;
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java
new file mode 100644
index 00000000..3529226a
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java
@@ -0,0 +1,64 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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 org.apache.velocity.util.introspection.Info;
+
+/**
+ * Convenience class to use when reporting out invalid syntax
+ * with line, column, and template name.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain </a>
+ * @version $Id$
+ * @since 1.5
+ */
+public class InvalidReferenceInfo extends Info
+{
+ private String invalidReference;
+
+ public InvalidReferenceInfo(String invalidReference, Info info)
+ {
+ super(info.getTemplateName(),info.getLine(),info.getColumn());
+ this.invalidReference = invalidReference;
+ }
+
+ /**
+ * Get the specific invalid reference string.
+ * @return the invalid reference string
+ */
+ public String getInvalidReference()
+ {
+ return invalidReference;
+ }
+
+
+
+ /**
+ * Formats a textual representation of this object as <code>SOURCE
+ * [line X, column Y]: invalidReference</code>.
+ *
+ * @return String representing this object.
+ */
+ public String toString()
+ {
+ return getTemplateName() + " [line " + getLine() + ", column " +
+ getColumn() + "]: " + invalidReference;
+ }
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java
new file mode 100644
index 00000000..ea88eafe
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java
@@ -0,0 +1,121 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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 org.apache.velocity.app.event.MethodExceptionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.RuntimeServicesAware;
+import org.apache.velocity.util.introspection.Info;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Simple event handler that renders method exceptions in the page
+ * rather than throwing the exception. Useful for debugging.
+ *
+ * <P>By default this event handler renders an error message containing the class and method which generated
+ * the exception, the exception name and its message.
+ *
+ * To render the reference and the location in the template, set the property <code>eventhandler.methodexception.templateinfo</code>
+ * to <code>true</code>.
+ *
+ * To render the stack trace, set the property <code>eventhandler.methodexception.stacktrace</code>
+ * to <code>true</code>.
+ *
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id$
+ * @since 1.5
+ */
+public class PrintExceptions implements MethodExceptionEventHandler, RuntimeServicesAware
+{
+
+ private static String SHOW_TEMPLATE_INFO = "eventhandler.methodexception.templateinfo";
+ private static String SHOW_STACK_TRACE = "eventhandler.methodexception.stacktrace";
+
+ /** Reference to the runtime service */
+ private RuntimeServices rs = null;
+
+ /**
+ * Render the method exception, and optionally the exception message and stack trace.
+ *
+ * @param context current context
+ * @param claz the class of the object the method is being applied to
+ * @param method the method
+ * @param e the thrown exception
+ * @param info template name and line, column informations
+ * @return an object to insert in the page
+ */
+ @Override
+ public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info)
+ {
+ boolean showTemplateInfo = rs.getBoolean(SHOW_TEMPLATE_INFO, false);
+ boolean showStackTrace = rs.getBoolean(SHOW_STACK_TRACE,false);
+
+ StringBuilder st = new StringBuilder();
+ st.append("Exception while executing method ").append(claz.toString()).append(".").append(method);
+ st.append(": ").append(e.getClass().getName()).append(": ").append(e.getMessage());
+
+ if (showTemplateInfo)
+ {
+ st.append(" at ").append(info.getTemplateName()).append(" (line ").append(info.getLine()).append(", column ").append(info.getColumn()).append(")");
+ }
+ if (showStackTrace)
+ {
+ st.append(System.lineSeparator()).append(getStackTrace(e));
+ }
+ return st.toString();
+
+ }
+
+ private static String getStackTrace(Throwable throwable)
+ {
+ PrintWriter printWriter = null;
+ try
+ {
+ StringWriter stackTraceWriter = new StringWriter();
+ printWriter = new PrintWriter(stackTraceWriter);
+ throwable.printStackTrace(printWriter);
+ printWriter.flush();
+ return stackTraceWriter.toString();
+ }
+ finally
+ {
+ if (printWriter != null)
+ {
+ printWriter.close();
+ }
+ }
+ }
+
+
+ /**
+ * @see org.apache.velocity.util.RuntimeServicesAware#setRuntimeServices(org.apache.velocity.runtime.RuntimeServices)
+ */
+ @Override
+ public void setRuntimeServices(RuntimeServices rs)
+ {
+ this.rs = rs;
+ }
+
+}
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java
new file mode 100644
index 00000000..2f9074f7
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java
@@ -0,0 +1,193 @@
+package org.apache.velocity.app.event.implement;
+
+/*
+ * 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 org.apache.velocity.app.event.InvalidReferenceEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.RuntimeServicesAware;
+import org.apache.velocity.util.introspection.Info;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Use this event handler to flag invalid references. Since this
+ * is intended to be used for a specific request, this should be
+ * used as a local event handler attached to a specific context
+ * instead of being globally defined in the Velocity properties file.
+ *
+ * <p>
+ * Note that InvalidReferenceHandler can be used
+ * in two modes. If the Velocity properties file contains the following:
+ * <pre>
+ * event_handler.invalid_references.exception = true
+ * </pre>
+ * then the event handler will throw a ParseErrorRuntimeException upon
+ * hitting the first invalid reference. This stops processing and is
+ * passed through to the application code. The ParseErrorRuntimeException
+ * contain information about the template name, line number, column number,
+ * and invalid reference.
+ *
+ * <p>
+ * If this configuration setting is false or omitted then the page
+ * will be processed as normal, but all invalid references will be collected
+ * in a List of InvalidReferenceInfo objects.
+ *
+ * <p>This feature should be regarded as experimental.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ * @since 1.5
+ */
+public class ReportInvalidReferences implements
+ InvalidReferenceEventHandler, RuntimeServicesAware
+{
+ public static final String EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "event_handler.invalid_references.exception";
+
+ @Deprecated
+ public static final String OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "eventhandler.invalidreference.exception";
+
+ /**
+ * List of InvalidReferenceInfo objects
+ */
+ List<InvalidReferenceInfo> invalidReferences = new ArrayList<>();
+
+ /**
+ * If true, stop at the first invalid reference and throw an exception.
+ */
+ private boolean stopOnFirstInvalidReference = false;
+
+
+ /**
+ * Collect the error and/or throw an exception, depending on configuration.
+ *
+ * @param context the context when the reference was found invalid
+ * @param reference string with complete invalid reference
+ * @param object the object referred to, or null if not found
+ * @param property the property name from the reference
+ * @param info contains template, line, column details
+ * @return always returns null
+ * @throws ParseErrorException
+ */
+ @Override
+ public Object invalidGetMethod(Context context, String reference, Object object,
+ String property, Info info)
+ {
+ reportInvalidReference(reference, info);
+ return null;
+ }
+
+ /**
+ * Collect the error and/or throw an exception, depending on configuration.
+ *
+ * @param context the context when the reference was found invalid
+ * @param reference complete invalid reference
+ * @param object the object referred to, or null if not found
+ * @param method the property name from the reference
+ * @param info contains template, line, column details
+ * @return always returns null
+ * @throws ParseErrorException
+ */
+ @Override
+ public Object invalidMethod(Context context, String reference, Object object,
+ String method, Info info)
+ {
+ if (reference == null)
+ {
+ reportInvalidReference(object.getClass().getName() + "." + method, info);
+ }
+ else
+ {
+ reportInvalidReference(reference, info);
+ }
+ return null;
+ }
+
+ /**
+ * Collect the error and/or throw an exception, depending on configuration.
+ *
+ * @param context the context when the reference was found invalid
+ * @param leftreference left reference being assigned to
+ * @param rightreference invalid reference on the right
+ * @param info contains info on template, line, col
+ * @return loop to end -- always returns false
+ */
+ @Override
+ public boolean invalidSetMethod(Context context, String leftreference, String rightreference, Info info)
+ {
+ reportInvalidReference(leftreference, info);
+ return false;
+ }
+
+
+ /**
+ * Check for an invalid reference and collect the error or throw an exception
+ * (depending on configuration).
+ *
+ * @param reference the invalid reference
+ * @param info line, column, template name
+ */
+ private void reportInvalidReference(String reference, Info info)
+ {
+ InvalidReferenceInfo invalidReferenceInfo = new InvalidReferenceInfo(reference, info);
+ invalidReferences.add(invalidReferenceInfo);
+
+ if (stopOnFirstInvalidReference)
+ {
+ throw new ParseErrorException(
+ "Error in page - invalid reference. ",
+ info,
+ invalidReferenceInfo.getInvalidReference());
+ }
+ }
+
+
+ /**
+ * All invalid references during the processing of this page.
+ * @return a List of InvalidReferenceInfo objects
+ */
+ public List<InvalidReferenceInfo> getInvalidReferences()
+ {
+ return invalidReferences;
+ }
+
+
+ /**
+ * Called automatically when event cartridge is initialized.
+ * @param rs RuntimeServices object assigned during initialization
+ */
+ @Override
+ public void setRuntimeServices(RuntimeServices rs)
+ {
+ Boolean b = rs.getConfiguration().getBoolean(OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, null);
+ if (b == null)
+ {
+ b = rs.getConfiguration().getBoolean(EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, false);
+ }
+ else
+ {
+ rs.getLog().warn("configuration key '{}' has been deprecated in favor of '{}'", OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, EVENTHANDLER_INVALIDREFERENCE_EXCEPTION);
+ }
+ stopOnFirstInvalidReference = b;
+ }
+
+}