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 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 explicitArrayForVarags = srcArgs.length == destArgs.length && 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 (explicitArrayForVarags) {
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         float cost = 0.0f;
231         Class<?> cls = srcClass;
232         if (!cls.isPrimitive()) {
233             // slight unwrapping penalty
234             cost += 0.1f;
235             cls = ClassUtils.wrapperToPrimitive(cls);
236         }
237         for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
238             if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
239                 cost += 0.1f;
240                 if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
241                     cls = ORDERED_PRIMITIVE_TYPES[i + 1];
242                 }
243             }
244         }
245         return cost;
246     }
247 
248     static boolean isMatchingMethod(final Method method, final Class<?>[] parameterTypes) {
249       return MemberUtils.isMatchingExecutable(Executable.of(method), parameterTypes);
250     }
251 
252     static boolean isMatchingConstructor(final Constructor<?> method, final Class<?>[] parameterTypes) {
253       return MemberUtils.isMatchingExecutable(Executable.of(method), parameterTypes);
254     }
255 
256     private static boolean isMatchingExecutable(final Executable method, final Class<?>[] parameterTypes) {
257         final Class<?>[] methodParameterTypes = method.getParameterTypes();
258         if (method.isVarArgs()) {
259             int i;
260             for (i = 0; i < methodParameterTypes.length - 1 && i < parameterTypes.length; i++) {
261                 if (!ClassUtils.isAssignable(parameterTypes[i], methodParameterTypes[i], true)) {
262                     return false;
263                 }
264             }
265             final Class<?> varArgParameterType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
266             for (; i < parameterTypes.length; i++) {
267                 if (!ClassUtils.isAssignable(parameterTypes[i], varArgParameterType, true)) {
268                     return false;
269                 }
270             }
271             return true;
272         }
273         return ClassUtils.isAssignable(parameterTypes, methodParameterTypes, true);
274     }
275 
276     /**
277      * <p> A class providing a subset of the API of java.lang.reflect.Executable in Java 1.8,
278      * providing a common representation for function signatures for Constructors and Methods.</p>
279      */
280     private static final class Executable {
281       private final Class<?>[] parameterTypes;
282       private final boolean  isVarArgs;
283 
284       private static Executable of(final Method method) {
285           return new Executable(method);
286       }
287 
288       private static Executable of(final Constructor<?> constructor) {
289           return new Executable(constructor);
290       }
291 
292       private Executable(final Method method) {
293         parameterTypes = method.getParameterTypes();
294         isVarArgs = method.isVarArgs();
295       }
296 
297       private Executable(final Constructor<?> constructor) {
298         parameterTypes = constructor.getParameterTypes();
299         isVarArgs = constructor.isVarArgs();
300       }
301 
302       public Class<?>[] getParameterTypes() {
303           return parameterTypes;
304       }
305 
306       public boolean isVarArgs() {
307           return isVarArgs;
308       }
309     }
310 
311 }