View Javadoc

1   /*
2    * Copyright 2002-2006 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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           * get our params
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               * DG: If we can't find an exact match, narrow the parameters and
87               * try again!
88               */
89              if (vm == null) {
90  
91                  // replace all numbers with the smallest type that will fit
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             // else it was already a double
138         } else {
139             long value = original.longValue();
140             if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
141                 // it will fit in a byte
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             // else it was already a long
149         }
150         return result;
151     }
152 
153 }