1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.model.interpolation.reflection;
20
21 import java.lang.reflect.Method;
22 import java.util.ArrayList;
23 import java.util.Hashtable;
24 import java.util.Iterator;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28
29 class MethodMap {
30 private static final int MORE_SPECIFIC = 0;
31
32 private static final int LESS_SPECIFIC = 1;
33
34 private static final int INCOMPARABLE = 2;
35
36 /**
37 * Keep track of all methods with the same name.
38 */
39 private final Map<String, List<Method>> methodByNameMap = new Hashtable<>();
40
41 /**
42 * Add a method to a list of methods by name.
43 * For a particular class we are keeping track
44 * of all the methods with the same name.
45 *
46 * @param method The method
47 */
48 void add(Method method) {
49 String methodName = method.getName();
50
51 List<Method> l = get(methodName);
52
53 if (l == null) {
54 l = new ArrayList<>();
55 methodByNameMap.put(methodName, l);
56 }
57
58 l.add(method);
59 }
60
61 /**
62 * Return a list of methods with the same name.
63 *
64 * @param key The name of the method.
65 * @return List list of methods
66 */
67 List<Method> get(String key) {
68 return methodByNameMap.get(key);
69 }
70
71 /**
72 * Find a method. Attempts to find the
73 * most specific applicable method using the
74 * algorithm described in the JLS section
75 * 15.12.2 (with the exception that it can't
76 * distinguish a primitive type argument from
77 * an object type argument, since in reflection
78 * primitive type arguments are represented by
79 * their object counterparts, so for an argument of
80 * type (say) java.lang.Integer, it will not be able
81 * to decide between a method that takes int and a
82 * method that takes java.lang.Integer as a parameter.
83 * <p>
84 * This turns out to be a relatively rare case
85 * where this is needed - however, functionality
86 * like this is needed.
87 *
88 * @param methodName name of method
89 * @param args the actual arguments with which the method is called
90 * @return the most specific applicable method, or null if no
91 * method is applicable.
92 * @throws AmbiguousException if there is more than one maximally
93 * specific applicable method
94 */
95 Method find(String methodName, Object... args) throws AmbiguousException {
96 List<Method> methodList = get(methodName);
97
98 if (methodList == null) {
99 return null;
100 }
101
102 int l = args.length;
103 Class<?>[] classes = new Class[l];
104
105 for (int i = 0; i < l; ++i) {
106 Object arg = args[i];
107 // if we are careful down below, a null argument goes in there
108 // so we can know that the null was passed to the method
109 classes[i] = arg == null ? null : arg.getClass();
110 }
111
112 return getMostSpecific(methodList, classes);
113 }
114
115 /**
116 * simple distinguishable exception, used when
117 * we run across ambiguous overloading
118 */
119 static class AmbiguousException extends Exception {
120
121 private static final long serialVersionUID = 751688436639650618L;
122 }
123
124 private static Method getMostSpecific(List<Method> methods, Class<?>... classes) throws AmbiguousException {
125 LinkedList<Method> applicables = getApplicables(methods, classes);
126
127 if (applicables.isEmpty()) {
128 return null;
129 }
130
131 if (applicables.size() == 1) {
132 return applicables.getFirst();
133 }
134
135 // This list will contain the maximally specific methods. Hopefully at
136 // the end of the below loop, the list will contain exactly one method,
137 // (the most specific method) otherwise we have ambiguity.
138 LinkedList<Method> maximals = new LinkedList<>();
139
140 for (Method app : applicables) {
141 Class<?>[] appArgs = app.getParameterTypes();
142 boolean lessSpecific = false;
143
144 for (Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); ) {
145 Method max = maximal.next();
146
147 switch (moreSpecific(appArgs, max.getParameterTypes())) {
148 case MORE_SPECIFIC:
149 // This method is more specific than the previously
150 // known maximally specific, so remove the old maximum.
151 maximal.remove();
152 break;
153
154 case LESS_SPECIFIC:
155 // This method is less specific than some of the
156 // currently known maximally specific methods, so we
157 // won't add it into the set of maximally specific
158 // methods
159 lessSpecific = true;
160 break;
161
162 default:
163 }
164 }
165
166 if (!lessSpecific) {
167 maximals.addLast(app);
168 }
169 }
170
171 if (maximals.size() > 1) {
172 // We have more than one maximally specific method
173 throw new AmbiguousException();
174 }
175
176 return maximals.getFirst();
177 }
178
179 /**
180 * Determines which method signature (represented by a class array) is more
181 * specific. This defines a partial ordering on the method signatures.
182 *
183 * @param c1 first signature to compare
184 * @param c2 second signature to compare
185 * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
186 * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
187 */
188 private static int moreSpecific(Class<?>[] c1, Class<?>[] c2) {
189 boolean c1MoreSpecific = false;
190 boolean c2MoreSpecific = false;
191
192 for (int i = 0; i < c1.length; ++i) {
193 if (c1[i] != c2[i]) {
194 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible(c2[i], c1[i]);
195 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible(c1[i], c2[i]);
196 }
197 }
198
199 if (c1MoreSpecific) {
200 if (c2MoreSpecific) {
201 // Incomparable due to cross-assignable arguments (i.e.
202 // foo(String, Object) vs. foo(Object, String))
203 return INCOMPARABLE;
204 }
205
206 return MORE_SPECIFIC;
207 }
208
209 if (c2MoreSpecific) {
210 return LESS_SPECIFIC;
211 }
212
213 // Incomparable due to non-related arguments (i.e.
214 // foo(Runnable) vs. foo(Serializable))
215 return INCOMPARABLE;
216 }
217
218 /**
219 * Returns all methods that are applicable to actual argument types.
220 *
221 * @param methods list of all candidate methods
222 * @param classes the actual types of the arguments
223 * @return a list that contains only applicable methods (number of
224 * formal and actual arguments matches, and argument types are assignable
225 * to formal types through a method invocation conversion).
226 */
227 private static LinkedList<Method> getApplicables(List<Method> methods, Class<?>... classes) {
228 LinkedList<Method> list = new LinkedList<>();
229
230 for (Method method : methods) {
231 if (isApplicable(method, classes)) {
232 list.add(method);
233 }
234 }
235 return list;
236 }
237
238 /**
239 * Returns true if the supplied method is applicable to actual
240 * argument types.
241 *
242 * @param method The method to check for applicability
243 * @param classes The arguments
244 * @return true if the method applies to the parameter types
245 */
246 private static boolean isApplicable(Method method, Class<?>... classes) {
247 Class<?>[] methodArgs = method.getParameterTypes();
248
249 if (methodArgs.length != classes.length) {
250 return false;
251 }
252
253 for (int i = 0; i < classes.length; ++i) {
254 if (!isMethodInvocationConvertible(methodArgs[i], classes[i])) {
255 return false;
256 }
257 }
258
259 return true;
260 }
261
262 /**
263 * Determines whether a type represented by a class object is
264 * convertible to another type represented by a class object using a
265 * method invocation conversion, treating object types of primitive
266 * types as if they were primitive types (that is, a Boolean actual
267 * parameter type matches boolean primitive formal type). This behavior
268 * is because this method is used to determine applicable methods for
269 * an actual parameter list, and primitive types are represented by
270 * their object duals in reflective method calls.
271 *
272 * @param formal the formal parameter type to which the actual
273 * parameter type should be convertible
274 * @param actual the actual parameter type.
275 * @return true if either formal type is assignable from actual type,
276 * or formal is a primitive type and actual is its corresponding object
277 * type or an object type of a primitive type that can be converted to
278 * the formal type.
279 */
280 private static boolean isMethodInvocationConvertible(Class<?> formal, Class<?> actual) {
281 // if it's a null, it means the arg was null
282 if (actual == null && !formal.isPrimitive()) {
283 return true;
284 }
285
286 // Check for identity or widening reference conversion
287 if (actual != null && formal.isAssignableFrom(actual)) {
288 return true;
289 }
290
291 // Check for boxing with widening primitive conversion. Note that
292 // actual parameters are never primitives.
293 if (formal.isPrimitive()) {
294 if (formal == Boolean.TYPE && actual == Boolean.class) {
295 return true;
296 }
297 if (formal == Character.TYPE && actual == Character.class) {
298 return true;
299 }
300 if (formal == Byte.TYPE && actual == Byte.class) {
301 return true;
302 }
303 if (formal == Short.TYPE && (actual == Short.class || actual == Byte.class)) {
304 return true;
305 }
306 if (formal == Integer.TYPE && (actual == Integer.class || actual == Short.class || actual == Byte.class)) {
307 return true;
308 }
309 if (formal == Long.TYPE
310 && (actual == Long.class
311 || actual == Integer.class
312 || actual == Short.class
313 || actual == Byte.class)) {
314 return true;
315 }
316 if (formal == Float.TYPE
317 && (actual == Float.class
318 || actual == Long.class
319 || actual == Integer.class
320 || actual == Short.class
321 || actual == Byte.class)) {
322 return true;
323 }
324 if (formal == Double.TYPE
325 && (actual == Double.class
326 || actual == Float.class
327 || actual == Long.class
328 || actual == Integer.class
329 || actual == Short.class
330 || actual == Byte.class)) {
331 return true;
332 }
333 }
334
335 return false;
336 }
337
338 /**
339 * Determines whether a type represented by a class object is
340 * convertible to another type represented by a class object using a
341 * method invocation conversion, without matching object and primitive
342 * types. This method is used to determine the more specific type when
343 * comparing signatures of methods.
344 *
345 * @param formal the formal parameter type to which the actual
346 * parameter type should be convertible
347 * @param actual the actual parameter type.
348 * @return true if either formal type is assignable from actual type,
349 * or formal and actual are both primitive types and actual can be
350 * subject to widening conversion to formal.
351 */
352 private static boolean isStrictMethodInvocationConvertible(Class<?> formal, Class<?> actual) {
353 // we shouldn't get a null into, but if so
354 if (actual == null && !formal.isPrimitive()) {
355 return true;
356 }
357
358 // Check for identity or widening reference conversion
359 if (formal.isAssignableFrom(actual)) {
360 return true;
361 }
362
363 // Check for widening primitive conversion.
364 if (formal.isPrimitive()) {
365 if (formal == Short.TYPE && (actual == Byte.TYPE)) {
366 return true;
367 }
368 if (formal == Integer.TYPE && (actual == Short.TYPE || actual == Byte.TYPE)) {
369 return true;
370 }
371 if (formal == Long.TYPE && (actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
372 return true;
373 }
374 if (formal == Float.TYPE
375 && (actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
376 return true;
377 }
378 if (formal == Double.TYPE
379 && (actual == Float.TYPE
380 || actual == Long.TYPE
381 || actual == Integer.TYPE
382 || actual == Short.TYPE
383 || actual == Byte.TYPE)) {
384 return true;
385 }
386 }
387 return false;
388 }
389 }