diff options
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.java | 224 |
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()); + } + } +} |