1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.jexl.parser;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21
22 import org.apache.commons.jexl.JexlContext;
23 import org.apache.commons.jexl.util.Introspector;
24 import org.apache.commons.jexl.util.introspection.VelMethod;
25 import org.apache.commons.jexl.util.introspection.Info;
26
27 /***
28 * Method execution.
29 */
30 public class ASTMethod extends SimpleNode {
31 /*** dummy velocity info. */
32 private static final Info DUMMY = new Info("", 1, 1);
33
34 /***
35 * Create the node given an id.
36 *
37 * @param id node id.
38 */
39 public ASTMethod(int id) {
40 super(id);
41 }
42
43 /***
44 * Create a node with the given parser and id.
45 *
46 * @param p a parser.
47 * @param id node id.
48 */
49 public ASTMethod(Parser p, int id) {
50 super(p, id);
51 }
52
53 /*** {@inheritDoc} */
54 public Object jjtAccept(ParserVisitor visitor, Object data) {
55 return visitor.visit(this, data);
56 }
57
58 /***
59 * evaluate a method invocation upon a base object.
60 *
61 * foo.bar(2)
62 *
63 * @param jc the {@link JexlContext} to evaluate against.
64 * @param obj The object to have the method invoked.
65 * @return the value of the method invocation.
66 * @throws Exception on any error
67 */
68 public Object execute(Object obj, JexlContext jc) throws Exception {
69 String methodName = ((ASTIdentifier) jjtGetChild(0)).val;
70
71 int paramCount = jjtGetNumChildren() - 1;
72
73
74
75
76
77 Object[] params = new Object[paramCount];
78
79 try {
80 for (int i = 0; i < paramCount; i++) {
81 params[i] = ((SimpleNode) jjtGetChild(i + 1)).value(jc);
82 }
83
84 VelMethod vm = Introspector.getUberspect().getMethod(obj, methodName, params, DUMMY);
85
86
87
88
89 if (vm == null) {
90
91
92 for (int i = 0; i < params.length; i++) {
93 Object param = params[i];
94 if (param instanceof Number) {
95 params[i] = narrow((Number) param);
96 }
97 }
98 vm = Introspector.getUberspect().getMethod(obj, methodName, params, DUMMY);
99 if (vm == null) {
100 return null;
101 }
102 }
103
104 return vm.invoke(obj, params);
105 } catch (InvocationTargetException e) {
106 Throwable t = e.getTargetException();
107
108 if (t instanceof Exception) {
109 throw (Exception) t;
110 }
111
112 throw e;
113 }
114 }
115
116 /***
117 * Given a Number, return back the value using the smallest type the result
118 * will fit into. This works hand in hand with parameter 'widening' in java
119 * method calls, e.g. a call to substring(int,int) with an int and a long
120 * will fail, but a call to substring(int,int) with an int and a short will
121 * succeed.
122 *
123 * @param original the original number.
124 * @return a value of the smallest type the original number will fit into.
125 * @since 1.1
126 */
127 private Number narrow(Number original) {
128 if (original == null || original instanceof BigDecimal || original instanceof BigInteger) {
129 return original;
130 }
131 Number result = original;
132 if (original instanceof Double || original instanceof Float) {
133 double value = original.doubleValue();
134 if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) {
135 result = new Float(result.floatValue());
136 }
137
138 } else {
139 long value = original.longValue();
140 if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
141
142 result = new Byte((byte) value);
143 } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
144 result = new Short((short) value);
145 } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
146 result = new Integer((int) value);
147 }
148
149 }
150 return result;
151 }
152
153 }