You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by ge...@locus.apache.org on 2000/12/04 03:05:14 UTC
cvs commit: jakarta-velocity/src/java/org/apache/velocity/runtime/parser/node ASTMethod.java
geirm 00/12/03 18:05:14
Modified: src/java/org/apache/velocity/runtime/parser/node
ASTMethod.java
Log:
Rework introspection for context/thread safety. Rework interaction between this and ASTReference.
Revision Changes Path
1.8 +62 -43 jakarta-velocity/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java
Index: ASTMethod.java
===================================================================
RCS file: /home/cvs/jakarta-velocity/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- ASTMethod.java 2000/11/30 05:29:13 1.7
+++ ASTMethod.java 2000/12/04 02:05:14 1.8
@@ -61,39 +61,33 @@
*
* NOTE :
*
- * 'late introspection' : Take the example of using in a reference a container whose methods can
- * return Objects, such as java.util.Vector and the firstElement() or get(i) methods, and you
- * you follow the container element accessor with a method call on the object returned :
- *
- * $foo.firstElement().getValue()
+ * introspection is now done at render time.
*
- * where $foo is a Vector. In this case, the introspection cannot figure
- * out at init() time what class actually is returned by the accessor firstElement(). To solve this
- * we now will call init() on the ASTMethod node in execute() when the init() failed.
- *
* Please look at the Parser.jjt file which is
* what controls the generation of this class.
*
* @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
- * @version $Id: ASTMethod.java,v 1.7 2000/11/30 05:29:13 geirm Exp $
+ * @version $Id: ASTMethod.java,v 1.8 2000/12/04 02:05:14 geirm Exp $
*/
package org.apache.velocity.runtime.parser.node;
import java.lang.reflect.Method;
+import java.io.*;
+
import org.apache.velocity.Context;
+import org.apache.velocity.runtime.Runtime;
import org.apache.velocity.runtime.parser.*;
import org.apache.velocity.util.introspection.Introspector;
public class ASTMethod extends SimpleNode
{
- private String methodName;
- private int paramCount;
- private Method method;
- private Object[] params;
+ private String methodName = "";
+ private int paramCount = 0;
+ private Object [] params;
public ASTMethod(int id)
{
@@ -111,32 +105,44 @@
return visitor.visit(this, data);
}
- public Object init(Context context, Object data)
+ /**
+ * simple init - init our subtree and get what we can from
+ * the AST
+ */
+ public Object init( Context context, Object data)
throws Exception
{
+ super.init( context, data );
+
+ /*
+ * this is about all we can do
+ */
+
methodName = getFirstToken().image;
-
paramCount = jjtGetNumChildren() - 1;
- params = new Object[paramCount];
+ params = new Object[paramCount];
+ return data;
+ }
+
+ /**
+ * does the instrospection of the class for the method needed.
+ * Note, as this calls value() on the args if any, this must
+ * only be called at execute() / render() time
+ */
+ private Method doIntrospection(Context context, Class data)
+ throws Exception
+ {
/*
* Now the parameters have to be processed, there
* may be references contained within that need
* to be introspected.
*/
- for (int i = 0; i < paramCount; i++)
- jjtGetChild(i + 1).init(context, null);
-
for (int j = 0; j < paramCount; j++)
params[j] = jjtGetChild(j + 1).value(context);
- method = Introspector.getMethod( (Class) data, methodName, params);
-
- if (method == null)
- return null;
-
- return method.getReturnType();
+ return Introspector.getMethod( data, methodName, params);
}
/**
@@ -147,30 +153,43 @@
public Object execute(Object o, Context context)
{
/*
- * I need to pass in the arguments to the
- * method.
+ * new strategy (strategery!) for introspection. Since we want to be thread- as well as
+ * context-safe, we *must* do it now, at execution time. There can be no caching.
+ *
+ * we ned to call initIntrospection() once and only once to prevent calling value() on paramter nodes
+ * more than once. This is critical.
+ *
+ * Example : when we have a container like an array that returns something generic, like j.u.Object
+ * we need to have the *actual object* to perform introspection for successful execution.
*/
- for (int j = 0; j < paramCount; j++)
- params[j] = jjtGetChild(j + 1).value(context);
-
- try
+ Method method = null;
+
+ try
{
/*
- * it is possible that we have to do late introspection. If we don't
- * have a valid method object, cast this object to a Class, and try init()
- * again. As far as I can tell, the only time that the introspection
- * fails is when we try to case a j.l.Object to a Class, which is the
- * fast track to a ClassCastException
- * See the notes above.
+ * get the class of what we are, and introspect it
*/
+ Class c = o.getClass();
+ method = doIntrospection( context, c );
+
+ /*
+ * if we still haven't gotten the method, either we are calling a method that
+ * doesn't exist (which is fine...) or I screwed it up.
+ */
+
if (method == null)
- {
- Class c = o.getClass();
- init( context, c);
- }
-
+ return null;
+ }
+ catch( Exception e )
+ {
+ Runtime.error("ASTMethod.execute() : exception : " + e );
+ return null;
+ }
+
+ try
+ {
/*
* get the returned object. It may be null, and that is
* valid for something declared with a void return type.