aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
diff options
context:
space:
mode:
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIndex.java')
-rw-r--r--velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIndex.java224
1 files changed, 224 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIndex.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
new file mode 100644
index 00000000..ff8576fe
--- /dev/null
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
@@ -0,0 +1,224 @@
+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.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.ClassUtils;
+import org.apache.velocity.util.StringUtils;
+import org.apache.velocity.util.introspection.VelMethod;
+
+/*
+ * 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.
+ */
+
+/**
+ * This node is responsible for the bracket notation at the end of
+ * a reference, e.g., $foo[1]
+ */
+
+public class ASTIndex extends SimpleNode
+{
+ private static final String methodName = "get";
+
+ /**
+ * Indicates if we are running in strict reference mode.
+ */
+ protected boolean strictRef = false;
+
+ /**
+ * @param i
+ */
+ public ASTIndex(int i)
+ {
+ super(i);
+ }
+
+ /**
+ * @param p
+ * @param i
+ */
+ public ASTIndex(Parser p, int i)
+ {
+ super(p, i);
+ }
+
+ /**
+ * @param context
+ * @param data
+ * @return data
+ * @throws TemplateInitException
+ */
+ @Override
+ public Object init(InternalContextAdapter context, Object data)
+ throws TemplateInitException
+ {
+ super.init(context, data);
+ strictRef = rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
+ cleanupParserAndTokens();
+ return data;
+ }
+
+ private final static Object[] noParams = {};
+ private final static Class<?>[] noTypes = {};
+
+ /**
+ * If argument is an Integer and negative, then return (o.size() - argument).
+ * Otherwise return the original argument. We use this to calculate the true
+ * index of a negative index e.g., $foo[-1]. If no size() method is found on the
+ * 'o' object, then we throw an VelocityException.
+ * @param argument
+ * @param o
+ * @param context Used to access the method cache.
+ * @param node ASTNode used for error reporting.
+ * @return found object
+ */
+ public static Object adjMinusIndexArg(Object argument, Object o,
+ InternalContextAdapter context, SimpleNode node)
+ {
+ if (argument instanceof Integer && (Integer) argument < 0)
+ {
+ // The index value is a negative number, $foo[-1], so we want to actually
+ // Index [size - value], so try and call the size method.
+ VelMethod method = ClassUtils.getMethod("size", noParams, noTypes,
+ o, context, node, false);
+ if (method == null)
+ {
+ // The object doesn't have a size method, so there is no notion of "at the end"
+ throw new VelocityException(
+ "A 'size()' method required for negative value "
+ + (Integer) argument + " does not exist for class '"
+ + o.getClass().getName() + "' at " + StringUtils.formatFileString(node),
+ null, node.getRuntimeServices().getLogContext().getStackTrace());
+ }
+
+ Object size = null;
+ try
+ {
+ size = method.invoke(o, noParams);
+ }
+ catch (Exception e)
+ {
+ throw new VelocityException("Error trying to calls the 'size()' method on '"
+ + o.getClass().getName() + "' at " + StringUtils.formatFileString(node), e,
+ node.getRuntimeServices().getLogContext().getStackTrace());
+ }
+
+ int sizeint = 0;
+ try
+ {
+ sizeint = (Integer) size;
+ }
+ catch (ClassCastException e)
+ {
+ // If size() doesn't return an Integer we want to report a pretty error
+ throw new VelocityException("Method 'size()' on class '"
+ + o.getClass().getName() + "' returned '" + size.getClass().getName()
+ + "' when Integer was expected at " + StringUtils.formatFileString(node),
+ null, node.getRuntimeServices().getLogContext().getStackTrace());
+ }
+
+ argument = sizeint + (Integer) argument;
+ }
+
+ // Nothing to do, return the original argument
+ return argument;
+ }
+
+ /**
+ * @param o
+ * @param context
+ * @return object value
+ * @throws MethodInvocationException
+ */
+ @Override
+ public Object execute(Object o, InternalContextAdapter context)
+ throws MethodInvocationException
+ {
+ Object argument = jjtGetChild(0).value(context);
+ // If negative, turn -1 into size - 1
+ argument = adjMinusIndexArg(argument, o, context, this);
+ Object [] params = {argument};
+ Class<?>[] paramClasses = {argument == null ? null : argument.getClass()};
+
+ VelMethod method = ClassUtils.getMethod(methodName, params, paramClasses,
+ o, context, this, strictRef);
+
+ if (method == null) return null;
+
+ try
+ {
+ /*
+ * get the returned object. It may be null, and that is
+ * valid for something declared with a void return type.
+ * Since the caller is expecting something to be returned,
+ * as long as things are peachy, we can return an empty
+ * String so ASTReference() correctly figures out that
+ * all is well.
+ */
+ Object obj = method.invoke(o, params);
+
+ if (obj == null)
+ {
+ if( method.getReturnType() == Void.TYPE)
+ {
+ return "";
+ }
+ }
+
+ return obj;
+ }
+ /*
+ * pass through application level runtime exceptions
+ */
+ catch( RuntimeException e )
+ {
+ throw e;
+ }
+ catch( Exception e )
+ {
+ String msg = "Error invoking method 'get("
+ + (argument == null ? "null" : argument.getClass().getName())
+ + ")' in " + o.getClass().getName()
+ + " at " + StringUtils.formatFileString(this);
+ log.error(msg, e);
+ throw new VelocityException(msg, e, rsvc.getLogContext().getStackTrace());
+ }
+ }
+}