Author: wglass Date: Thu Oct 12 04:52:06 2006 New Revision: 463213 URL: http://svn.apache.org/viewvc?view=rev&rev=463213 Log: New event handler: InvalidReferenceEventHandler. Allows the developer to trap invalid references, property assignments, method calls. Sample implementation can be used to record all invalid references for later retrieval. With a configuration option, it can also throw an exception upon the first invalid reference. Related to VELOCITY-423.
Added: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java (with props) jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java (with props) jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java (with props) jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java (with props) Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/EventHandlerUtil.java jakarta/velocity/engine/trunk/src/java/org/apache/velocity/exception/ParseErrorException.java jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/BuiltInEventHandlerTestCase.java Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/EventHandlerUtil.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/EventHandlerUtil.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/EventHandlerUtil.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/EventHandlerUtil.java Thu Oct 12 04:52:06 2006 @@ -21,6 +21,7 @@ import org.apache.velocity.context.InternalContextAdapter; import org.apache.velocity.runtime.RuntimeServices; import org.apache.velocity.util.ExceptionUtils; +import org.apache.velocity.util.introspection.Info; /** @@ -228,7 +229,121 @@ throw ExceptionUtils.createRuntimeException("Exception in event handler.",e); } } + + + /** + * Called when an invalid get method is encountered. + * + * @param rsvc The RuntimeServices Object. + * @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 + */ + public static Object invalidGetMethod(RuntimeServices rsvc, + InternalContextAdapter context, String reference, + Object object, String property, Info info) + { + return + invalidReferenceHandlerCall ( + new InvalidReferenceEventHandler.InvalidGetMethodExecutor + (context, reference, object, property, info), + rsvc, + context); + } + + + /** + * Called when an invalid get method is encountered. + * + * @param rsvc The RuntimeServices Object. + * @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 + */ + public static void invalidSetMethod(RuntimeServices rsvc, + InternalContextAdapter context, String leftreference, + String rightreference, Info info) + { + /** + * ignore return value + */ + invalidReferenceHandlerCall ( + new InvalidReferenceEventHandler.InvalidSetMethodExecutor + (context, leftreference, rightreference, info), + rsvc, + context); + } + /** + * Called when an invalid method is encountered. + * + * @param rsvc The RuntimeServices Object. + * @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 + */ + public static Object invalidMethod(RuntimeServices rsvc, + InternalContextAdapter context, String reference, + Object object, String method, Info info) + { + return + invalidReferenceHandlerCall ( + new InvalidReferenceEventHandler.InvalidMethodExecutor + (context, reference, object, method, info), + rsvc, + context); + } + + + /** + * Generic call to InvalidReferenceEventHandler method. + * @param methodExecutor + * @param rsvc + * @param context + * @return + */ + public static Object invalidReferenceHandlerCall( + EventHandlerMethodExecutor methodExecutor, + RuntimeServices rsvc, + InternalContextAdapter context) + { + // app level cartridges have already been initialized + EventCartridge ev1 = rsvc.getApplicationEventCartridge(); + Iterator applicationEventHandlerIterator = + (ev1 == null) ? null: ev1.getInvalidReferenceEventHandlers(); + + EventCartridge ev2 = context.getEventCartridge(); + initializeEventCartridge(rsvc, ev2); + Iterator contextEventHandlerIterator = + (ev2 == null) ? null: ev2.getInvalidReferenceEventHandlers(); + + try + { + callEventHandlers( + applicationEventHandlerIterator, + contextEventHandlerIterator, methodExecutor); + + return methodExecutor.getReturnValue(); + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw ExceptionUtils.createRuntimeException("Exception in event handler.",e); + } + + } + /** * Initialize the event cartridge if appropriate. * @param rsvc Added: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java?view=auto&rev=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java (added) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java Thu Oct 12 04:52:06 2006 @@ -0,0 +1,234 @@ +package org.apache.velocity.app.event; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed 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:[EMAIL PROTECTED]">Will Glass-Husain</a> + * @version $Id$ + */ +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 + */ + public 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 string to which the value is being assigned + * @param rightreference the invalid reference on the right + * @param property the property name from the reference + * @param info contains template, line, column details + * @param if true then stop calling invalidSetMethod along the + * chain. + */ + public 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 + */ + public Object invalidMethod(Context context, String reference, + Object object, String method, Info info); + + + /** + * Defines the execution strategy for invalidGetMethod + */ + static class InvalidGetMethodExecutor implements EventHandlerMethodExecutor + { + private Context context; + private String reference; + private Object object; + private String property; + private Info info; + + private Object result; + + InvalidGetMethodExecutor( + Context context, + String reference, + Object object, + String property, + Info info) + { + this.context = context; + this.reference = reference; + this.object = object; + this.property = property; + this.info = info; + } + + /** + * Call the method invalidGetMethod() + * + * @param handler call the appropriate method on this handler + */ + public void execute(EventHandler handler) + { + result = ((InvalidReferenceEventHandler) handler).invalidGetMethod( + context, reference, object, property, info); + } + + public Object getReturnValue() + { + return result; + } + + public boolean isDone() + { + return (result != null); + } + } + + /** + * Defines the execution strategy for invalidGetMethod + */ + static class InvalidSetMethodExecutor implements EventHandlerMethodExecutor + { + private Context context; + private String leftreference; + private String rightreference; + private Info info; + + private boolean result; + + InvalidSetMethodExecutor( + Context context, + String leftreference, + String rightreference, + Info info) + { + this.context = context; + this.leftreference = leftreference; + this.rightreference = rightreference; + this.info = info; + } + + /** + * Call the method invalidSetMethod() + * + * @param handler call the appropriate method on this handler + */ + public void execute(EventHandler handler) + { + result = ((InvalidReferenceEventHandler) handler).invalidSetMethod( + context, leftreference, rightreference, info); + } + + public Object getReturnValue() + { + return null; + } + + public boolean isDone() + { + return result; + } + + } + + /** + * Defines the execution strategy for invalidGetMethod + */ + static class InvalidMethodExecutor implements EventHandlerMethodExecutor + { + private Context context; + private String reference; + private Object object; + private String method; + private Info info; + + private Object result; + private boolean executed = false; + + InvalidMethodExecutor( + Context context, + String reference, + Object object, + String method, + Info info) + { + this.context = context; + this.reference = reference; + this.object = object; + this.method = method; + this.info = info; + } + + /** + * Call the method invalidMethod() + * + * @param handler call the appropriate method on this handler + */ + public void execute(EventHandler handler) + { + executed = true; + result = ((InvalidReferenceEventHandler) handler).invalidMethod( + context, reference, object, method, info); + } + + public Object getReturnValue() + { + return result; + } + + public boolean isDone() + { + return executed && (result != null); + } + + } + +} Propchange: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java ------------------------------------------------------------------------------ svn:keywords = Id Author Date Revision Added: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java?view=auto&rev=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java (added) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java Thu Oct 12 04:52:06 2006 @@ -0,0 +1,60 @@ +package org.apache.velocity.app.event.implement; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed 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:[EMAIL PROTECTED]">Will Glass-Husain </a> + * @version $Id$ + */ +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]</code>. + * + * @return String representing this object. + */ + public String toString() + { + return getTemplateName() + " [line " + getLine() + ", column " + + getColumn() + "]: " + invalidReference; + } +} Propchange: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java ------------------------------------------------------------------------------ svn:keywords = Id Author Date Revision Added: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java?view=auto&rev=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java (added) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java Thu Oct 12 04:52:06 2006 @@ -0,0 +1,177 @@ +package org.apache.velocity.app.event.implement; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.ArrayList; +import java.util.List; + +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; + +/** + * 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> + * <CODE>eventhandler.invalidreference.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:[EMAIL PROTECTED]">Will Glass-Husain</a> + * @version $Id$ + */ +public class ReportInvalidReferences implements +InvalidReferenceEventHandler, RuntimeServicesAware +{ + + public static final String EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "eventhandler.invalidreference.exception"; + + /** + * List of InvalidReferenceInfo objects + */ + List 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 + */ + 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 + */ + 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 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 + * @param always returns false + * @throws ParseErrorException + */ + 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). + * + * @throws ParseErrorException + */ + 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 getInvalidReferences() + { + return invalidReferences; + } + + + /** + * Called automatically when event cartridge is initialized. + * @param rs RuntimeServices object assigned during initialization + */ + public void setRuntimeServices(RuntimeServices rs) + { + stopOnFirstInvalidReference = rs.getConfiguration().getBoolean( + EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, + false); + } + +} Propchange: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java ------------------------------------------------------------------------------ svn:keywords = Id Author Date Revision Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/exception/ParseErrorException.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/exception/ParseErrorException.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/exception/ParseErrorException.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/exception/ParseErrorException.java Thu Oct 12 04:52:06 2006 @@ -17,6 +17,7 @@ */ import org.apache.velocity.runtime.parser.ParseException; +import org.apache.velocity.util.introspection.Info; /** * Application-level exception thrown when a resource of any type @@ -53,6 +54,11 @@ private String templateName = "*unset*"; /** + * If applicable, contains the invalid syntax or reference that triggered this exception + */ + private String invalidSyntax; + + /** * Create a ParseErrorException with the given message. * * @param exceptionMessage the error exception message @@ -93,6 +99,39 @@ } } + + /** + * Create a ParseErrorRuntimeException with the given message and info + * + * @param exceptionMessage the error exception message + * @param info an Info object with the current template info + */ + public ParseErrorException(String exceptionMessage, Info info) + { + super(exceptionMessage); + columnNumber = info.getColumn(); + lineNumber = info.getLine(); + templateName = info.getTemplateName(); + } + + /** + * Create a ParseErrorRuntimeException with the given message and info + * + * @param exceptionMessage the error exception message + * @param info an Info object with the current template info + * @param invalidSyntax the invalid syntax or reference triggering this exception + */ + public ParseErrorException(String exceptionMessage, + Info info, String invalidSyntax) + { + super(exceptionMessage); + columnNumber = info.getColumn(); + lineNumber = info.getLine(); + templateName = info.getTemplateName(); + this.invalidSyntax = invalidSyntax; + } + + /** * Return the column number of the parsing error, or -1 if not defined. * @@ -124,4 +163,17 @@ { return templateName; } + + /** + * Return the invalid syntax or reference that triggered this error, or null + * if not defined. + * + * @return Return the invalid syntax or reference that triggered this error, or null + * if not defined + */ + public String getInvalidSyntax() + { + return invalidSyntax; + } + } Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeConstants.java Thu Oct 12 04:52:06 2006 @@ -253,6 +253,14 @@ */ public static final String EVENTHANDLER_INCLUDE = "eventhandler.include.class"; + /** + * The <code>eventhandler.invalidreferences.class</code> property + * specifies a list of the [EMAIL PROTECTED] + * org.apache.velocity.app.event.InvalidReferenceEventHandler} + * implementations to use. + */ + public static final String EVENTHANDLER_INVALIDREFERENCES = "eventhandler.invalidreferences.class"; + /* * ---------------------------------------------------------------------- Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/RuntimeInstance.java Thu Oct 12 04:52:06 2006 @@ -31,6 +31,7 @@ import org.apache.velocity.app.event.EventCartridge; import org.apache.velocity.app.event.EventHandler; import org.apache.velocity.app.event.IncludeEventHandler; +import org.apache.velocity.app.event.InvalidReferenceEventHandler; import org.apache.velocity.app.event.MethodExceptionEventHandler; import org.apache.velocity.app.event.NullSetEventHandler; import org.apache.velocity.app.event.ReferenceInsertionEventHandler; @@ -641,6 +642,20 @@ eventCartridge.addIncludeEventHandler((IncludeEventHandler) ev); } } + + String[] invalidReferenceSet = configuration.getStringArray(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES); + if ( invalidReferenceSet != null ) + { + for ( int i=0; i < invalidReferenceSet.length; i++ ) + { + EventHandler ev = initializeSpecificEventHandler(invalidReferenceSet[i],RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES,InvalidReferenceEventHandler.class); + if (ev != null) + { + eventCartridge.addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev); + } + } + } + } Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java Thu Oct 12 04:52:06 2006 @@ -50,6 +50,8 @@ private String methodName = ""; private int paramCount = 0; + protected Info uberInfo; + /** * @param id */ @@ -89,6 +91,12 @@ super.init( context, data ); /* + * make an uberinfo - saves new's later on + */ + + uberInfo = new Info(context.getCurrentTemplateName(), + getLine(),getColumn()); + /* * this is about all we can do */ @@ -191,7 +199,9 @@ */ if (method == null) + { return null; + } } catch( MethodInvocationException mie ) { @@ -396,5 +406,14 @@ return result; } } + + /** + * @return Returns the methodName. + */ + public String getMethodName() + { + return methodName; + } + } Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java Thu Oct 12 04:52:06 2006 @@ -173,7 +173,8 @@ if (result == null) { - return null; + return EventHandlerUtil.invalidGetMethod(rsvc, context, + "$" + rootString, null, null, uberInfo); } /* @@ -191,15 +192,58 @@ try { + Object previousResult = result; + int failedChild = -1; for (int i = 0; i < numChildren; i++) { + previousResult = result; result = jjtGetChild(i).execute(result,context); if (result == null) { + failedChild = i; break; } } + if (result == null) + { + if (failedChild == -1) + { + result = EventHandlerUtil.invalidGetMethod(rsvc, context, + "$" + rootString, previousResult, null, uberInfo); + } + else + { + StringBuffer name = new StringBuffer("$").append(rootString); + for (int i = 0; i <= failedChild; i++) + { + Node node = jjtGetChild(i); + if (node instanceof ASTMethod) + { + name.append(".").append(((ASTMethod) node).getMethodName()).append("()"); + } + else + { + name.append(".").append(node.getFirstToken().image); + } + } + + if (jjtGetChild(failedChild) instanceof ASTMethod) + { + String methodName = ((ASTMethod) jjtGetChild(failedChild)).getMethodName(); + result = EventHandlerUtil.invalidMethod(rsvc, context, + name.toString(), previousResult, methodName, uberInfo); + } + else + { + String property = jjtGetChild(failedChild).getFirstToken().image; + result = EventHandlerUtil.invalidGetMethod(rsvc, context, + name.toString(), previousResult, property, uberInfo); + } + } + + } + return result; } catch(MethodInvocationException mie) Modified: jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java (original) +++ jakarta/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java Thu Oct 12 04:52:06 2006 @@ -25,6 +25,7 @@ import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.parser.Parser; import org.apache.velocity.runtime.parser.ParserVisitor; +import org.apache.velocity.util.introspection.Info; /** * Node for the #set directive @@ -41,6 +42,11 @@ boolean logOnNull = false; /** + * This is really immutable after the init, so keep one for this node + */ + protected Info uberInfo; + + /** * @param id */ public ASTSetDirective(int id) @@ -81,6 +87,9 @@ super.init( context, data ); + uberInfo = new Info(context.getCurrentTemplateName(), + getLine(), getColumn()); + right = getRightHandSide(); left = getLeftHandSide(); @@ -119,7 +128,7 @@ if( !rsvc.getBoolean(RuntimeConstants.SET_NULL_ALLOWED,false) ) { if ( value == null ) - { + { /* * first, are we supposed to say anything anyway? */ @@ -134,18 +143,35 @@ + ", column " + getColumn() + "]"); } } - + + String rightReference = null; + if (right instanceof ASTExpression) + { + rightReference = ((ASTExpression) right).getLastToken().image; + } + EventHandlerUtil.invalidSetMethod(rsvc, context, leftReference, rightReference, uberInfo); + return false; } } if ( value == null ) { + String rightReference = null; + if (right instanceof ASTExpression) + { + rightReference = ((ASTExpression) right).getLastToken().image; + } + EventHandlerUtil.invalidSetMethod(rsvc, context, leftReference, rightReference, uberInfo); + /* * if RHS is null it doesn't matter if LHS is simple or complex * because the LHS is removed from context */ context.remove( leftReference ); + + return false; + } else { Modified: jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/BuiltInEventHandlerTestCase.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/BuiltInEventHandlerTestCase.java?view=diff&rev=463213&r1=463212&r2=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/BuiltInEventHandlerTestCase.java (original) +++ jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/BuiltInEventHandlerTestCase.java Thu Oct 12 04:52:06 2006 @@ -22,6 +22,7 @@ import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; +import java.util.List; import junit.framework.Test; import junit.framework.TestSuite; @@ -29,11 +30,14 @@ import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.app.event.EventCartridge; import org.apache.velocity.app.event.implement.EscapeHtmlReference; import org.apache.velocity.app.event.implement.EscapeJavaScriptReference; import org.apache.velocity.app.event.implement.EscapeReference; import org.apache.velocity.app.event.implement.EscapeSqlReference; import org.apache.velocity.app.event.implement.EscapeXmlReference; +import org.apache.velocity.app.event.implement.InvalidReferenceInfo; +import org.apache.velocity.app.event.implement.ReportInvalidReferences; import org.apache.velocity.context.Context; import org.apache.velocity.runtime.RuntimeConstants; @@ -95,6 +99,59 @@ return new TestSuite(BuiltInEventHandlerTestCase.class); } + + + /** + * Test reporting of invalid syntax + * @throws Exception + */ + public void testReportInvalidReferences1() throws Exception + { + VelocityEngine ve = new VelocityEngine(); + ReportInvalidReferences reporter = new ReportInvalidReferences(); + ve.init(); + + VelocityContext context = new VelocityContext(); + EventCartridge ec = new EventCartridge(); + ec.addEventHandler(reporter); + ec.attachToContext(context); + + context.put("a1","test"); + context.put("b1","test"); + Writer writer = new StringWriter(); + + ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar()"); + + List errors = reporter.getInvalidReferences(); + assertEquals(2,errors.size()); + assertEquals("$c1",((InvalidReferenceInfo) errors.get(0)).getInvalidReference()); + assertEquals("$a1.foobar()",((InvalidReferenceInfo) errors.get(1)).getInvalidReference()); + } + + public void testReportInvalidReferences2() throws Exception + { + VelocityEngine ve = new VelocityEngine(); + ve.setProperty("eventhandler.invalidreference.exception","true"); + ReportInvalidReferences reporter = new ReportInvalidReferences(); + ve.init(); + + VelocityContext context = new VelocityContext(); + EventCartridge ec = new EventCartridge(); + ec.addEventHandler(reporter); + ec.attachToContext(context); + + context.put("a1","test"); + context.put("b1","test"); + Writer writer = new StringWriter(); + + ve.evaluate(context,writer,"test","$a1 no problem"); + + try { + ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar()"); + fail ("Expected exception."); + } catch (RuntimeException E) {} + + } /** * Test escaping Added: jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java URL: http://svn.apache.org/viewvc/jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java?view=auto&rev=463213 ============================================================================== --- jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java (added) +++ jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java Thu Oct 12 04:52:06 2006 @@ -0,0 +1,472 @@ +package org.apache.velocity.test; + +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.StringWriter; +import java.io.Writer; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.app.event.EventCartridge; +import org.apache.velocity.app.event.InvalidReferenceEventHandler; +import org.apache.velocity.context.Context; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.RuntimeServicesAware; +import org.apache.velocity.util.introspection.Info; + +/** + * Tests event handling for all event handlers except IncludeEventHandler. This is tested + * separately due to its complexity. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a> + * @version $Id$ + */ +public class InvalidEventHandlerTestCase +extends TestCase +{ + /** + * Default constructor. + */ + public InvalidEventHandlerTestCase(String name) + { + super(name); + } + + public static Test suite () + { + return new TestSuite(InvalidEventHandlerTestCase.class); + } + + public void testManualEventHandlers() + throws Exception + { + TestEventCartridge te = new TestEventCartridge(); + + /** + * Test attaching the event cartridge to the context + */ + VelocityEngine ve = new VelocityEngine(); + ve.init(); + + /* + * lets make a Context and add the event cartridge + */ + + VelocityContext inner = new VelocityContext(); + + /* + * Now make an event cartridge, register all the + * event handlers (at once) and attach it to the + * Context + */ + + EventCartridge ec = new EventCartridge(); + ec.addEventHandler(te); + ec.attachToContext( inner ); + + doTestInvalidReferenceEventHandler1(ve, inner); + doTestInvalidReferenceEventHandler2(ve, inner); + doTestInvalidReferenceEventHandler3(ve, inner); + doTestInvalidReferenceEventHandler4(ve, inner); + } + + /** + * Test assigning the event handlers via properties + */ + + public void testConfigurationEventHandlers() + throws Exception + { + VelocityEngine ve = new VelocityEngine(); + ve.setProperty(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES, TestEventCartridge.class.getName()); + + ve.init(); + doTestInvalidReferenceEventHandler1(ve, null); + doTestInvalidReferenceEventHandler2(ve, null); + doTestInvalidReferenceEventHandler3(ve, null); + doTestInvalidReferenceEventHandler4(ve, null); + } + + /** + * Test deeper structures + * @param ve + * @param vc + * @throws Exception + */ + private void doTestInvalidReferenceEventHandler4(VelocityEngine ve, VelocityContext vc) + throws Exception + { + VelocityContext context = new VelocityContext(vc); + + Tree test = new Tree(); + test.setField("10"); + Tree test2 = new Tree(); + test2.setField("12"); + test.setChild(test2); + + context.put("tree",test); + String s; + Writer w; + + // show work fine + s = "$tree.Field $tree.field $tree.child.Field"; + w = new StringWriter(); + ve.evaluate( context, w, "mystring", s ); + + s = "$tree.x $tree.field.x $tree.child.y $tree.child.Field.y"; + w = new StringWriter(); + ve.evaluate( context, w, "mystring", s ); + + } + + /** + * Test invalid #set + * @param ve + * @param vc + * @throws Exception + */ + private void doTestInvalidReferenceEventHandler3(VelocityEngine ve, VelocityContext vc) + throws Exception + { + VelocityContext context = new VelocityContext(vc); + context.put("a1",new Integer(5)); + context.put("a4",new Integer(5)); + context.put("b1","abc"); + + String s; + Writer w; + + // good object, bad right hand side + s = "#set($xx = $a1.afternoon())"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + // good object, bad right hand reference + s = "#set($yy = $q1)"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + } + + /** + * Test invalid method calls + * @param ve + * @param vc + * @throws Exception + */ + private void doTestInvalidReferenceEventHandler2(VelocityEngine ve, VelocityContext vc) + throws Exception + { + VelocityContext context = new VelocityContext(vc); + context.put("a1",new Integer(5)); + context.put("a4",new Integer(5)); + context.put("b1","abc"); + + String s; + Writer w; + + // good object, bad method + s = "$a1.afternoon()"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + // bad object, bad method -- fails on get + s = "$zz.daylight()"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + // change result + s = "$b1.baby()"; + w = new StringWriter(); + ve.evaluate( context, w, "mystring", s ); + assertEquals("www",w.toString()); + } + + /** + * Test invalid gets/references + * @param ve + * @param vc + * @throws Exception + */ + private void doTestInvalidReferenceEventHandler1(VelocityEngine ve, VelocityContext vc) + throws Exception + { + String result; + + VelocityContext context = new VelocityContext(vc); + context.put("a1",new Integer(5)); + context.put("a4",new Integer(5)); + context.put("b1","abc"); + + // normal - should be no calls to handler + String s = "$a1 $a1.intValue() $b1 $b1.length() #set($c1 = '5')"; + Writer w = new StringWriter(); + ve.evaluate( context, w, "mystring", s ); + + // good object, bad property + s = "$a1.foobar"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + // bad object, bad property + s = "$a2.foobar"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + // bad object, no property + s = "$a3"; + w = new StringWriter(); + try { + ve.evaluate( context, w, "mystring", s ); + fail("Expected exception."); + } catch (RuntimeException e) {} + + // good object, bad property; change the value + s = "$a4.foobar"; + w = new StringWriter(); + ve.evaluate( context, w, "mystring", s ); + result = w.toString(); + assertEquals("zzz", result); + + } + + + + /** + * Test assigning the event handlers via properties + */ + + public static class TestEventCartridge + implements InvalidReferenceEventHandler, + RuntimeServicesAware + { + private RuntimeServices rs; + + public TestEventCartridge() + { + } + + /** + * Required by EventHandler + */ + public void setRuntimeServices( RuntimeServices rs ) + { + // make sure this is only called once + if (this.rs == null) + this.rs = rs; + + else + fail("initialize called more than once."); + } + + + public Object invalidGetMethod(Context context, String reference, Object object, String property, Info info) + { + // as a test, make sure this EventHandler is initialized + if (rs == null) + fail ("Event handler not initialized!"); + + // good object, bad property + if (reference.equals("$a1.foobar")) + { + assertEquals(new Integer(5),object); + assertEquals("foobar",property); + throw new RuntimeException("expected exception"); + } + + // bad object, bad property + else if (reference.equals("$a2")) + { + assertNull(object); + assertNull(property); + throw new RuntimeException("expected exception"); + } + + // bad object, no property + else if (reference.equals("$a3")) + { + assertNull(object); + assertNull(property); + throw new RuntimeException("expected exception"); + } + + // good object, bad property; change the value + else if (reference.equals("$a4.foobar")) + { + assertEquals(new Integer(5),object); + assertEquals("foobar",property); + return "zzz"; + } + + // bad object, bad method -- fail on the object + else if (reference.equals("$zz")) + { + assertNull(object); + assertNull(property); + throw new RuntimeException("expected exception"); + } + + // pass q1 through + else if (reference.equals("$q1")) + { + + } + + + else if (reference.equals("$tree.x")) + { + assertEquals("x",property); + } + + else if (reference.equals("$tree.field.x")) + { + assertEquals("x",property); + } + + else if (reference.equals("$tree.child.y")) + { + assertEquals("y",property); + } + + else if (reference.equals("$tree.child.Field.y")) + { + assertEquals("y",property); + } + + else + { + fail("invalidGetMethod: unexpected reference: " + reference); + } + return null; + } + + public Object invalidMethod(Context context, String reference, Object object, String method, Info info) + { + // as a test, make sure this EventHandler is initialized + if (rs == null) + fail ("Event handler not initialized!"); + + // good reference, bad method + if (object.getClass().equals(Integer.class)) + { + assertEquals("$a1.afternoon()",reference); + assertEquals("afternoon",method); + throw new RuntimeException("expected exception"); + } + + else if (object.getClass().equals(String.class) && "baby".equals(method)) + { + return "www"; + } + + else + { + fail("Unexpected invalid method. " + method); + } + + return null; + } + + + public boolean invalidSetMethod(Context context, String leftreference, String rightreference, Info info) + { + + // as a test, make sure this EventHandler is initialized + if (rs == null) + fail ("Event handler not initialized!"); + + // good object, bad method + if (leftreference.equals("xx")) + { + assertEquals("q1.afternoon()",rightreference); + throw new RuntimeException("expected exception"); + } + if (leftreference.equals("yy")) + { + assertEquals("$q1",rightreference); + throw new RuntimeException("expected exception"); + } + else + { + fail("Unexpected left hand side. " + leftreference); + } + + return false; + } + + } + + public static class Tree + { + String field; + Tree child; + + public Tree() + { + + } + + public String getField() + { + return field; + } + + public void setField(String field) + { + this.field = field; + } + + public Tree getChild() + { + return child; + } + + public void setChild(Tree child) + { + this.child = child; + } + + public String testMethod() + { + return "123"; + } + } + +} Propchange: jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jakarta/velocity/engine/trunk/src/test/org/apache/velocity/test/InvalidEventHandlerTestCase.java ------------------------------------------------------------------------------ svn:keywords = Id Author Date Revision --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]