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</code> 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 leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0;
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 destArgs The destination arguments
146      * @param isVarArgs True if the destination arguments are for a varags methods
147      * @return The total transformation cost
148      */
149     private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Executable executable) {
150         final Class<?>[] destArgs = executable.getParameterTypes();
151         final boolean isVarArgs = executable.isVarArgs();
152 
153         // "source" and "destination" are the actual and declared args respectively.
154         float totalCost = 0.0f;
155         final long normalArgsLen = isVarArgs ? destArgs.length-1 : destArgs.length;
156         if (srcArgs.length < normalArgsLen)
157             return Float.MAX_VALUE;
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 explicitArrayForVarags = (srcArgs.length == destArgs.length) && srcArgs[srcArgs.length-1].isArray();
166 
167             final float varArgsCost = 0.001f;
168             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             }
173             else if (explicitArrayForVarags) {
174                 Class<?> sourceClass = srcArgs[srcArgs.length-1].getComponentType();
175                 totalCost += getObjectTransformationCost(sourceClass, destClass) + varArgsCost;
176             }
177             else {
178                 // This is typical varargs case.
179                 for (int i = destArgs.length-1; i < srcArgs.length; i++) {
180                     Class<?> srcClass = srcArgs[i];
181                     totalCost += getObjectTransformationCost(srcClass, destClass) + varArgsCost;
182                 }
183             }
184         }
185         return totalCost;
186     }
187 
188     /**
189      * Gets the number of steps required needed to turn the source class into
190      * the destination class. This represents the number of steps in the object
191      * hierarchy graph.
192      * @param srcClass The source class
193      * @param destClass The destination class
194      * @return The cost of transforming an object
195      */
196     private static float getObjectTransformationCost(Class<?> srcClass, final Class<?> destClass) {
197         if (destClass.isPrimitive()) {
198             return getPrimitivePromotionCost(srcClass, destClass);
199         }
200         float cost = 0.0f;
201         while (srcClass != null && !destClass.equals(srcClass)) {
202             if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass)) {
203                 // slight penalty for interface match.
204                 // we still want an exact match to override an interface match,
205                 // but
206                 // an interface match should override anything where we have to
207                 // get a superclass.
208                 cost += 0.25f;
209                 break;
210             }
211             cost++;
212             srcClass = srcClass.getSuperclass();
213         }
214         /*
215          * If the destination class is null, we've traveled all the way up to
216          * an Object match. We'll penalize this by adding 1.5 to the cost.
217          */
218         if (srcClass == null) {
219             cost += 1.5f;
220         }
221         return cost;
222     }
223 
224     /**
225      * Gets the number of steps required to promote a primitive number to another
226      * type.
227      * @param srcClass the (primitive) source class
228      * @param destClass the (primitive) destination class
229      * @return The cost of promoting the primitive
230      */
231     private static float getPrimitivePromotionCost(final Class<?> srcClass, final Class<?> destClass) {
232         float cost = 0.0f;
233         Class<?> cls = srcClass;
234         if (!cls.isPrimitive()) {
235             // slight unwrapping penalty
236             cost += 0.1f;
237             cls = ClassUtils.wrapperToPrimitive(cls);
238         }
239         for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
240             if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
241                 cost += 0.1f;
242                 if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
243                     cls = ORDERED_PRIMITIVE_TYPES[i + 1];
244                 }
245             }
246         }
247         return cost;
248     }
249 
250     static boolean isMatchingMethod(Method method, Class<?>[] parameterTypes) {
251       return MemberUtils.isMatchingExecutable(Executable.of(method), parameterTypes);
252     }
253 
254     static boolean isMatchingConstructor(Constructor<?> method, Class<?>[] parameterTypes) {
255       return MemberUtils.isMatchingExecutable(Executable.of(method), parameterTypes);
256     }
257 
258     private static boolean isMatchingExecutable(Executable method, Class<?>[] parameterTypes) {
259         final Class<?>[] methodParameterTypes = method.getParameterTypes();
260         if (method.isVarArgs()) {
261             int i;
262             for (i = 0; i < methodParameterTypes.length - 1 && i < parameterTypes.length; i++) {
263                 if (!ClassUtils.isAssignable(parameterTypes[i], methodParameterTypes[i], true)) {
264                     return false;
265                 }
266             }
267             Class<?> varArgParameterType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
268             for (; i < parameterTypes.length; i++) {
269                 if (!ClassUtils.isAssignable(parameterTypes[i], varArgParameterType, true)) {
270                     return false;
271                 }
272             }
273             return true;
274         }
275         return ClassUtils.isAssignable(parameterTypes, methodParameterTypes, true);
276     }
277 
278     /**
279      * <p> A class providing a subset of the API of java.lang.reflect.Executable in Java 1.8,
280      * providing a common representation for function signatures for Constructors and Methods.</p>
281      */
282     private static final class Executable {
283       private final Class<?>[] parameterTypes;
284       private final boolean  isVarArgs;
285 
286       private static Executable of(Method method) { return new Executable(method); }
287       private static Executable of(Constructor<?> constructor) { return new Executable(constructor); }
288 
289       private Executable(Method method) {
290         parameterTypes = method.getParameterTypes();
291         isVarArgs = method.isVarArgs();
292       }
293 
294       private Executable(Constructor<?> constructor) {
295         parameterTypes = constructor.getParameterTypes();
296         isVarArgs = constructor.isVarArgs();
297       }
298 
299       public Class<?>[] getParameterTypes() { return parameterTypes; }
300 
301       public boolean isVarArgs() { return isVarArgs; }
302     }
303 
304 }