1 52 53 package freemarker.ext.beans; 54 55 import java.lang.reflect.AccessibleObject ; 56 import java.lang.reflect.Constructor ; 57 import java.lang.reflect.Method ; 58 import java.util.HashMap ; 59 import java.util.Iterator ; 60 import java.util.LinkedList ; 61 import java.util.List ; 62 import java.util.Map ; 63 64 import freemarker.template.TemplateModelException; 65 66 class MethodMap 67 { 68 private static final Class BIGDECIMAL_CLASS = java.math.BigDecimal .class; 69 private static final Class NUMBER_CLASS = java.lang.Number .class; 70 71 private static final Object [] EMPTY_ARGS = new Object [0]; 72 private static final Class NULL_CLASS = java.lang.Object .class; 73 private static final ClassString EMPTY_STRING = new ClassString(EMPTY_ARGS); 74 75 private static final Object NO_SUCH_METHOD = new Object (); 76 private static final Object AMBIGUOUS_METHOD = new Object (); 77 78 private final String name; 79 private final Map cache = new HashMap (); 80 private final List methods = new LinkedList (); 81 82 MethodMap(String name) 83 { 84 this.name = name; 85 } 86 87 void addMethod(Method method) 88 { 89 methods.add(method); 90 } 91 92 void addConstructor(Constructor constructor) 93 { 94 methods.add(constructor); 95 } 96 String getName() 97 { 98 return name; 99 } 100 101 AccessibleObject getMostSpecific(Object [] args) 102 throws TemplateModelException 103 { 104 ClassString cs = null; 105 if(args == null) 106 { 107 args = EMPTY_ARGS; 108 cs = EMPTY_STRING; 109 } 110 else 111 { 112 cs = new ClassString(args); 113 } 114 synchronized(cache) 115 { 116 Object obj = cache.get(cs); 117 if(obj == null) 118 { 119 cache.put(cs, obj = cs.getMostSpecific(methods)); 120 } 121 if(obj instanceof AccessibleObject ) 122 { 123 return (AccessibleObject )obj; 124 } 125 if(obj == NO_SUCH_METHOD) 126 { 127 throw new TemplateModelException("No signature of method " + name + " matches " + cs.listArgumentTypes()); 128 } 129 else 130 { 131 throw new TemplateModelException("Multiple signatures of method " + name + " match " + cs.listArgumentTypes()); 133 } 134 } 135 } 136 137 private static final class ClassString 138 { 139 private final Class [] classes; 140 141 ClassString(Object [] objects) 142 { 143 int l = objects.length; 144 classes = new Class [l]; 145 for(int i = 0; i < l; ++i) 146 { 147 Object obj = objects[i]; 148 classes[i] = obj == null ? NULL_CLASS : obj.getClass(); 149 } 150 } 151 152 public int hashCode() 153 { 154 int hash = 0; 155 for(int i = 0; i < classes.length; ++i) 156 { 157 hash ^= classes[i].hashCode(); 158 } 159 return hash; 160 } 161 162 public boolean equals(Object o) 163 { 164 if(o instanceof ClassString) 165 { 166 ClassString cs = (ClassString)o; 167 if(cs.classes.length != classes.length) 168 { 169 return false; 170 } 171 for(int i = 0; i < classes.length; ++i) 172 { 173 if(cs.classes[i] != classes[i]) 174 { 175 return false; 176 } 177 } 178 return true; 179 } 180 return false; 181 } 182 183 private static final int MORE_SPECIFIC = 0; 184 private static final int LESS_SPECIFIC = 1; 185 private static final int INDETERMINATE = 2; 186 187 Object getMostSpecific(List methods) 188 { 189 LinkedList applicables = getApplicables(methods); 190 if(applicables.isEmpty()) 191 { 192 return NO_SUCH_METHOD; 193 } 194 if(applicables.size() == 1) 195 { 196 return applicables.getFirst(); 197 } 198 LinkedList maximals = new LinkedList (); 199 for (Iterator applicable = applicables.iterator(); 200 applicable.hasNext();) 201 { 202 Object objapp = applicable.next(); 203 Class [] appArgs = getParameterTypes(objapp); 204 boolean lessSpecific = false; 205 for (Iterator maximal = maximals.iterator(); 206 !lessSpecific && maximal.hasNext();) 207 { 208 Object max = maximal.next(); 209 switch(moreSpecific(appArgs, getParameterTypes(max))) 210 { 211 case MORE_SPECIFIC: 212 { 213 maximal.remove(); 214 break; 215 } 216 case LESS_SPECIFIC: 217 { 218 lessSpecific = true; 219 break; 220 } 221 } 222 } 223 if(!lessSpecific) 224 { 225 maximals.addLast(objapp); 226 } 227 } 228 if(maximals.size() > 1) 229 { 230 return AMBIGUOUS_METHOD; 231 } 232 return maximals.getFirst(); 233 } 234 235 private static Class [] getParameterTypes(Object obj) 236 { 237 if(obj instanceof Method ) 238 { 239 return ((Method )obj).getParameterTypes(); 240 } 241 if(obj instanceof Constructor ) 242 { 243 return ((Constructor )obj).getParameterTypes(); 244 } 245 throw new Error (); 247 } 248 249 private static int moreSpecific(Class [] c1, Class [] c2) 250 { 251 boolean c1MoreSpecific = false; 252 boolean c2MoreSpecific = false; 253 for(int i = 0; i < c1.length; ++i) 254 { 255 if(c1[i] != c2[i]) 256 { 257 c1MoreSpecific = 258 c1MoreSpecific || 259 isMoreSpecific(c1[i], c2[i]); 260 c2MoreSpecific = 261 c2MoreSpecific || 262 isMoreSpecific(c2[i], c1[i]); 263 } 264 } 265 if(c1MoreSpecific) 266 { 267 if(c2MoreSpecific) 268 { 269 return INDETERMINATE; 270 } 271 return MORE_SPECIFIC; 272 } 273 if(c2MoreSpecific) 274 { 275 return LESS_SPECIFIC; 276 } 277 return INDETERMINATE; 278 } 279 280 281 285 LinkedList getApplicables(List methods) 286 { 287 LinkedList list = new LinkedList (); 288 for (Iterator imethod = methods.iterator(); imethod.hasNext();) 289 { 290 Object method = imethod.next(); 291 if(isApplicable(method)) 292 { 293 list.add(method); 294 } 295 296 } 297 return list; 298 } 299 300 305 private boolean isApplicable(Object method) 306 { 307 Class [] methodArgs = getParameterTypes(method); 308 if(methodArgs.length != classes.length) 309 { 310 return false; 311 } 312 for(int i = 0; i < classes.length; ++i) 313 { 314 if(!isMethodInvocationConvertible(methodArgs[i], classes[i])) 315 { 316 return false; 317 } 318 } 319 return true; 320 } 321 322 339 private static boolean isMethodInvocationConvertible(Class formal, Class actual) 340 { 341 if(formal.isAssignableFrom(actual)) 343 { 344 return true; 345 } 346 if(formal.isPrimitive()) 349 { 350 if(formal == Boolean.TYPE && actual == Boolean .class) 351 return true; 352 if(formal == Character.TYPE && actual == Character .class) 353 return true; 354 if(formal == Byte.TYPE && actual == Byte .class) 355 return true; 356 if(formal == Short.TYPE && 357 (actual == Short .class || actual == Byte .class)) 358 return true; 359 if(formal == Integer.TYPE && 360 (actual == Integer .class || actual == Short .class || 361 actual == Byte .class)) 362 return true; 363 if(formal == Long.TYPE && 364 (actual == Long .class || actual == Integer .class || 365 actual == Short .class || actual == Byte .class)) 366 return true; 367 if(formal == Float.TYPE && 368 (actual == Float .class || actual == Long .class || 369 actual == Integer .class || actual == Short .class || 370 actual == Byte .class)) 371 return true; 372 if(formal == Double.TYPE && 373 (actual == Double .class || actual == Float .class || 374 actual == Long .class || actual == Integer .class || 375 actual == Short .class || actual == Byte .class)) 376 return true; 377 } 378 return isBigDecimalConvertible(formal, actual); 383 } 384 385 398 private static boolean isMoreSpecific(Class specific, Class generic) 399 { 400 if(generic.isAssignableFrom(specific)) 402 { 403 return true; 404 } 405 if(generic.isPrimitive()) 407 { 408 if(generic == Short.TYPE && (specific == Byte.TYPE)) 409 return true; 410 if(generic == Integer.TYPE && 411 (specific == Short.TYPE || specific == Byte.TYPE)) 412 return true; 413 if(generic == Long.TYPE && 414 (specific == Integer.TYPE || specific == Short.TYPE || 415 specific == Byte.TYPE)) 416 return true; 417 if(generic == Float.TYPE && 418 (specific == Long.TYPE || specific == Integer.TYPE || 419 specific == Short.TYPE || specific == Byte.TYPE)) 420 return true; 421 if(generic == Double.TYPE && 422 (specific == Float.TYPE || specific == Long.TYPE || 423 specific == Integer.TYPE || specific == Short.TYPE || 424 specific == Byte.TYPE)) 425 return true; 426 } 427 return isBigDecimalConvertible(generic, specific); 428 } 429 430 private static boolean isBigDecimalConvertible(Class formal, Class actual) 431 { 432 if(BIGDECIMAL_CLASS.isAssignableFrom(actual)) 434 { 435 if(NUMBER_CLASS.isAssignableFrom(formal)) 436 { 437 return true; 438 } 439 if(formal.isPrimitive() && 440 formal != Boolean.TYPE && formal != Character.TYPE) 441 { 442 return true; 443 } 444 } 445 return false; 446 } 447 448 private String listArgumentTypes() 449 { 450 StringBuffer buf = 451 new StringBuffer (classes.length * 32).append('('); 452 for(int i = 0; i < classes.length; ++i) 453 { 454 buf.append(classes[i].getName()).append(','); 455 } 456 buf.setLength(buf.length() - 1); 457 return buf.append(')').toString(); 458 } 459 } 460 } 461 | Popular Tags |