| 1 46 package groovy.lang; 47 48 import java.beans.BeanInfo ; 49 import java.beans.EventSetDescriptor ; 50 import java.beans.IntrospectionException ; 51 import java.beans.Introspector ; 52 import java.beans.PropertyDescriptor ; 53 import java.lang.reflect.Array ; 54 import java.lang.reflect.Constructor ; 55 import java.lang.reflect.Field ; 56 import java.lang.reflect.InvocationHandler ; 57 import java.lang.reflect.InvocationTargetException ; 58 import java.lang.reflect.Method ; 59 import java.lang.reflect.Modifier ; 60 import java.lang.reflect.Proxy ; 61 import java.math.BigDecimal ; 62 import java.math.BigInteger ; 63 import java.net.URL ; 64 import java.security.AccessControlException ; 65 import java.security.AccessController ; 66 import java.security.PrivilegedAction ; 67 import java.security.PrivilegedActionException ; 68 import java.security.PrivilegedExceptionAction ; 69 import java.util.*; 70 import java.util.logging.Logger ; 71 72 import org.codehaus.groovy.ast.ClassNode; 73 import org.codehaus.groovy.classgen.ReflectorGenerator; 74 import org.codehaus.groovy.control.CompilationUnit; 75 import org.codehaus.groovy.control.Phases; 76 import org.codehaus.groovy.control.CompilerConfiguration; 77 import org.codehaus.groovy.runtime.ClosureListener; 78 import org.codehaus.groovy.runtime.DefaultGroovyMethods; 79 import org.codehaus.groovy.runtime.GroovyCategorySupport; 80 import org.codehaus.groovy.runtime.InvokerHelper; 81 import org.codehaus.groovy.runtime.InvokerInvocationException; 82 import org.codehaus.groovy.runtime.MethodClosure; 83 import org.codehaus.groovy.runtime.MethodHelper; 84 import org.codehaus.groovy.runtime.MethodKey; 85 import org.codehaus.groovy.runtime.NewInstanceMetaMethod; 86 import org.codehaus.groovy.runtime.NewStaticMetaMethod; 87 import org.codehaus.groovy.runtime.ReflectionMetaMethod; 88 import org.codehaus.groovy.runtime.Reflector; 89 import org.codehaus.groovy.runtime.TemporaryMethodKey; 90 import org.codehaus.groovy.runtime.TransformMetaMethod; 91 import org.objectweb.asm.ClassVisitor; 92 import org.objectweb.asm.ClassWriter; 93 94 101 public class MetaClass { 102 103 private static final Logger log = Logger.getLogger(MetaClass.class.getName()); 104 105 public static final Object [] EMPTY_ARRAY = { 106 }; 107 public static Class [] EMPTY_TYPE_ARRAY = { 108 }; 109 protected static final Object [] ARRAY_WITH_NULL = { null }; 110 111 private static boolean useReflection = false; 112 113 protected MetaClassRegistry registry; 114 protected Class theClass; 115 private ClassNode classNode; 116 private Map methodIndex = new HashMap(); 117 private Map staticMethodIndex = new HashMap(); 118 private List newGroovyMethodsList = new ArrayList(); 119 private Map propertyMap = Collections.synchronizedMap(new HashMap()); 121 private Map listeners = new HashMap(); 122 private Map methodCache = Collections.synchronizedMap(new HashMap()); 123 private Map staticMethodCache = Collections.synchronizedMap(new HashMap()); 124 private MetaMethod genericGetMethod; 125 private MetaMethod genericSetMethod; 126 private List constructors; 127 private List allMethods = new ArrayList(); 128 private List interfaceMethods; 129 private Reflector reflector; 130 private boolean initialised; 131 private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty(); 133 134 public MetaClass(MetaClassRegistry registry, final Class theClass) throws IntrospectionException { 135 this.registry = registry; 136 this.theClass = theClass; 137 138 constructors = Arrays.asList(theClass.getDeclaredConstructors()); 139 addMethods(theClass,true); 140 141 BeanInfo info = null; 143 try { 144 info =(BeanInfo ) AccessController.doPrivileged(new PrivilegedExceptionAction () { 145 public Object run() throws IntrospectionException { 146 return Introspector.getBeanInfo(theClass); 147 } 148 }); 149 } catch (PrivilegedActionException pae) { 150 if (pae.getException() instanceof IntrospectionException ) { 151 throw (IntrospectionException ) pae.getException(); 152 } else { 153 throw new RuntimeException (pae.getException()); 154 } 155 } 156 157 PropertyDescriptor [] descriptors = info.getPropertyDescriptors(); 158 159 setupProperties(descriptors); 162 163 169 170 EventSetDescriptor [] eventDescriptors = info.getEventSetDescriptors(); 171 for (int i = 0; i < eventDescriptors.length; i++) { 172 EventSetDescriptor descriptor = eventDescriptors[i]; 173 Method [] listenerMethods = descriptor.getListenerMethods(); 174 for (int j = 0; j < listenerMethods.length; j++) { 175 Method listenerMethod = listenerMethods[j]; 176 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod()); 177 listeners.put(listenerMethod.getName(), metaMethod); 178 } 179 } 180 } 181 182 public static boolean isUseReflection() { 183 return useReflection; 184 } 185 186 192 public static void setUseReflection(boolean useReflection) { 193 MetaClass.useReflection = useReflection; 194 } 195 196 private void addInheritedMethods() { 197 LinkedList superClasses = new LinkedList(); 198 for (Class c = theClass.getSuperclass(); c!=Object .class && c!= null; c = c.getSuperclass()) { 199 superClasses.addFirst(c); 200 } 201 for (Iterator iter = superClasses.iterator(); iter.hasNext();) { 203 Class c = (Class ) iter.next(); 204 addMethods(c,true); 205 addNewStaticMethodsFrom(c); 206 } 207 208 Class [] interfaces = theClass.getInterfaces(); 210 for (int i = 0; i < interfaces.length; i++) { 211 addNewStaticMethodsFrom(interfaces[i]); 212 } 213 214 if (theClass != Object .class) { 217 addMethods(Object .class, false); 218 addNewStaticMethodsFrom(Object .class); 219 } 220 221 if (theClass.isArray() && !theClass.equals(Object [].class)) { 222 addNewStaticMethodsFrom(Object [].class); 223 } 224 } 225 226 230 public List getMethods(String name) { 231 List answer = (List) methodIndex.get(name); 232 List used = GroovyCategorySupport.getCategoryMethods(theClass, name); 233 if (used != null) { 234 if (answer != null) { 235 used.addAll(answer); 236 } 237 answer = used; 238 } 239 if (answer == null) { 240 answer = Collections.EMPTY_LIST; 241 } 242 return answer; 243 } 244 245 249 public List getStaticMethods(String name) { 250 List answer = (List) staticMethodIndex.get(name); 251 if (answer == null) { 252 return Collections.EMPTY_LIST; 253 } 254 return answer; 255 } 256 257 263 protected void addNewInstanceMethod(Method method) { 264 if (initialised) { 265 throw new RuntimeException ("Already initialized, cannot add new method: " + method); 266 } 267 else { 268 NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method)); 269 addMethod(newMethod,false); 270 addNewInstanceMethod(newMethod); 271 } 272 } 273 274 protected void addNewInstanceMethod(MetaMethod method) { 275 newGroovyMethodsList.add(method); 276 } 277 278 protected void addNewStaticMethod(Method method) { 279 if (initialised) { 280 throw new RuntimeException ("Already initialized, cannot add new method: " + method); 281 } 282 else { 283 NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method)); 284 addMethod(newMethod,false); 285 addNewStaticMethod(newMethod); 286 } 287 } 288 289 protected void addNewStaticMethod(MetaMethod method) { 290 newGroovyMethodsList.add(method); 291 } 292 293 public Object invokeMethod(Object object, String methodName, Object arguments) { 294 return invokeMethod(object, methodName, asArray(arguments)); 295 } 296 297 301 public Object invokeMethod(Object object, String methodName, Object [] arguments) { 302 if (object == null) { 303 throw new NullPointerException ("Cannot invoke method: " + methodName + " on null object"); 304 } 305 306 MetaMethod method = retrieveMethod(object, methodName, arguments); 307 308 if (method != null) { 309 return doMethodInvoke(object, method, arguments); 310 } else { 311 try { 313 Object value = this.getProperty(object, methodName); 314 if (value instanceof Closure && object!=this) { 315 Closure closure = (Closure) value; 316 closure.setDelegate(this); 317 return closure.call(arguments); 318 } 319 else { 320 throw new MissingMethodException(methodName, theClass, arguments); 321 } 322 } 323 catch (Exception e) { 324 throw new MissingMethodException(methodName, theClass, arguments); 325 } 326 } 327 } 328 329 protected MetaMethod retrieveMethod(Object owner, String methodName, Object [] arguments) { 330 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments); 332 MetaMethod method = (MetaMethod) methodCache.get(methodKey); 333 if (method == null) { 334 method = pickMethod(owner, methodName, arguments); 335 if (method != null && method.isCacheable()) { 336 methodCache.put(methodKey.createCopy(), method); 337 } 338 } 339 return method; 340 } 341 342 public MetaMethod retrieveMethod(String methodName, Class [] arguments) { 343 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments); 345 MetaMethod method = (MetaMethod) methodCache.get(methodKey); 346 if (method == null) { 347 method = pickMethod(methodName, arguments); if (method != null && method.isCacheable()) { 349 methodCache.put(methodKey.createCopy(), method); 350 } 351 } 352 return method; 353 } 354 355 public Constructor retrieveConstructor(Class [] arguments) { 356 Constructor constructor = (Constructor ) chooseMethod("<init>", constructors, arguments, false); 357 if (constructor != null) { 358 return constructor; 359 } 360 else { 361 constructor = (Constructor ) chooseMethod("<init>", constructors, arguments, true); 362 if (constructor != null) { 363 return constructor; 364 } 365 } 366 return null; 367 } 368 369 public MetaMethod retrieveStaticMethod(String methodName, Class [] arguments) { 370 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments); 371 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey); 372 if (method == null) { 373 method = pickStaticMethod(methodName, arguments); 374 if (method != null) { 375 staticMethodCache.put(methodKey.createCopy(), method); 376 } 377 } 378 return method; 379 } 380 383 protected MetaMethod pickMethod(Object object, String methodName, Object [] arguments) { 384 MetaMethod method = null; 385 List methods = getMethods(methodName); 386 if (!methods.isEmpty()) { 387 Class [] argClasses = convertToTypeArray(arguments); 388 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true); 389 if (method == null) { 390 int size = (arguments != null) ? arguments.length : 0; 391 if (size == 1) { 392 Object firstArgument = arguments[0]; 393 if (firstArgument instanceof List) { 394 398 List list = (List) firstArgument; 399 arguments = list.toArray(); 400 argClasses = convertToTypeArray(arguments); 401 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true); 402 if (method==null) return null; 403 return new TransformMetaMethod(method) { 404 public Object invoke(Object object, Object [] arguments) throws Exception { 405 Object firstArgument = arguments[0]; 406 List list = (List) firstArgument; 407 arguments = list.toArray(); 408 return super.invoke(object, arguments); 409 } 410 }; 411 } 412 } 413 } 414 } 415 return method; 416 } 417 418 425 protected MetaMethod pickMethod(String methodName, Class [] arguments) { 426 MetaMethod method = null; 427 List methods = getMethods(methodName); 428 if (!methods.isEmpty()) { 429 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false); 430 } 435 return method; 436 } 437 438 public Object invokeStaticMethod(Object object, String methodName, Object [] arguments) { 439 444 MethodKey methodKey = new TemporaryMethodKey(methodName, arguments); 446 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey); 447 if (method == null) { 448 method = pickStaticMethod(object, methodName, arguments); 449 if (method != null) { 450 staticMethodCache.put(methodKey.createCopy(), method); 451 } 452 } 453 454 if (method != null) { 455 return doMethodInvoke(object, method, arguments); 456 } 457 476 throw new MissingMethodException(methodName, theClass, arguments); 477 } 478 479 protected MetaMethod pickStaticMethod(Object object, String methodName, Object [] arguments) { 480 MetaMethod method = null; 481 List methods = getStaticMethods(methodName); 482 483 if (!methods.isEmpty()) { 484 method = (MetaMethod) chooseMethod(methodName, methods, convertToTypeArray(arguments), false); 485 } 486 487 if (method == null && theClass != Class .class) { 488 MetaClass classMetaClass = registry.getMetaClass(Class .class); 489 method = classMetaClass.pickMethod(object, methodName, arguments); 490 } 491 return method; 492 } 493 494 protected MetaMethod pickStaticMethod(String methodName, Class [] arguments) { 495 MetaMethod method = null; 496 List methods = getStaticMethods(methodName); 497 498 if (!methods.isEmpty()) { 499 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false); 500 } 505 506 if (method == null && theClass != Class .class) { 507 MetaClass classMetaClass = registry.getMetaClass(Class .class); 508 method = classMetaClass.pickMethod(methodName, arguments); 509 } 510 return method; 511 } 512 513 public Object invokeConstructor(Object [] arguments) { 514 Class [] argClasses = convertToTypeArray(arguments); 515 Constructor constructor = (Constructor ) chooseMethod("<init>", constructors, argClasses, false); 516 if (constructor != null) { 517 return doConstructorInvoke(constructor, arguments); 518 } 519 else { 520 constructor = (Constructor ) chooseMethod("<init>", constructors, argClasses, true); 521 if (constructor != null) { 522 return doConstructorInvoke(constructor, arguments); 523 } 524 } 525 526 if (arguments.length == 1) { 527 Object firstArgument = arguments[0]; 528 if (firstArgument instanceof Map) { 529 constructor = (Constructor ) chooseMethod("<init>", constructors, EMPTY_TYPE_ARRAY, false); 530 if (constructor != null) { 531 Object bean = doConstructorInvoke(constructor, EMPTY_ARRAY); 532 setProperties(bean, ((Map) firstArgument)); 533 return bean; 534 } 535 } 536 } 537 throw new GroovyRuntimeException( 538 "Could not find matching constructor for: " 539 + theClass.getName() 540 + "("+InvokerHelper.toTypeString(arguments)+")"); 541 } 542 543 548 public void setProperties(Object bean, Map map) { 549 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 550 Map.Entry entry = (Map.Entry) iter.next(); 551 String key = entry.getKey().toString(); 552 553 if(propertyMap.get(key) == null) 555 continue; 556 557 Object value = entry.getValue(); 558 try { 559 setProperty(bean, key, value); 560 } 561 catch (GroovyRuntimeException e) { 562 566 } 567 } 568 } 569 570 573 public Object getProperty(final Object object, final String property) { 574 MetaProperty mp = (MetaProperty) propertyMap.get(property); 576 if(mp != null) { 577 try { 578 return mp.getProperty(object); 582 } 583 catch(Exception e) { 584 throw new GroovyRuntimeException("Cannot read property: " + property); 585 } 586 } 587 588 if (genericGetMethod == null) { 589 List possibleGenericMethods = getMethods("get"); 591 if (possibleGenericMethods != null) { 592 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) { 593 MetaMethod mmethod = (MetaMethod) i.next(); 594 Class [] paramTypes = mmethod.getParameterTypes(); 595 if (paramTypes.length == 1 && paramTypes[0] == String .class) { 596 Object [] arguments = {property}; 597 Object answer = doMethodInvoke(object, mmethod, arguments); 598 return answer; 599 } 600 } 601 } 602 } 603 else { 604 Object [] arguments = { property }; 605 Object answer = doMethodInvoke(object, genericGetMethod, arguments); 606 if (answer != null) { 608 return answer; 609 } 610 } 611 612 if (!CompilerConfiguration.isJsrGroovy()) { 613 List methods = getMethods(property); 616 if (!methods.isEmpty()) { 617 return new MethodClosure(object, property); 618 } 619 } 620 621 Exception lastException = null; 624 try { 625 MetaMethod method = findGetter(object, "get" + capitalize(property)); 626 if (method != null) { 627 return doMethodInvoke(object, method, EMPTY_ARRAY); 628 } 629 } 630 catch (GroovyRuntimeException e) { 631 lastException = e; 632 } 633 634 635 if (genericGetMethod != null) { 636 return null; 637 } 638 else { 639 640 if (object instanceof Class ) { 641 return getStaticProperty((Class ) object, property); 643 } 644 if (object instanceof Collection) { 645 return DefaultGroovyMethods.getAt((Collection) object, property); 646 } 647 if (object instanceof Object []) { 648 return DefaultGroovyMethods.getAt(Arrays.asList((Object []) object), property); 649 } 650 if (object instanceof Object ) { 651 Field field = null; 652 try { 653 field = object.getClass().getDeclaredField(property); 655 return field.get(object); 656 } catch (IllegalAccessException iae) { 657 lastException = new IllegalPropertyAccessException(field,object.getClass()); 658 } catch (Exception e1) { 659 } 661 } 662 663 MetaMethod addListenerMethod = (MetaMethod) listeners.get(property); 664 if (addListenerMethod != null) { 665 666 return null; 667 } 668 669 if (lastException == null) 670 throw new MissingPropertyException(property, theClass); 671 else 672 throw new MissingPropertyException(property, theClass, lastException); 673 } 674 } 675 676 680 public List getProperties() { 681 return new ArrayList(propertyMap.values()); 683 } 684 685 689 protected void setupProperties(PropertyDescriptor [] propertyDescriptors) { 690 MetaProperty mp; 691 Method method; 692 MetaMethod getter = null; 693 MetaMethod setter = null; 694 Class klass; 695 696 klass = theClass; 698 while(klass != null) { 699 Field [] fields = klass.getDeclaredFields(); 700 for(int i = 0; i < fields.length; i++) { 701 if((fields[i].getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0) 703 continue; 704 705 if(propertyMap.get(fields[i].getName()) != null) 707 continue; 708 709 propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i])); 713 } 714 715 |