001: /*
002: * Copyright 2002-2006 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jexl.parser;
017:
018: import java.lang.reflect.InvocationTargetException;
019: import java.math.BigDecimal;
020: import java.math.BigInteger;
021:
022: import org.apache.commons.jexl.JexlContext;
023: import org.apache.commons.jexl.util.Introspector;
024: import org.apache.commons.jexl.util.introspection.VelMethod;
025: import org.apache.commons.jexl.util.introspection.Info;
026:
027: /**
028: * Method execution.
029: */
030: public class ASTMethod extends SimpleNode {
031: /** dummy velocity info. */
032: private static final Info DUMMY = new Info("", 1, 1);
033:
034: /**
035: * Create the node given an id.
036: *
037: * @param id node id.
038: */
039: public ASTMethod(int id) {
040: super (id);
041: }
042:
043: /**
044: * Create a node with the given parser and id.
045: *
046: * @param p a parser.
047: * @param id node id.
048: */
049: public ASTMethod(Parser p, int id) {
050: super (p, id);
051: }
052:
053: /** {@inheritDoc} */
054: public Object jjtAccept(ParserVisitor visitor, Object data) {
055: return visitor.visit(this , data);
056: }
057:
058: /**
059: * evaluate a method invocation upon a base object.
060: *
061: * foo.bar(2)
062: *
063: * @param jc the {@link JexlContext} to evaluate against.
064: * @param obj The object to have the method invoked.
065: * @return the value of the method invocation.
066: * @throws Exception on any error
067: */
068: public Object execute(Object obj, JexlContext jc) throws Exception {
069: String methodName = ((ASTIdentifier) jjtGetChild(0)).val;
070:
071: int paramCount = jjtGetNumChildren() - 1;
072:
073: /*
074: * get our params
075: */
076:
077: Object[] params = new Object[paramCount];
078:
079: try {
080: for (int i = 0; i < paramCount; i++) {
081: params[i] = ((SimpleNode) jjtGetChild(i + 1)).value(jc);
082: }
083:
084: VelMethod vm = Introspector.getUberspect().getMethod(obj,
085: methodName, params, DUMMY);
086: /*
087: * DG: If we can't find an exact match, narrow the parameters and
088: * try again!
089: */
090: if (vm == null) {
091:
092: // replace all numbers with the smallest type that will fit
093: for (int i = 0; i < params.length; i++) {
094: Object param = params[i];
095: if (param instanceof Number) {
096: params[i] = narrow((Number) param);
097: }
098: }
099: vm = Introspector.getUberspect().getMethod(obj,
100: methodName, params, DUMMY);
101: if (vm == null) {
102: return null;
103: }
104: }
105:
106: return vm.invoke(obj, params);
107: } catch (InvocationTargetException e) {
108: Throwable t = e.getTargetException();
109:
110: if (t instanceof Exception) {
111: throw (Exception) t;
112: }
113:
114: throw e;
115: }
116: }
117:
118: /**
119: * Given a Number, return back the value using the smallest type the result
120: * will fit into. This works hand in hand with parameter 'widening' in java
121: * method calls, e.g. a call to substring(int,int) with an int and a long
122: * will fail, but a call to substring(int,int) with an int and a short will
123: * succeed.
124: *
125: * @param original the original number.
126: * @return a value of the smallest type the original number will fit into.
127: * @since 1.1
128: */
129: private Number narrow(Number original) {
130: if (original == null || original instanceof BigDecimal
131: || original instanceof BigInteger) {
132: return original;
133: }
134: Number result = original;
135: if (original instanceof Double || original instanceof Float) {
136: double value = original.doubleValue();
137: if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) {
138: result = new Float(result.floatValue());
139: }
140: // else it was already a double
141: } else {
142: long value = original.longValue();
143: if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
144: // it will fit in a byte
145: result = new Byte((byte) value);
146: } else if (value <= Short.MAX_VALUE
147: && value >= Short.MIN_VALUE) {
148: result = new Short((short) value);
149: } else if (value <= Integer.MAX_VALUE
150: && value >= Integer.MIN_VALUE) {
151: result = new Integer((int) value);
152: }
153: // else it was already a long
154: }
155: return result;
156: }
157:
158: }
|