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