aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java
diff options
context:
space:
mode:
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java')
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java305
1 files changed, 305 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java
new file mode 100644
index 00000000..3cb762a4
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java
@@ -0,0 +1,305 @@
+package org.apache.velocity.runtime.parser.node;
+
+/*
+ * 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.EventHandlerUtil;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.TemplateInitException;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.parser.Parser;
+import org.apache.velocity.util.introspection.Info;
+import org.apache.velocity.util.introspection.IntrospectionCacheData;
+import org.apache.velocity.util.introspection.VelPropertyGet;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * ASTIdentifier.java
+ *
+ * Method support for identifiers : $foo
+ *
+ * mainly used by ASTReference
+ *
+ * Introspection is now moved to 'just in time' or at render / execution
+ * time. There are many reasons why this has to be done, but the
+ * primary two are thread safety, to remove any context-derived
+ * information from class member variables.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class ASTIdentifier extends SimpleNode
+{
+ private String identifier = "";
+
+ /**
+ * This is really immutable after the init, so keep one for this node
+ */
+ protected Info uberInfo;
+
+ /**
+ * Indicates if we are running in strict reference mode.
+ */
+ protected boolean strictRef = false;
+
+ /**
+ * @param id
+ */
+ public ASTIdentifier(int id)
+ {
+ super(id);
+ }
+
+ /**
+ * @param p
+ * @param id
+ */
+ public ASTIdentifier(Parser p, int id)
+ {
+ super(p, id);
+ }
+
+ /**
+ * Identifier getter
+ * @return identifier
+ */
+ public String getIdentifier()
+ {
+ return identifier;
+ }
+
+ /**
+ * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object)
+ */
+ @Override
+ public Object jjtAccept(ParserVisitor visitor, Object data)
+ {
+ return visitor.visit(this, data);
+ }
+
+ /**
+ * simple init - don't do anything that is context specific.
+ * just get what we need from the AST, which is static.
+ * @param context
+ * @param data
+ * @return The data object.
+ * @throws TemplateInitException
+ */
+ @Override
+ public Object init(InternalContextAdapter context, Object data)
+ throws TemplateInitException
+ {
+ super.init(context, data);
+
+ identifier = rsvc.useStringInterning() ? getFirstToken().image.intern() : getFirstToken().image;
+
+ uberInfo = new Info(getTemplateName(), getLine(), getColumn());
+
+ strictRef = rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
+
+ saveTokenImages();
+ cleanupParserAndTokens();
+
+ return data;
+ }
+
+ /**
+ * @see org.apache.velocity.runtime.parser.node.SimpleNode#execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter)
+ */
+ @Override
+ public Object execute(Object o, InternalContextAdapter context)
+ throws MethodInvocationException
+ {
+ try
+ {
+ rsvc.getLogContext().pushLogContext(this, uberInfo);
+
+ VelPropertyGet vg = null;
+
+ try
+ {
+ /*
+ * first, see if we have this information cached.
+ */
+
+ IntrospectionCacheData icd = context.icacheGet(this);
+ Class<?> clazz = o instanceof Class<?> ? (Class<?>)o : o.getClass();
+
+ /*
+ * if we have the cache data and the class of the object we are
+ * invoked with is the same as that in the cache, then we must
+ * be all right. The last 'variable' is the method name, and
+ * that is fixed in the template :)
+ */
+
+ if ( icd != null && (icd.contextData == clazz) )
+ {
+ vg = (VelPropertyGet) icd.thingy;
+ }
+ else
+ {
+ /*
+ * otherwise, do the introspection, and cache it. Use the
+ * uberspector
+ */
+
+ vg = rsvc.getUberspect().getPropertyGet(o, identifier, uberInfo);
+
+ if (vg != null && vg.isCacheable())
+ {
+ icd = new IntrospectionCacheData();
+ icd.contextData = clazz;
+ icd.thingy = vg;
+ context.icachePut(this,icd);
+ }
+ }
+ }
+
+ /*
+ * pass through application level runtime exceptions
+ */
+ catch( RuntimeException e )
+ {
+ throw e;
+ }
+ catch(Exception e)
+ {
+ String msg = "ASTIdentifier.execute() : identifier = "+identifier;
+ log.error(msg, e);
+ throw new VelocityException(msg, e, rsvc.getLogContext().getStackTrace());
+ }
+
+ /*
+ * we have no getter... punt...
+ */
+
+ if (vg == null)
+ {
+ if (strictRef)
+ {
+ throw new MethodInvocationException("Object '" + o.getClass().getName() +
+ "' does not contain property '" + identifier + "'",
+ null, rsvc.getLogContext().getStackTrace(), identifier,
+ uberInfo.getTemplateName(), uberInfo.getLine(), uberInfo.getColumn());
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /*
+ * now try and execute. If we get a MIE, throw that
+ * as the app wants to get these. If not, log and punt.
+ */
+ try
+ {
+ return vg.invoke(o);
+ }
+ catch(InvocationTargetException ite)
+ {
+ /*
+ * if we have an event cartridge, see if it wants to veto
+ * also, let non-Exception Throwables go...
+ */
+
+ Throwable t = ite.getTargetException();
+ if (t instanceof Exception)
+ {
+ try
+ {
+ return EventHandlerUtil.methodException(rsvc, context, o.getClass(), vg.getMethodName(),
+ (Exception) t, uberInfo);
+ }
+
+ /*
+ * If the event handler throws an exception, then wrap it
+ * in a MethodInvocationException.
+ */
+ catch( Exception e )
+ {
+ throw new MethodInvocationException(
+ "Invocation of method '" + vg.getMethodName() + "'"
+ + " in " + o.getClass()
+ + " threw exception "
+ + ite.getTargetException().toString(),
+ ite.getTargetException(), rsvc.getLogContext().getStackTrace(), vg.getMethodName(), getTemplateName(), this.getLine(), this.getColumn());
+ }
+ }
+ else
+ {
+ /*
+ * no event cartridge to override. Just throw
+ */
+
+ throw new MethodInvocationException(
+ "Invocation of method '" + vg.getMethodName() + "'"
+ + " in " + o.getClass()
+ + " threw exception "
+ + ite.getTargetException().toString(),
+ ite.getTargetException(), rsvc.getLogContext().getStackTrace(), vg.getMethodName(), getTemplateName(), this.getLine(), this.getColumn());
+
+
+ }
+ }
+ catch(IllegalArgumentException iae)
+ {
+ return null;
+ }
+ /*
+ * pass through application level runtime exceptions
+ */
+ catch( RuntimeException e )
+ {
+ throw e;
+ }
+ catch(Exception e)
+ {
+ String msg = "ASTIdentifier() : exception invoking method "
+ + "for identifier '" + identifier + "' in "
+ + o.getClass();
+ log.error(msg, e);
+ throw new VelocityException(msg, e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+ finally
+ {
+ rsvc.getLogContext().popLogContext();
+ }
+ }
+
+ /**
+ * Returns the string ".<i>identifier</i>". This method is only used for displaying the VTL stacktrace
+ * when a rendering error is encountered when runtime.log.track_location is true.
+ * @return
+ */
+ @Override
+ public String literal()
+ {
+ if (literal != null)
+ {
+ return literal;
+ }
+ return literal = '.' + getIdentifier();
+ }
+}