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