View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl2.internal;
18  
19  import java.lang.reflect.Array;
20  import java.lang.reflect.InvocationTargetException;
21  import org.apache.commons.jexl2.internal.introspection.MethodKey;
22  
23  /**
24   * Specialized executor to invoke a method on an object.
25   * @since 2.0
26   */
27  public final class MethodExecutor extends AbstractExecutor.Method {
28      /** Whether this method handles varargs. */
29      private final boolean isVarArgs;
30      /**
31       * Creates a new instance.
32       * @param is the introspector used to discover the method
33       * @param obj the object to find the method in
34       * @param name the method name
35       * @param args the method arguments
36       */
37      public MethodExecutor(Introspector is, Object obj, String name, Object[] args) {
38          super(obj.getClass(), discover(is, obj, name, args));
39          isVarArgs = method != null && isVarArgMethod(method);
40      }
41  
42      /**
43       * Invokes the method to be executed.
44       * @param o the object to invoke the method upon
45       * @param args the method arguments
46       * @return the result of the method invocation
47       * @throws IllegalAccessException Method is inaccessible.
48       * @throws InvocationTargetException Method body throws an exception.
49       */
50      @Override
51      public Object execute(Object o, Object[] args)
52          throws IllegalAccessException, InvocationTargetException  {
53          if (isVarArgs) {
54              Class<?>[] formal = method.getParameterTypes();
55              int index = formal.length - 1;
56              Class<?> type = formal[index].getComponentType();
57              if (args.length >= index) {
58                  args = handleVarArg(type, index, args);
59              }
60          }
61          if (method.getDeclaringClass() == ArrayListWrapper.class && o.getClass().isArray()) {
62              return method.invoke(new ArrayListWrapper(o), args);
63          } else {
64              return method.invoke(o, args);
65          }
66      }
67  
68      /** {@inheritDoc} */
69      @Override
70      public Object tryExecute(String name, Object obj, Object[] args) {
71          MethodKey tkey = new MethodKey(name, args);
72          // let's assume that invocation will fly if the declaring class is the
73          // same and arguments have the same type
74          if (objectClass.equals(obj.getClass()) && tkey.equals(key)) {
75              try {
76                  return execute(obj, args);
77              } catch (InvocationTargetException xinvoke) {
78                  return TRY_FAILED; // fail
79              } catch (IllegalAccessException xill) {
80                  return TRY_FAILED;// fail
81              }
82          }
83          return TRY_FAILED;
84      }
85  
86  
87      /**
88       * Discovers a method for a {@link MethodExecutor}.
89       * <p>
90       * If the object is an array, an attempt will be made to find the
91       * method in a List (see {@link ArrayListWrapper})
92       * </p>
93       * <p>
94       * If the object is a class, an attempt will be made to find the
95       * method as a static method of that class.
96       * </p>
97       * @param is the introspector used to discover the method
98       * @param obj the object to introspect
99       * @param method the name of the method to find
100      * @param args the method arguments
101      * @return a filled up parameter (may contain a null method)
102      */
103     private static Parameter discover(Introspector is,
104             Object obj, String method, Object[] args) {
105         final Class<?> clazz = obj.getClass();
106         final MethodKey key = new MethodKey(method, args);
107         java.lang.reflect.Method m = is.getMethod(clazz, key);
108         if (m == null && clazz.isArray()) {
109             // check for support via our array->list wrapper
110             m = is.getMethod(ArrayListWrapper.class, key);
111         }
112         if (m == null && obj instanceof Class<?>) {
113             m = is.getMethod((Class<?>) obj, key);
114         }
115         return new Parameter(m, key);
116     }
117 
118     /**
119      * Reassembles arguments if the method is a vararg method.
120      * @param type   The vararg class type (aka component type
121      *               of the expected array arg)
122      * @param index  The index of the vararg in the method declaration
123      *               (This will always be one less than the number of
124      *               expected arguments.)
125      * @param actual The actual parameters being passed to this method
126      * @return The actual parameters adjusted for the varargs in order
127      * to fit the method declaration.
128      */
129     protected Object[] handleVarArg(Class<?> type, int index, Object[] actual) {
130         final int size = actual.length - index;
131         // if no values are being passed into the vararg, size == 0
132         if (size == 1) {
133             // if one non-null value is being passed into the vararg,
134             // and that arg is not the sole argument and not an array of the expected type,
135             // make the last arg an array of the expected type
136             if (actual[index] != null) {
137                 Class<?> aclazz = actual[index].getClass();
138                 if (!aclazz.isArray() || !aclazz.getComponentType().equals(type)) {
139                     // create a 1-length array to hold and replace the last argument
140                     Object lastActual = Array.newInstance(type, 1);
141                     Array.set(lastActual, 0, actual[index]);
142                     actual[index] = lastActual;
143                 }
144             }
145             // else, the vararg is null and used as is, considered as T[]
146         } else {
147             // if no or multiple values are being passed into the vararg,
148             // put them in an array of the expected type
149             Object lastActual = Array.newInstance(type, size);
150             for (int i = 0; i < size; i++) {
151                 Array.set(lastActual, i, actual[index + i]);
152             }
153 
154             // put all arguments into a new actual array of the appropriate size
155             Object[] newActual = new Object[index + 1];
156             System.arraycopy(actual, 0, newActual, 0, index);
157             newActual[index] = lastActual;
158 
159             // replace the old actual array
160             actual = newActual;
161         }
162         return actual;
163     }
164 
165    /**
166      * Determines if a method can accept a variable number of arguments.
167      * @param m a the method to check
168      * @return true if method is vararg, false otherwise
169      */
170     private static boolean isVarArgMethod(java.lang.reflect.Method m) {
171         Class<?>[] formal = m.getParameterTypes();
172         if (formal == null || formal.length == 0) {
173             return false;
174         } else {
175             Class<?> last = formal[formal.length - 1];
176             // if the last arg is an array, then
177             // we consider this a varargs method
178             return last.isArray();
179         }
180     }
181 }
182 
183