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.lang3.reflect;
18  
19  import java.lang.reflect.AccessibleObject;
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.Member;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  
25  import org.apache.commons.lang3.ClassUtils;
26  
27  /**
28   * Contains common code for working with {@link java.lang.reflect.Method Methods}/{@link java.lang.reflect.Constructor Constructors},
29   * extracted and refactored from {@link MethodUtils} when it was imported from Commons BeanUtils.
30   *
31   * @since 2.5
32   */
33  abstract class MemberUtils {
34      // TODO extract an interface to implement compareParameterSets(...)?
35  
36      private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
37  
38      /** Array of primitive number types ordered by "promotability" */
39      private static final Class<?>[] ORDERED_PRIMITIVE_TYPES = { Byte.TYPE, Short.TYPE,
40              Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE };
41  
42      /**
43       * XXX Default access superclass workaround.
44       *
45       * When a {@code public} class has a default access superclass with {@code public} members,
46       * these members are accessible. Calling them from compiled code works fine.
47       * Unfortunately, on some JVMs, using reflection to invoke these members
48       * seems to (wrongly) prevent access even when the modifier is {@code public}.
49       * Calling {@code setAccessible(true)} solves the problem but will only work from
50       * sufficiently privileged code. Better workarounds would be gratefully
51       * accepted.
52       * @param o the AccessibleObject to set as accessible
53       * @return a boolean indicating whether the accessibility of the object was set to true.
54       */
55      static boolean setAccessibleWorkaround(final AccessibleObject o) {
56          if (o == null || o.isAccessible()) {
57              return false;
58          }
59          final Member m = (Member) o;
60          if (!o.isAccessible() && Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
61              try {
62                  o.setAccessible(true);
63                  return true;
64              } catch (final SecurityException e) { // NOPMD
65                  // ignore in favor of subsequent IllegalAccessException
66              }
67          }
68          return false;
69      }
70  
71      /**
72       * Returns whether a given set of modifiers implies package access.
73       * @param modifiers to test
74       * @return {@code true} unless {@code package}/{@code protected}/{@code private} modifier detected
75       */
76      static boolean isPackageAccess(final int modifiers) {
77          return (modifiers & ACCESS_TEST) == 0;
78      }
79  
80      /**
81       * Returns whether a {@link Member} is accessible.
82       * @param m Member to check
83       * @return {@code true} if {@code m} is accessible
84       */
85      static boolean isAccessible(final Member m) {
86          return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
87      }
88  
89      /**
90       * Compares the relative fitness of two Constructors in terms of how well they
91       * match a set of runtime parameter types, such that a list ordered
92       * by the results of the comparison would return the best match first
93       * (least).
94       *
95       * @param left the "left" Constructor
96       * @param right the "right" Constructor
97       * @param actual the runtime parameter types to match against
98       * {@code left}/{@code right}
99       * @return int consistent with {@code compare} semantics
100      * @since 3.5
101      */
102     static int compareConstructorFit(final Constructor<?> left, final Constructor<?> right, final Class<?>[] actual) {
103       return compareParameterTypes(Executable.of(left), Executable.of(right), actual);
104     }
105 
106     /**
107      * Compares the relative fitness of two Methods in terms of how well they
108      * match a set of runtime parameter types, such that a list ordered
109      * by the results of the comparison would return the best match first
110      * (least).
111      *
112      * @param left the "left" Method
113      * @param right the "right" Method
114      * @param actual the runtime parameter types to match against
115      * {@code left}/{@code right}
116      * @return int consistent with {@code compare} semantics
117      * @since 3.5
118      */
119     static int compareMethodFit(final Method left, final Method right, final Class<?>[] actual) {
120       return compareParameterTypes(Executable.of(left), Executable.of(right), actual);
121     }
122 
123     /**
124      * Compares the relative fitness of two Executables in terms of how well they
125      * match a set of runtime parameter types, such that a list ordered
126      * by the results of the comparison would return the best match first
127      * (least).
128      *
129      * @param left the "left" Executable
130      * @param right the "right" Executable
131      * @param actual the runtime parameter types to match against
132      * {@code left}/{@code right}
133      * @return int consistent with {@code compare} semantics
134      */
135     private static int compareParameterTypes(final Executable left, final Executable right, final Class<?>[] actual) {
136         final float leftCost = getTotalTransformationCost(actual, left);
137         final float rightCost = getTotalTransformationCost(actual, right);
138         return Float.compare(leftCost, rightCost);
139     }
140 
141     /**
142      * Returns the sum of the object transformation cost for each class in the
143      * source argument list.
144      * @param srcArgs The source arguments
145      * @param executable The executable to calculate transformation costs for
146      * @return The total transformation cost
147      */
148     private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Executable executable) {
149         final Class<?>[] destArgs = executable.getParameterTypes();
150         final boolean isVarArgs = executable.isVarArgs();
151 
152         // "source" and "destination" are the actual and declared args respectively.
153         float totalCost = 0.0f;
154         final long normalArgsLen = isVarArgs ? destArgs.length-1 : destArgs.length;
155         if (srcArgs.length < normalArgsLen) {
156             return Float.MAX_VALUE;
157         }
158         for (int i = 0; i < normalArgsLen; i++) {
159             totalCost += getObjectTransformationCost(srcArgs[i], destArgs[i]);
160         }
161         if (isVarArgs) {
162             // When isVarArgs is true, srcArgs and dstArgs may differ in length.
163             // There are two special cases to consider:
164             final boolean noVarArgsPassed = srcArgs.length < destArgs.length;
165             final boolean explicitArrayForVarargs = srcArgs.length == destArgs.length && srcArgs[srcArgs.length-1] != null && srcArgs[srcArgs.length-1].isArray();
166 
167             final float varArgsCost = 0.001f;
168             final Class<?> destClass = destArgs[destArgs.length-1].getComponentType();
169             if (noVarArgsPassed) {
170                 // When no varargs passed, the best match is the most generic matching type, not the most specific.
171                 totalCost += getObjectTransformationCost(destClass, Object.class) + varArgsCost;
172             } else if (explicitArrayForVarargs) {
173                 final Class<?> sourceClass = srcArgs[srcArgs.length-1].getComponentType();
174                 totalCost += getObjectTransformationCost(sourceClass, destClass) + varArgsCost;
175             } else {
176                 // This is typical varargs case.
177                 for (int i = destArgs.length-1; i < srcArgs.length; i++) {
178                     final Class<?> srcClass = srcArgs[i];
179                     totalCost += getObjectTransformationCost(srcClass, destClass) + varArgsCost;
180                 }
181             }
182         }
183         return totalCost;
184     }
185 
186     /**
187      * Gets the number of steps required needed to turn the source class into
188      * the destination class. This represents the number of steps in the object
189      * hierarchy graph.
190      * @param srcClass The source class
191      * @param destClass The destination class
192      * @return The cost of transforming an object
193      */
194     private static float getObjectTransformationCost(Class<?> srcClass, final Class<?> destClass) {
195         if (destClass.isPrimitive()) {
196             return getPrimitivePromotionCost(srcClass, destClass);
197         }
198         float cost = 0.0f;
199         while (srcClass != null && !destClass.equals(srcClass)) {
200             if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass)) {
201                 // slight penalty for interface match.
202                 // we still want an exact match to override an interface match,
203                 // but
204                 // an interface match should override anything where we have to
205                 // get a superclass.
206                 cost += 0.25f;
207                 break;
208             }
209             cost++;
210             srcClass = srcClass.getSuperclass();
211         }
212         /*
213          * If the destination class is null, we've traveled all the way up to
214          * an Object match. We'll penalize this by adding 1.5 to the cost.
215          */
216         if (srcClass == null) {
217             cost += 1.5f;
218         }
219         return cost;
220     }
221 
222     /**
223      * Gets the number of steps required to promote a primitive number to another
224      * type.
225      * @param srcClass the (primitive) source class
226      * @param destClass the (primitive) destination class
227      * @return The cost of promoting the primitive
228      */
229     private static float getPrimitivePromotionCost(final Class<?> srcClass, final Class<?> destClass) {
230         if (srcClass == null) {
231             return 1.5f;
232         }
233         float cost = 0.0f;
234         Class<?> cls = srcClass;
235         if (!cls.isPrimitive()) {
236             // slight unwrapping penalty
237             cost += 0.1f;
238             cls = ClassUtils.wrapperToPrimitive(cls);
239         }
240         for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
241             if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
242                 cost += 0.1f;
243                 if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
244                     cls = ORDERED_PRIMITIVE_TYPES[i + 1];
245                 }
246             }
247         }
248         return cost;
249     }
250 
251     static boolean isMatchingMethod(final Method method, final Class<?>[] parameterTypes) {
252       return isMatchingExecutable(Executable.of(method), parameterTypes);
253     }
254 
255     static boolean isMatchingConstructor(final Constructor<?> method, final Class<?>[] parameterTypes) {
256       return isMatchingExecutable(Executable.of(method), parameterTypes);
257     }
258 
259     private static boolean isMatchingExecutable(final Executable method, final Class<?>[] parameterTypes) {
260         final Class<?>[] methodParameterTypes = method.getParameterTypes();
261         if (ClassUtils.isAssignable(parameterTypes, methodParameterTypes, true)) {
262             return true;
263         }
264 
265         if (method.isVarArgs()) {
266             int i;
267             for (i = 0; i < methodParameterTypes.length - 1 && i < parameterTypes.length; i++) {
268                 if (!ClassUtils.isAssignable(parameterTypes[i], methodParameterTypes[i], true)) {
269                     return false;
270                 }
271             }
272             final Class<?> varArgParameterType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
273             for (; i < parameterTypes.length; i++) {
274                 if (!ClassUtils.isAssignable(parameterTypes[i], varArgParameterType, true)) {
275                     return false;
276                 }
277             }
278             return true;
279         }
280 
281         return false;
282     }
283 
284     /**
285      * <p> A class providing a subset of the API of java.lang.reflect.Executable in Java 1.8,
286      * providing a common representation for function signatures for Constructors and Methods.</p>
287      */
288     private static final class Executable {
289       private final Class<?>[] parameterTypes;
290       private final boolean  isVarArgs;
291 
292       private static Executable of(final Method method) {
293           return new Executable(method);
294       }
295 
296       private static Executable of(final Constructor<?> constructor) {
297           return new Executable(constructor);
298       }
299 
300       private Executable(final Method method) {
301         parameterTypes = method.getParameterTypes();
302         isVarArgs = method.isVarArgs();
303       }
304 
305       private Executable(final Constructor<?> constructor) {
306         parameterTypes = constructor.getParameterTypes();
307         isVarArgs = constructor.isVarArgs();
308       }
309 
310       public Class<?>[] getParameterTypes() {
311           return parameterTypes;
312       }
313 
314       public boolean isVarArgs() {
315           return isVarArgs;
316       }
317     }
318 
319 }