001 /* 002 $Id: MetaClassImpl.java 4611 2006-12-23 11:17:12Z blackdrag $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package groovy.lang; 047 048 import java.beans.BeanInfo; 049 import java.beans.EventSetDescriptor; 050 import java.beans.IntrospectionException; 051 import java.beans.Introspector; 052 import java.beans.PropertyDescriptor; 053 import java.lang.reflect.Constructor; 054 import java.lang.reflect.Field; 055 import java.lang.reflect.Method; 056 import java.lang.reflect.Modifier; 057 import java.net.URL; 058 import java.security.AccessController; 059 import java.security.PrivilegedAction; 060 import java.security.PrivilegedActionException; 061 import java.security.PrivilegedExceptionAction; 062 import java.util.ArrayList; 063 import java.util.Arrays; 064 import java.util.Collection; 065 import java.util.Collections; 066 import java.util.Comparator; 067 import java.util.HashMap; 068 import java.util.HashSet; 069 import java.util.Iterator; 070 import java.util.LinkedList; 071 import java.util.List; 072 import java.util.Map; 073 import java.util.Set; 074 import java.util.logging.Level; 075 076 import org.codehaus.groovy.GroovyBugError; 077 import org.codehaus.groovy.ast.ClassNode; 078 import org.codehaus.groovy.classgen.BytecodeHelper; 079 import org.codehaus.groovy.control.CompilationUnit; 080 import org.codehaus.groovy.control.Phases; 081 import org.codehaus.groovy.runtime.CurriedClosure; 082 import org.codehaus.groovy.runtime.DefaultGroovyMethods; 083 import org.codehaus.groovy.runtime.DefaultMethodKey; 084 import org.codehaus.groovy.runtime.GroovyCategorySupport; 085 import org.codehaus.groovy.runtime.InvokerHelper; 086 import org.codehaus.groovy.runtime.MetaClassHelper; 087 import org.codehaus.groovy.runtime.MethodClosure; 088 import org.codehaus.groovy.runtime.MethodKey; 089 import org.codehaus.groovy.runtime.NewInstanceMetaMethod; 090 import org.codehaus.groovy.runtime.NewStaticMetaMethod; 091 import org.codehaus.groovy.runtime.ReflectionMetaMethod; 092 import org.codehaus.groovy.runtime.Reflector; 093 import org.codehaus.groovy.runtime.TransformMetaMethod; 094 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; 095 import org.codehaus.groovy.runtime.wrappers.Wrapper; 096 import org.objectweb.asm.ClassVisitor; 097 098 /** 099 * Allows methods to be dynamically added to existing classes at runtime 100 * 101 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 102 * @author Guillaume Laforge 103 * @author Jochen Theodorou 104 * @version $Revision: 4611 $ 105 * @see groovy.lang.MetaClass 106 */ 107 public class MetaClassImpl extends MetaClass { 108 109 protected MetaClassRegistry registry; 110 private ClassNode classNode; 111 private Map classMethodIndex = new HashMap(); 112 private Map classMethodIndexForSuper; 113 private Map classStaticMethodIndex = new HashMap(); 114 private Map classPropertyIndex = new HashMap(); 115 private Map classPropertyIndexForSuper = new HashMap(); 116 private Map staticPropertyIndex = new HashMap(); 117 private Map listeners = new HashMap(); 118 private Map methodCache = Collections.synchronizedMap(new HashMap()); 119 private Map staticMethodCache = Collections.synchronizedMap(new HashMap()); 120 private MetaMethod genericGetMethod; 121 private MetaMethod genericSetMethod; 122 private List constructors; 123 private List allMethods = new ArrayList(); 124 private List interfaceMethods; 125 private Reflector reflector; 126 private boolean initialized; 127 // we only need one of these that can be reused over and over. 128 private MetaProperty arrayLengthProperty = new MetaArrayLengthProperty(); 129 private final static MetaMethod AMBIGOUS_LISTENER_METHOD = new MetaMethod(null,null,new Class[]{},null,0); 130 private static final Object[] EMPTY_ARGUMENTS = {}; 131 private List newGroovyMethodsList = new LinkedList(); 132 133 public MetaClassImpl(MetaClassRegistry registry, final Class theClass) { 134 super(theClass); 135 this.registry = registry; 136 137 constructors = (List) AccessController.doPrivileged(new PrivilegedAction() { 138 public Object run() { 139 return Arrays.asList (theClass.getDeclaredConstructors()); 140 } 141 }); 142 } 143 144 private void fillMethodIndex() { 145 LinkedList superClasses = getSuperClasses(); 146 // let's add all the base class methods 147 for (Iterator iter = superClasses.iterator(); iter.hasNext();) { 148 Class c = (Class) iter.next(); 149 addMethods(c); 150 } 151 152 Set interfaces = new HashSet(); 153 makeInterfaceSet(theClass,interfaces); 154 155 inheritMethods(superClasses,classMethodIndex); 156 inheritInterfaceMethods(interfaces); 157 copyClassMethodIndexForSuper(); 158 159 connectMultimethods(superClasses); 160 populateInterfaces(interfaces); 161 removeMultimethodsOverloadedWithPrivateMethods(); 162 163 replaceWithMOPCalls(); 164 } 165 166 private LinkedList getSuperClasses() { 167 LinkedList superClasses = new LinkedList(); 168 for (Class c = theClass; c!= null; c = c.getSuperclass()) { 169 superClasses.addFirst(c); 170 } 171 if (theClass.isArray() && theClass!=Object[].class && !theClass.getComponentType().isPrimitive()) { 172 superClasses.addFirst(Object[].class); 173 } 174 return superClasses; 175 } 176 177 private void removeMultimethodsOverloadedWithPrivateMethods() { 178 Map privates = new HashMap(); 179 MethodIndexAction mia = new MethodIndexAction() { 180 public List methodNameAction(Class clazz, String methodName, List methods) { 181 boolean hasPrivate=false; 182 for (Iterator iter = methods.iterator(); iter.hasNext();) { 183 MetaMethod method = (MetaMethod) iter.next(); 184 if (method.isPrivate() && clazz == method.getDeclaringClass()) { 185 hasPrivate = true; 186 break; 187 } 188 } 189 if (!hasPrivate) return null; 190 // We have private methods for that name, so remove the 191 // multimethods. That is the same as in our index for 192 // super, so just copy the list from there. It is not 193 // possible to use a pointer here, because the methods 194 // in the index for super are replaced later by MOP 195 // methods like super$5$foo 196 methods.clear(); 197 methods.addAll((Collection) ((Map) classMethodIndexForSuper.get(clazz)).get(methodName)); 198 return methods; 199 } 200 public boolean replaceMethodList() {return false;} 201 }; 202 mia.iterate(classMethodIndex); 203 } 204 205 206 private void replaceWithMOPCalls() { 207 // no MOP methods if not a child of GroovyObject 208 if (!GroovyObject.class.isAssignableFrom(theClass)) return; 209 210 final Map mainClassMethodIndex = (Map) classMethodIndex.get(theClass); 211 class MOPIter extends MethodIndexAction { 212 boolean useThis; 213 public boolean skipClass(Class clazz) { 214 return !useThis && clazz==theClass; 215 } 216 public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) { 217 String mopName = getMOPMethodName(method.getDeclaringClass(), methodName,useThis); 218 List matches = (List) mainClassMethodIndex.get(mopName); 219 if (matches==null) { 220 newList.add(method); 221 return; 222 } 223 matches = new ArrayList(matches); 224 MetaMethod matchingMethod = removeMatchingMethod(matches,method); 225 if (matchingMethod==null) { 226 newList.add(method); 227 return; 228 } else { 229 newList.add(matchingMethod); 230 } 231 } 232 } 233 MOPIter iter = new MOPIter(); 234 235 // replace all calls for super with the correct MOP method 236 iter.useThis = false; 237 iter.iterate(classMethodIndexForSuper); 238 // replace all calls for this with the correct MOP method 239 iter.useThis = true; 240 iter.iterate(classMethodIndex); 241 } 242 243 private String getMOPMethodName(Class declaringClass, String name, boolean useThis) { 244 int distance = 0; 245 for (;declaringClass!=null; declaringClass=declaringClass.getSuperclass()) { 246 distance++; 247 } 248 return (useThis?"this":"super")+"$"+distance+"$"+name; 249 } 250 251 private void copyClassMethodIndexForSuper() { 252 classMethodIndexForSuper = new HashMap(classMethodIndex.size()); 253 for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) { 254 Map.Entry cmiEntry = (Map.Entry) iter.next(); 255 Map methodIndex = (Map) cmiEntry.getValue(); 256 Map copy = new HashMap (methodIndex.size()); 257 for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) { 258 Map.Entry mEntry = (Map.Entry) iterator.next(); 259 copy.put(mEntry.getKey(), new ArrayList((List) mEntry.getValue())); 260 } 261 classMethodIndexForSuper.put(cmiEntry.getKey(),copy); 262 } 263 } 264 265 private void inheritInterfaceMethods(Set interfaces) { 266 // add methods declared by DGM for interfaces 267 List methods = registry.getInstanceMethods(); 268 for (Iterator iter = methods.iterator(); iter.hasNext();) { 269 Method element = (Method) iter.next(); 270 Class dgmClass = element.getParameterTypes()[0]; 271 if (!interfaces.contains(dgmClass)) continue; 272 NewInstanceMetaMethod method = new NewInstanceMetaMethod(createMetaMethod(element)); 273 if (! newGroovyMethodsList.contains(method)){ 274 newGroovyMethodsList.add(method); 275 } 276 Map methodIndex = (Map) classMethodIndex.get(theClass); 277 List list = (List) methodIndex.get(method.getName()); 278 if (list == null) { 279 list = new ArrayList(); 280 methodIndex.put(method.getName(), list); 281 list.add(method); 282 } else { 283 addMethodToList(list,method); 284 } 285 } 286 methods = registry.getStaticMethods(); 287 for (Iterator iter = methods.iterator(); iter.hasNext();) { 288 Method element = (Method) iter.next(); 289 Class dgmClass = element.getParameterTypes()[0]; 290 if (!interfaces.contains(dgmClass)) continue; 291 addNewStaticMethod(element); 292 } 293 } 294 295 private void populateInterfaces(Set interfaces){ 296 Map currentIndex = (Map) classMethodIndex.get(theClass); 297 Map index = new HashMap(); 298 copyNonPrivateMethods(currentIndex,index); 299 for (Iterator iter = interfaces.iterator(); iter.hasNext();) { 300 Class iClass = (Class) iter.next(); 301 Map methodIndex = (Map) classMethodIndex.get(iClass); 302 if (methodIndex==null || methodIndex.size()==0) { 303 classMethodIndex.put(iClass,index); 304 continue; 305 } 306 copyNonPrivateMethods(currentIndex,methodIndex); 307 } 308 } 309 310 private static void makeInterfaceSet(Class c, Set s) { 311 if (c==null) return; 312 Class[] interfaces = c.getInterfaces(); 313 for (int i = 0; i < interfaces.length; i++) { 314 if (!s.contains(interfaces[i])) { 315 s.add(interfaces[i]); 316 makeInterfaceSet(interfaces[i],s); 317 } 318 } 319 makeInterfaceSet(c.getSuperclass(),s); 320 } 321 322 private void copyNonPrivateMethods(Map from, Map to) { 323 for (Iterator iterator = from.entrySet().iterator(); iterator.hasNext();) { 324 Map.Entry element = (Map.Entry) iterator.next(); 325 List oldList = (List) element.getValue(); 326 List newList = (List) to.get(element.getKey()); 327 if (newList==null) { 328 to.put(element.getKey(),new ArrayList(oldList)); 329 } else { 330 addNonPrivateMethods(newList,oldList); 331 } 332 } 333 } 334 335 private void connectMultimethods(List superClasses){ 336 superClasses = DefaultGroovyMethods.reverse(superClasses); 337 Map last = null; 338 for (Iterator iter = superClasses.iterator(); iter.hasNext();) { 339 Class c = (Class) iter.next(); 340 Map methodIndex = (Map) classMethodIndex.get(c); 341 if (methodIndex==last) continue; 342 if (last!=null) copyNonPrivateMethods(last,methodIndex); 343 last = methodIndex; 344 } 345 } 346 347 private void inheritMethods(Collection superClasses, Map classMethodIndex){ 348 Map last = null; 349 for (Iterator iter = superClasses.iterator(); iter.hasNext();) { 350 Class c = (Class) iter.next(); 351 Map methodIndex = (Map) classMethodIndex.get(c); 352 if (last!=null) { 353 if (methodIndex.size()==0) { 354 classMethodIndex.put(c,last); 355 continue; 356 } 357 copyNonPrivateMethods(last,methodIndex); 358 } 359 last = methodIndex; 360 } 361 } 362 363 private void addNonPrivateMethods(List newList, List oldList) { 364 for (Iterator iter = oldList.iterator(); iter.hasNext();) { 365 MetaMethod element = (MetaMethod) iter.next(); 366 if (element.isPrivate()) continue; 367 addMethodToList(newList,element); 368 } 369 } 370 371 /** 372 * @return all the normal instance methods avaiable on this class for the 373 * given name 374 */ 375 private List getMethods(Class sender, String name, boolean isCallToSuper) { 376 Map methodIndex; 377 if (isCallToSuper) { 378 methodIndex = (Map) classMethodIndexForSuper.get(sender); 379 } else { 380 methodIndex = (Map) classMethodIndex.get(sender); 381 } 382 List answer; 383 if (methodIndex!=null) { 384 answer = (List) methodIndex.get(name); 385 if (answer == null) answer = Collections.EMPTY_LIST; 386 } else { 387 answer = Collections.EMPTY_LIST; 388 } 389 390 if (!isCallToSuper && GroovyCategorySupport.hasCategoryInAnyThread()) { 391 List used = GroovyCategorySupport.getCategoryMethods(sender, name); 392 if (used != null) { 393 answer = new ArrayList(answer); 394 for (Iterator iter = used.iterator(); iter.hasNext();) { 395 MetaMethod element = (MetaMethod) iter.next(); 396 removeMatchingMethod(answer,element); 397 } 398 answer.addAll(used); 399 } 400 } 401 return answer; 402 } 403 404 /** 405 * @return all the normal static methods avaiable on this class for the 406 * given name 407 */ 408 private List getStaticMethods(Class sender, String name) { 409 Map methodIndex = (Map) classStaticMethodIndex.get(sender); 410 if (methodIndex == null) return Collections.EMPTY_LIST; 411 List answer = (List) methodIndex.get(name); 412 if (answer == null) return Collections.EMPTY_LIST; 413 return answer; 414 } 415 416 public void addNewInstanceMethod(Method method) { 417 NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method)); 418 if (! newGroovyMethodsList.contains(newMethod)){ 419 newGroovyMethodsList.add(newMethod); 420 addMetaMethod(newMethod); 421 } 422 } 423 424 public void addNewStaticMethod(Method method) { 425 NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method)); 426 if (! newGroovyMethodsList.contains(newMethod)){ 427 newGroovyMethodsList.add(newMethod); 428 addMetaMethod(newMethod); 429 } 430 } 431 432 private void unwrap(Object[] arguments) { 433 // 434 // Temp code to ignore wrapped parameters 435 // The New MOP will deal with these properly 436 // 437 for (int i = 0; i != arguments.length; i++) { 438 if (arguments[i] instanceof Wrapper) { 439 arguments[i] = ((Wrapper)arguments[i]).unwrap(); 440 } 441 } 442 } 443 444 445 /** 446 * Invokes the given method on the object. 447 * @deprecated 448 */ 449 public Object invokeMethod(Object object, String methodName, Object[] originalArguments) { 450 return invokeMethod(theClass,object,methodName,originalArguments,false,false); 451 } 452 453 454 /** 455 * Invokes the given method on the object. 456 * 457 */ 458 public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) { 459 checkInitalised(); 460 if (object == null) { 461 throw new NullPointerException("Cannot invoke method: " + methodName + " on null object"); 462 } 463 if (log.isLoggable(Level.FINER)){ 464 MetaClassHelper.logMethodCall(object, methodName, originalArguments); 465 } 466 Object[] arguments = originalArguments; 467 if (arguments==null) arguments = EMPTY_ARGUMENTS; 468 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); 469 unwrap(arguments); 470 471 MetaMethod method = retrieveMethod(sender, methodName, argClasses, isCallToSuper); 472 473 if (method==null && arguments.length==1 && arguments[0] instanceof List) { 474 Object[] newArguments = ((List) arguments[0]).toArray(); 475 Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments); 476 method = retrieveMethod(sender, methodName, newArgClasses, isCallToSuper); 477 if (method!=null) { 478 MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses); 479 method = new TransformMetaMethod(method) { 480 public Object invoke(Object object, Object[] arguments) { 481 Object firstArgument = arguments[0]; 482 List list = (List) firstArgument; 483 arguments = list.toArray(); 484 return super.invoke(object, arguments); 485 } 486 }; 487 cacheInstanceMethod(methodKey, method); 488 return invokeMethod(sender,object,methodName, originalArguments, isCallToSuper, fromInsideClass); 489 } 490 } 491 492 boolean isClosure = object instanceof Closure; 493 if (isClosure) { 494 Closure closure = (Closure) object; 495 Object delegate = closure.getDelegate(); 496 Object owner = closure.getOwner(); 497 498 499 if ("call".equals(methodName) || "doCall".equals(methodName)) { 500 if (object.getClass()==MethodClosure.class) { 501 MethodClosure mc = (MethodClosure) object; 502 methodName = mc.getMethod(); 503 Class ownerClass = owner.getClass(); 504 if (owner instanceof Class) ownerClass = (Class) owner; 505 MetaClass ownerMetaClass = registry.getMetaClass(ownerClass); 506 return ownerMetaClass.invokeMethod(ownerClass,owner,methodName,arguments,false,false); 507 } else if (object.getClass()==CurriedClosure.class) { 508 CurriedClosure cc = (CurriedClosure) object; 509 // change the arguments for an uncurried call 510 arguments = cc.getUncurriedArguments(arguments); 511 Class ownerClass = owner.getClass(); 512 if (owner instanceof Class) ownerClass = (Class) owner; 513 MetaClass ownerMetaClass = registry.getMetaClass(ownerClass); 514 return ownerMetaClass.invokeMethod(owner,methodName,arguments); 515 } 516 } else if ("curry".equals(methodName)) { 517 return closure.curry(arguments); 518 } 519 520 if (method==null && owner!=closure) { 521 Class ownerClass = owner.getClass(); 522 if (owner instanceof Class) ownerClass = (Class) owner; 523 MetaClass ownerMetaClass = registry.getMetaClass(ownerClass); 524 method = ownerMetaClass.retrieveMethod(methodName,argClasses); 525 if (method!=null) return ownerMetaClass.invokeMethod(owner,methodName,originalArguments); 526 } 527 if (method==null && delegate!=closure && delegate!=null) { 528 Class delegateClass = delegate.getClass(); 529 if (delegate instanceof Class) delegateClass = (Class) delegate; 530 MetaClass delegateMetaClass = registry.getMetaClass(delegateClass); 531 method = delegateMetaClass.retrieveMethod(methodName,argClasses); 532 if (method!=null) return delegateMetaClass.invokeMethod(delegate,methodName,originalArguments); 533 } 534 if (method==null) { 535 // still no methods found, test if delegate or owner are GroovyObjects 536 // and invoke the method on them if so. 537 MissingMethodException last = null; 538 if (owner!=closure && (owner instanceof GroovyObject)) { 539 try { 540 GroovyObject go = (GroovyObject) owner; 541 return go.invokeMethod(methodName,originalArguments); 542 } catch (MissingMethodException mme) { 543 if (last==null) last = mme; 544 } 545 } 546 if (delegate!=closure && (delegate instanceof GroovyObject)) { 547 try { 548 GroovyObject go = (GroovyObject) delegate; 549 return go.invokeMethod(methodName,originalArguments); 550 } catch (MissingMethodException mme) { 551 last = mme; 552 } 553 } 554 if (last!=null) throw last; 555 } 556 557 } 558 559 if (method != null) { 560 return MetaClassHelper.doMethodInvoke(object, method, arguments); 561 } else { 562 // if no method was found, try to find a closure defined as a field of the class and run it 563 try { 564 Object value = this.getProperty(object, methodName); 565 if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this 566 Closure closure = (Closure) value; 567 MetaClass delegateMetaClass = closure.getMetaClass(); 568 return delegateMetaClass.invokeMethod(closure.getClass(),closure,"doCall",originalArguments,false,fromInsideClass); 569 } 570 } catch (MissingPropertyException mpe) {} 571 572 throw new MissingMethodException(methodName, theClass, originalArguments, false); 573 } 574 } 575 576 public MetaMethod retrieveMethod(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) { 577 // lets try use the cache to find the method 578 if (GroovyCategorySupport.hasCategoryInAnyThread() && !isCallToSuper) { 579 return pickMethod(sender, methodName, arguments, isCallToSuper); 580 } else { 581 //TODO: add isSuperCall to key 582 MethodKey methodKey = new DefaultMethodKey(sender, methodName, arguments); 583 MetaMethod method = (MetaMethod) methodCache.get(methodKey); 584 if (method == null) { 585 method = pickMethod(sender, methodName, arguments, isCallToSuper); 586 cacheInstanceMethod(methodKey, method); 587 } 588 return method; 589 } 590 } 591 592 protected void cacheInstanceMethod(MethodKey key, MetaMethod method) { 593 if (method != null && method.isCacheable()) { 594 methodCache.put(key, method); 595 } 596 } 597 598 protected void cacheStaticMethod(MethodKey key, MetaMethod method) { 599 if (method != null && method.isCacheable()) { 600 staticMethodCache.put(key, method); 601 } 602 } 603 604 605 public Constructor retrieveConstructor(Class[] arguments) { 606 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false); 607 if (constructor != null) { 608 return constructor; 609 } 610 constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true); 611 if (constructor != null) { 612 return constructor; 613 } 614 return null; 615 } 616 617 public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) { 618 MethodKey methodKey = new DefaultMethodKey(theClass, methodName, arguments); 619 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey); 620 if (method == null) { 621 method = pickStaticMethod(theClass,methodName, arguments); 622 cacheStaticMethod(methodKey, method); 623 } 624 return method; 625 } 626 627 628 629 /** 630 * pick a method in a strict manner, i.e., without reinterpreting the first List argument. 631 * this method is used only by ClassGenerator for static binding 632 * @param methodName 633 * @param arguments 634 */ 635 public MetaMethod pickMethod(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) { 636 MetaMethod method = null; 637 List methods = getMethods(sender,methodName,isCallToSuper); 638 if (methods!=null && !methods.isEmpty()) { 639 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false); 640 } 641 return method; 642 } 643 644 public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) { 645 checkInitalised(); 646 if (log.isLoggable(Level.FINER)){ 647 MetaClassHelper.logMethodCall(object, methodName, arguments); 648 } 649 650 Class sender = object.getClass(); 651 if (object instanceof Class) sender = (Class) object; 652 if (sender!=theClass) { 653 MetaClass mc = registry.getMetaClass(sender); 654 return mc.invokeStaticMethod(sender,methodName,arguments); 655 } 656 if (sender==Class.class) { 657 return invokeMethod(object,methodName,arguments); 658 } 659 660 if (arguments==null) arguments = EMPTY_ARGUMENTS; 661 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); 662 unwrap(arguments); 663 664 // lets try use the cache to find the method 665 MethodKey methodKey = new DefaultMethodKey(sender, methodName, argClasses); 666 MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey); 667 if (method == null) { 668 method = pickStaticMethod(sender, methodName, argClasses); 669 cacheStaticMethod(methodKey.createCopy(), method); 670 } 671 672 if (method != null) { 673 return MetaClassHelper.doMethodInvoke(object, method, arguments); 674 } 675 676 throw new MissingMethodException(methodName, sender, arguments, true); 677 } 678 679 private MetaMethod pickStaticMethod(Class sender, String methodName, Class[] arguments) { 680 MetaMethod method = null; 681 List methods = getStaticMethods(sender,methodName); 682 683 if (!methods.isEmpty()) { 684 method = (MetaMethod) chooseMethod(methodName, methods, arguments, false); 685 } 686 if (method == null && theClass != Class.class) { 687 MetaClass classMetaClass = registry.getMetaClass(Class.class); 688 method = classMetaClass.pickMethod(methodName, arguments); 689 } 690 if (method == null) { 691 method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true); 692 } 693 return method; 694 } 695 696 public Object invokeConstructor(Object[] arguments) { 697 return invokeConstructor(theClass,arguments,false); 698 } 699 700 public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) { 701 //TODO: that is just a quick prototype, not the real thing! 702 if (numberOfCosntructors != constructors.size()) { 703 throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for "+ 704 this.theClass.getName()+" do not match. Expected "+numberOfCosntructors+" but got "+constructors.size()); 705 } 706 707 if (arguments==null) arguments = EMPTY_ARGUMENTS; 708 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); 709 unwrap(arguments); 710 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false); 711 if (constructor == null) { 712 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true); 713 } 714 if (constructor==null) { 715 throw new GroovyRuntimeException( 716 "Could not find matching constructor for: " 717 + theClass.getName() 718 + "("+InvokerHelper.toTypeString(arguments)+")"); 719 } 720 List l = new ArrayList(constructors); 721 Comparator comp = new Comparator() { 722 public int compare(Object arg0, Object arg1) { 723 Constructor c0 = (Constructor) arg0; 724 Constructor c1 = (Constructor) arg1; 725 String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getParameterTypes()); 726 String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getParameterTypes()); 727 return descriptor0.compareTo(descriptor1); 728 } 729 }; 730 Collections.sort(l,comp); 731 int found = -1; 732 for (int i=0; i<l.size(); i++) { 733 if (l.get(i)!=constructor) continue; 734 found = i; 735 break; 736 } 737 // NOTE: must be changed to "1 |" if constructor was vargs 738 int ret = 0 | (found << 8); 739 return ret; 740 } 741 742 /** 743 * checks if the initialisation of the class id complete. 744 * This method should be called as a form of assert, it is no 745 * way to test if there is still initialisation work to be done. 746 * Such logic must be implemented in a different way. 747 * @throws IllegalStateException if the initialisation is incomplete yet 748 */ 749 protected void checkInitalised() { 750 if (!isInitialized()) 751 throw new IllegalStateException( 752 "initialize must be called for meta " + 753 "class of "+ theClass + 754 "("+this.getClass() + ") " + 755 "to complete initialisation process " + 756 "before any invocation or field/property " + 757 "access can be done"); 758 } 759 760 private Object invokeConstructor(Class at, Object[] arguments, boolean setAccessible) { 761 checkInitalised(); 762 if (arguments==null) arguments = EMPTY_ARGUMENTS; 763 Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); 764 unwrap(arguments); 765 Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false); 766 if (constructor != null) { 767 return doConstructorInvoke(at, constructor, arguments, true); 768 } 769 constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true); 770 if (constructor != null) { 771 return doConstructorInvoke(at, constructor, arguments, true); 772 } 773 774 if (arguments.length == 1) { 775 Object firstArgument = arguments[0]; 776 if (firstArgument instanceof Map) { 777 constructor = (Constructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false); 778 if (constructor != null) { 779 Object bean = doConstructorInvoke(at, constructor, MetaClassHelper.EMPTY_ARRAY, true); 780 setProperties(bean, ((Map) firstArgument)); 781 return bean; 782 } 783 } 784 } 785 throw new GroovyRuntimeException( 786 "Could not find matching constructor for: " 787 + theClass.getName() 788 + "("+InvokerHelper.toTypeString(arguments)+")"); 789 } 790 791 /** 792 * Sets a number of bean properties from the given Map where the keys are 793 * the String names of properties and the values are the values of the 794 * properties to set 795 */ 796 public void setProperties(Object bean, Map map) { 797 checkInitalised(); 798 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 799 Map.Entry entry = (Map.Entry) iter.next(); 800 String key = entry.getKey().toString(); 801 802 Object value = entry.getValue(); 803 setProperty(bean, key, value); 804 } 805 } 806 807 /** 808 * @return the given property's value on the object 809 */ 810 public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) { 811 checkInitalised(); 812 813 //---------------------------------------------------------------------- 814 // handling of static 815 //---------------------------------------------------------------------- 816 boolean isStatic = theClass != Class.class && object instanceof Class; 817 if (isStatic && object != theClass) { 818 MetaClass mc = registry.getMetaClass((Class) object); 819 return mc.getProperty(sender,object,name,useSuper,false); 820 } 821 822 MetaMethod method = null; 823 Object[] arguments = EMPTY_ARGUMENTS; 824 825 //---------------------------------------------------------------------- 826 // getter 827 //---------------------------------------------------------------------- 828 MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic); 829 if (mp != null) { 830 if (mp instanceof MetaBeanProperty) { 831 MetaBeanProperty mbp = (MetaBeanProperty) mp; 832 method = mbp.getGetter(); 833 mp = mbp.getField(); 834 } 835 } 836 837 // check for a category method named like a getter 838 if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) { 839 String getterName = "get"+MetaClassHelper.capitalize(name); 840 method = getCategoryMethodGetter(sender,getterName,false); 841 } 842 843 //---------------------------------------------------------------------- 844 // field 845 //---------------------------------------------------------------------- 846 if (method==null && mp!=null) { 847 return mp.getProperty(object); 848 } 849 850 851 //---------------------------------------------------------------------- 852 // generic get method 853 //---------------------------------------------------------------------- 854 // check for a generic get method provided through a category 855 if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) { 856 method = getCategoryMethodGetter(sender,"get",true); 857 if (method!=null) arguments = new Object[]{name}; 858 } 859 860 // the generic method is valid, if available (!=null), if static or 861 // if it is not static and we do no static access 862 if (method==null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) { 863 arguments = new Object[]{ name }; 864 method = genericGetMethod; 865 } 866 867 //---------------------------------------------------------------------- 868 // special cases 869 //---------------------------------------------------------------------- 870 if (method==null) { 871 /** todo these special cases should be special MetaClasses maybe */ 872 if (theClass != Class.class && object instanceof Class) { 873 MetaClass mc = registry.getMetaClass(Class.class); 874 return mc.getProperty(Class.class,object,name,useSuper,false); 875 } else if (object instanceof Collection) { 876 return DefaultGroovyMethods.getAt((Collection) object, name); 877 } else if (object instanceof Object[]) { 878 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name); 879 } else { 880 MetaMethod addListenerMethod = (MetaMethod) listeners.get(name); 881 if (addListenerMethod != null) { 882 //TODO: one day we could try return the previously registered Closure listener for easy removal 883 return null; 884 } 885 } 886 } else { 887 888 //---------------------------------------------------------------------- 889 // executing the getter method 890 //---------------------------------------------------------------------- 891 return MetaClassHelper.doMethodInvoke(object,method,arguments); 892 } 893 894 //---------------------------------------------------------------------- 895 // error due to missing method/field 896 //---------------------------------------------------------------------- 897 throw new MissingPropertyException(name, theClass); 898 } 899 900 private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) { 901 List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name); 902 if (possibleGenericMethods != null) { 903 for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) { 904 MetaMethod mmethod = (MetaMethod) iter.next(); 905 Class[] paramTypes = mmethod.getParameterTypes(); 906 if (useLongVersion) { 907 if (paramTypes.length==1 && paramTypes[0] == String.class) { 908 return mmethod; 909 } 910 } else { 911 if (paramTypes.length==0) return mmethod; 912 } 913 } 914 } 915 return null; 916 } 917 918 private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) { 919 List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name); 920 if (possibleGenericMethods != null) { 921 for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) { 922 MetaMethod mmethod = (MetaMethod) iter.next(); 923 Class[] paramTypes = mmethod.getParameterTypes(); 924 if (useLongVersion) { 925 if (paramTypes.length==2 && paramTypes[0] == String.class) { 926 return mmethod; 927 } 928 } else { 929 if (paramTypes.length==1) return mmethod; 930 } 931 } 932 } 933 return null; 934 } 935 936 /** 937 * Get all the properties defined for this type 938 * @return a list of MetaProperty objects 939 */ 940 public List getProperties() { 941 checkInitalised(); 942 Map propertyMap = (Map) classPropertyIndex.get(theClass); 943 // simply return the values of the metaproperty map as a List 944 List ret = new ArrayList(propertyMap.size()); 945 for (Iterator iter = propertyMap.values().iterator(); iter.hasNext();) { 946 MetaProperty element = (MetaProperty) iter.next(); 947 if (element instanceof MetaFieldProperty) continue; 948 // filter out DGM beans 949 if (element instanceof MetaBeanProperty) { 950 MetaBeanProperty mp = (MetaBeanProperty) element; 951 boolean setter = true; 952 boolean getter = true; 953 if (mp.getGetter()==null || mp.getGetter() instanceof NewInstanceMetaMethod) { 954 getter=false; 955 } 956 if (mp.getSetter()==null || mp.getSetter() instanceof NewInstanceMetaMethod) { 957 setter=false; 958 } 959 if (!setter && !getter) continue; 960 if (!setter && mp.getSetter()!=null) { 961 element = new MetaBeanProperty(mp.getName(),mp.getType(),mp.getGetter(),null); 962 } 963 if (!getter && mp.getGetter()!=null) { 964 element = new MetaBeanProperty(mp.getName(),mp.getType(), null, mp.getSetter()); 965 } 966 } 967 ret.add(element); 968 } 969 return ret; 970 } 971 972 private MetaMethod findPropertyMethod(List methods, boolean isGetter) { 973 LinkedList ret = new LinkedList(); 974 for (Iterator iter = methods.iterator(); iter.hasNext();) { 975 MetaMethod element = (MetaMethod) iter.next(); 976 if ( !isGetter && 977 //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) && 978 element.getParameterTypes().length == 1) 979 { 980 ret.add(element); 981 } 982 if ( isGetter && 983 !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) && 984 element.getParameterTypes().length == 0) 985 { 986 ret.add(element); 987 } 988 } 989 if (ret.size() == 0) return null; 990 if (ret.size() == 1) return (MetaMethod) ret.getFirst(); 991 992 // we found multiple matching methods 993 // this is a problem, because we can use only one 994 // if it is a getter, then use the most general return 995 // type to decide which method to use. If it is a setter 996 // we use the type of the first parameter 997 MetaMethod method = null; 998 int distance = -1; 999 for (Iterator iter = ret.iterator(); iter.hasNext();) { 1000 MetaMethod element = (MetaMethod) iter.next(); 1001 Class c; 1002 if (isGetter) { 1003 c = element.getReturnType(); 1004 } else { 1005 c = element.getParameterTypes()[0]; 1006 } 1007 int localDistance = distanceToObject(c); 1008 //TODO: maybe implement the case localDistance==distance 1009 if (distance==-1 || distance>localDistance) { 1010 distance = localDistance; 1011 method = element; 1012 } 1013 } 1014 return method; 1015 } 1016 1017 private static int distanceToObject(Class c) { 1018 int count; 1019 for (count=0; c!=null; count++) { 1020 c=c.getSuperclass(); 1021 } 1022 return count; 1023 } 1024 1025 1026 /** 1027 * This will build up the property map (Map of MetaProperty objects, keyed on 1028 * property name). 1029 */ 1030 private void setupProperties(PropertyDescriptor[] propertyDescriptors) { 1031 LinkedList superClasses = getSuperClasses(); 1032 Set interfaces = new HashSet(); 1033 makeInterfaceSet(theClass,interfaces); 1034 1035 // if this an Array, then add the special read-only "length" property 1036 if (theClass.isArray()) { 1037 Map map = new HashMap(); 1038 map.put("length", arrayLengthProperty); 1039 classPropertyIndex.put(theClass,map); 1040 } 1041 1042 inheritStaticInterfaceFields(superClasses, interfaces); 1043 inheritFields(superClasses); 1044 applyPropertyDescriptors(propertyDescriptors); 1045 1046 applyStrayPropertyMethods(superClasses,classMethodIndex,classPropertyIndex); 1047 applyStrayPropertyMethods(superClasses,classMethodIndexForSuper,classPropertyIndexForSuper); 1048 1049 copyClassPropertyIndexForSuper(); 1050 makeStaticPropertyIndex(); 1051 } 1052 1053 private void makeStaticPropertyIndex() { 1054 Map propertyMap = (Map) classPropertyIndex.get(theClass); 1055 for (Iterator iter = propertyMap.entrySet().iterator(); iter.hasNext();) { 1056 Map.Entry entry = (Map.Entry) iter.next(); 1057 MetaProperty mp = (MetaProperty) entry.getValue(); 1058 if (mp instanceof MetaFieldProperty) { 1059 MetaFieldProperty mfp = (MetaFieldProperty) mp; 1060 if (!mfp.isStatic()) continue; 1061 } else if (mp instanceof MetaBeanProperty) { 1062 MetaBeanProperty mbp = (MetaBeanProperty) mp; 1063 boolean getter = mbp.getGetter()==null || mbp.getGetter().isStatic(); 1064 boolean setter = mbp.getSetter()==null || mbp.getSetter().isStatic(); 1065 boolean field = mbp.getField()==null || mbp.getField().isStatic(); 1066 1067 if (!getter && !setter && !field) { 1068 continue; 1069 } else if (setter && getter) { 1070 if (field) { 1071 mp = mbp; // nothing to do 1072 } else { 1073 mp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),mbp.getSetter()); 1074 } 1075 } else if (getter && !setter) { 1076 if (mbp.getGetter()==null) { 1077 mp = mbp.getField(); 1078 } else { 1079 MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),mbp.getGetter(),null); 1080 if (field) newmp.setField(mbp.getField()); 1081 mp = newmp; 1082 } 1083 } else if (setter && !getter) { 1084 if (mbp.getSetter()==null) { 1085 mp = mbp.getField(); 1086 } else { 1087 MetaBeanProperty newmp = new MetaBeanProperty(mbp.getName(),mbp.getType(),null,mbp.getSetter()); 1088 if (field) newmp.setField(mbp.getField()); 1089 mp = newmp; 1090 } 1091 } else if (field) { 1092 mp = mbp.getField(); 1093 } 1094 } else { 1095 continue; // ignore all other types 1096 } 1097 if (mp==null) continue; 1098 staticPropertyIndex.put(entry.getKey(),mp); 1099 } 1100 1101 } 1102 1103 private void copyClassPropertyIndexForSuper() { 1104 for (Iterator iter = classPropertyIndex.entrySet().iterator(); iter.hasNext();) { 1105 Map.Entry entry = (Map.Entry) iter.next(); 1106 HashMap newVal = new HashMap((Map)entry.getValue()); 1107 classPropertyIndexForSuper.put(entry.getKey(),newVal); 1108 } 1109 } 1110 1111 private Map getMap2MapNotNull(Map m, Object key) { 1112 Map ret = (Map) m.get(key); 1113 if (ret==null) { 1114 ret = new HashMap(); 1115 m.put(key,ret); 1116 } 1117 return ret; 1118 } 1119 1120 private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) { 1121 for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) { 1122 Class iclass = (Class) interfaceIter.next(); 1123 Map iPropertyIndex = getMap2MapNotNull(classPropertyIndex,iclass); 1124 addFields(iclass,iPropertyIndex); 1125 for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) { 1126 Class sclass = (Class) classIter.next(); 1127 if (! iclass.isAssignableFrom(sclass)) continue; 1128 Map sPropertyIndex = getMap2MapNotNull(classPropertyIndex,sclass); 1129 copyNonPrivateFields(iPropertyIndex,sPropertyIndex); 1130 } 1131 } 1132 } 1133 1134 private void inheritFields(LinkedList superClasses) { 1135 Map last = null; 1136 for (Iterator iter = superClasses.iterator(); iter.hasNext();) { 1137 Class klass = (Class) iter.next(); 1138 Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass); 1139 if (last != null) { 1140 copyNonPrivateFields(last,propertyIndex); 1141 } 1142 last = propertyIndex; 1143 addFields(klass,propertyIndex); 1144 } 1145 } 1146 1147 private void addFields(final Class klass, Map propertyIndex) { 1148 Field[] fields = (Field[]) AccessController.doPrivileged(new PrivilegedAction() { 1149 public Object run() { 1150 return klass.getDeclaredFields(); 1151 } 1152 }); 1153 for(int i = 0; i < fields.length; i++) { 1154 MetaFieldProperty mfp = new MetaFieldProperty(fields[i]); 1155 propertyIndex.put(fields[i].getName(), mfp); 1156 } 1157 } 1158 1159 private void copyNonPrivateFields(Map from, Map to) { 1160 for (Iterator iter = from.entrySet().iterator(); iter.hasNext();) { 1161 Map.Entry entry = (Map.Entry) iter.next(); 1162 MetaFieldProperty mfp = (MetaFieldProperty) entry.getValue(); 1163 if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue; 1164 to.put(entry.getKey(),mfp); 1165 } 1166 } 1167 1168 private void applyStrayPropertyMethods(LinkedList superClasses, Map classMethodIndex, Map classPropertyIndex) { 1169 // now look for any stray getters that may be used to define a property 1170 for (Iterator iter = superClasses.iterator(); iter.hasNext();) { 1171 Class klass = (Class) iter.next(); 1172 Map methodIndex = (Map) classMethodIndex.get(klass); 1173 Map propertyIndex = getMap2MapNotNull(classPropertyIndex,klass); 1174 for (Iterator nameMethodIterator = methodIndex.entrySet().iterator(); nameMethodIterator.hasNext();) { 1175 Map.Entry entry = (Map.Entry) nameMethodIterator.next(); 1176 String methodName = (String) entry.getKey(); 1177 // name too sort? 1178 if (methodName.length() < 4) continue; 1179 //possible getter/setter 1180 boolean isGetter = methodName.startsWith("get"); 1181 boolean isSetter = methodName.startsWith("set"); 1182 if (!isGetter && !isSetter) continue; 1183 1184 // get the name of the property 1185 String propName = methodName.substring(3,4).toLowerCase() + methodName.substring(4); 1186 MetaMethod propertyMethod = findPropertyMethod((List) entry.getValue(), isGetter); 1187 if (propertyMethod==null) continue; 1188 1189 createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod); 1190 } 1191 } 1192 } 1193 1194 private void createMetaBeanProperty(Map propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod){ 1195 // is this property already accounted for? 1196 MetaProperty mp = (MetaProperty) propertyIndex.get(propName); 1197 if (mp == null) { 1198 if (isGetter) { 1199 mp = new MetaBeanProperty(propName, 1200 propertyMethod.getReturnType(), 1201 propertyMethod, null); 1202 } else { 1203 //isSetter 1204 mp = new MetaBeanProperty(propName, 1205 propertyMethod.getParameterTypes()[0], 1206 null, propertyMethod); 1207 } 1208 } else { 1209 MetaBeanProperty mbp; 1210 MetaFieldProperty mfp; 1211 if (mp instanceof MetaBeanProperty) { 1212 mbp = (MetaBeanProperty) mp; 1213 mfp = mbp.getField(); 1214 } else if (mp instanceof MetaFieldProperty){ 1215 mfp = (MetaFieldProperty) mp; 1216 mbp = new MetaBeanProperty(propName, 1217 mfp.getType(), 1218 null, null); 1219 } else { 1220 throw new GroovyBugError("unknown MetaProperty class used. Class is "+mp.getClass()); 1221 } 1222 // we may have already found one for this name 1223 if (isGetter && mbp.getGetter()==null) { 1224 mbp.setGetter(propertyMethod); 1225 } else if (!isGetter && mbp.getSetter()==null) { 1226 mbp.setSetter(propertyMethod); 1227 } 1228 mbp.setField(mfp); 1229 mp = mbp; 1230 } 1231 propertyIndex.put(propName, mp); 1232 } 1233 1234 private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) { 1235 Map propertyMap = (Map) classPropertyIndex.get(theClass); 1236 // now iterate over the map of property descriptors and generate 1237 // MetaBeanProperty objects 1238 for(int i=0; i<propertyDescriptors.length; i++) { 1239 PropertyDescriptor pd = propertyDescriptors[i]; 1240 1241 // skip if the property type is unknown (this seems to be the case if the 1242 // property descriptor is based on a setX() method that has two parameters, 1243 // which is not a valid property) 1244 if(pd.getPropertyType() == null) 1245 continue; 1246 1247 // get the getter method 1248 Method method = pd.getReadMethod(); 1249 MetaMethod getter; 1250 if(method != null) 1251 getter = findMethod(method); 1252 else 1253 getter = null; 1254 1255 // get the setter method 1256 MetaMethod setter; 1257 method = pd.getWriteMethod(); 1258 if(method != null) 1259 setter = findMethod(method); 1260 else 1261 setter = null; 1262 1263 // now create the MetaProperty object 1264 MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter); 1265 1266 //keep field 1267 MetaFieldProperty field = null; 1268 MetaProperty old = (MetaProperty) propertyMap.get(pd.getName()); 1269 if (old!=null) { 1270 if (old instanceof MetaBeanProperty) { 1271 field = ((MetaBeanProperty) old).getField(); 1272 } else { 1273 field = (MetaFieldProperty) old; 1274 } 1275 mp.setField(field); 1276 } 1277 1278 // put it in the list 1279 // this will overwrite a possible field property 1280 propertyMap.put(pd.getName(), mp); 1281 } 1282 } 1283 1284 /** 1285 * Sets the property value on an object 1286 */ 1287 public void setProperty(Class sender,Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) { 1288 checkInitalised(); 1289 1290 //---------------------------------------------------------------------- 1291 // handling of static 1292 //---------------------------------------------------------------------- 1293 boolean isStatic = theClass != Class.class && object instanceof Class; 1294 if (isStatic && object != theClass) { 1295 MetaClass mc = registry.getMetaClass((Class) object); 1296 mc.getProperty(sender,object,name,useSuper,fromInsideClass); 1297 return; 1298 } 1299 1300 //---------------------------------------------------------------------- 1301 // Unwrap wrapped values fo now - the new MOP will handle them properly 1302 //---------------------------------------------------------------------- 1303 if (newValue instanceof Wrapper) newValue = ((Wrapper)newValue).unwrap(); 1304 1305 1306 1307 MetaMethod method = null; 1308 Object[] arguments = null; 1309 1310 //---------------------------------------------------------------------- 1311 // setter 1312 //---------------------------------------------------------------------- 1313 MetaProperty mp = getMetaProperty(sender,name,useSuper, isStatic); 1314 MetaProperty field = null; 1315 if (mp != null) { 1316 if (mp instanceof MetaBeanProperty) { 1317 MetaBeanProperty mbp = (MetaBeanProperty) mp; 1318 method = mbp.getSetter(); 1319 if (method!=null) arguments = new Object[] { newValue }; 1320 field = mbp.getField(); 1321 } else { 1322 field = mp; 1323 } 1324 } 1325 1326 // check for a category method named like a setter 1327 if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) { 1328 String getterName = "set"+MetaClassHelper.capitalize(name); 1329 method = getCategoryMethodSetter(sender,getterName,false); 1330 if (method!=null) arguments = new Object[] { newValue }; 1331 } 1332 1333 //---------------------------------------------------------------------- 1334 // listener method 1335 //---------------------------------------------------------------------- 1336 boolean ambigousListener = false; 1337 boolean usesProxy = false; 1338 if (method==null) { 1339 method = (MetaMethod) listeners.get(name); 1340 ambigousListener = method == AMBIGOUS_LISTENER_METHOD; 1341 if ( method != null && 1342 !ambigousListener && 1343 newValue instanceof Closure) 1344 { 1345 // lets create a dynamic proxy 1346 Object proxy = 1347 MetaClassHelper.createListenerProxy(method.getParameterTypes()[0], name, (Closure) newValue); 1348 arguments = new Object[] { proxy }; 1349 newValue = proxy; 1350 usesProxy = true; 1351 } else { 1352 method = null; 1353 } 1354 } 1355 1356 //---------------------------------------------------------------------- 1357 // field 1358 //---------------------------------------------------------------------- 1359 if (method==null && field!=null) { 1360 field.setProperty(object,newValue); 1361 return; 1362 } 1363 1364 //---------------------------------------------------------------------- 1365 // generic set method 1366 //---------------------------------------------------------------------- 1367 // check for a generic get method provided through a category 1368 if (method==null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) { 1369 method = getCategoryMethodSetter(sender,"set",true); 1370 if (method!=null) arguments = new Object[]{name,newValue}; 1371 } 1372 1373 // the generic method is valid, if available (!=null), if static or 1374 // if it is not static and we do no static access 1375 if (method==null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) { 1376 arguments = new Object[]{ name, newValue }; 1377 method = genericSetMethod; 1378 } 1379 1380 //---------------------------------------------------------------------- 1381 // executing the getter method 1382 //---------------------------------------------------------------------- 1383 if (method!=null) { 1384 if (arguments.length==1) { 1385 newValue = DefaultTypeTransformation.castToType( 1386 newValue, 1387 method.getParameterTypes()[0]); 1388 arguments[0] = newValue; 1389 } else { 1390 newValue = DefaultTypeTransformation.castToType( 1391 newValue, 1392 method.getParameterTypes()[1]); 1393 arguments[1] = newValue; 1394 } 1395 MetaClassHelper.doMethodInvoke(object,method,arguments); 1396 return; 1397 } 1398 1399 //---------------------------------------------------------------------- 1400 // error due to missing method/field 1401 //---------------------------------------------------------------------- 1402 if (ambigousListener){ 1403 throw new GroovyRuntimeException("There are multiple listeners for the property "+name+". Please do not use the bean short form to access this listener."); 1404 } 1405 throw new MissingPropertyException(name, theClass); 1406 } 1407 1408 private MetaProperty getMetaProperty(Class clazz, String name, boolean useSuper, boolean useStatic) { 1409 Map propertyMap; 1410 if (useStatic) { 1411 propertyMap = staticPropertyIndex; 1412 } else if (useSuper){ 1413 propertyMap = (Map) classPropertyIndexForSuper.get(clazz); 1414 } else { 1415 propertyMap = (Map) classPropertyIndex.get(clazz); 1416 } 1417 if (propertyMap==null) { 1418 if (clazz!=theClass) { 1419 return getMetaProperty(theClass,name,useSuper, useStatic); 1420 } else { 1421 return null; 1422 } 1423 } 1424 return (MetaProperty) propertyMap.get(name); 1425 } 1426 1427 1428 /** 1429 * Looks up the given attribute (field) on the given object 1430 */ 1431 public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) { 1432 checkInitalised(); 1433 1434 boolean isStatic = theClass != Class.class && object instanceof Class; 1435 if (isStatic && object != theClass) { 1436 MetaClass mc = registry.getMetaClass((Class) object); 1437 return mc.getAttribute(sender,object,attribute,useSuper); 1438 } 1439 1440 MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic); 1441 1442 if (mp != null) { 1443 if (mp instanceof MetaBeanProperty) { 1444 MetaBeanProperty mbp = (MetaBeanProperty) mp; 1445 mp = mbp.getField(); 1446 } 1447 try { 1448 // delegate the get operation to the metaproperty 1449 if (mp != null) return mp.getProperty(object); 1450 } catch(Exception e) { 1451 throw new GroovyRuntimeException("Cannot read field: " + attribute,e); 1452 } 1453 } 1454 1455 throw new MissingFieldException(attribute, theClass); 1456 } 1457 1458 /** 1459 * Sets the given attribute (field) on the given object 1460 */ 1461 public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) { 1462 checkInitalised(); 1463 1464 boolean isStatic = theClass != Class.class && object instanceof Class; 1465 if (isStatic && object != theClass) { 1466 MetaClass mc = registry.getMetaClass((Class) object); 1467 mc.setAttribute(sender,object,attribute,newValue,useSuper,fromInsideClass); 1468 return; 1469 } 1470 1471 MetaProperty mp = getMetaProperty(sender,attribute,useSuper, isStatic); 1472 1473 if (mp != null) { 1474 if (mp instanceof MetaBeanProperty) { 1475 MetaBeanProperty mbp = (MetaBeanProperty) mp; 1476 mp = mbp.getField(); 1477 } 1478 if (mp != null) { 1479 mp.setProperty(object,newValue); 1480 return; 1481 } 1482 } 1483 1484 throw new MissingFieldException(attribute, theClass); 1485 } 1486 1487 public ClassNode getClassNode() { 1488 if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) { 1489 // lets try load it from the classpath 1490 String className = theClass.getName(); 1491 String groovyFile = className; 1492 int idx = groovyFile.indexOf('$'); 1493 if (idx > 0) { 1494 groovyFile = groovyFile.substring(0, idx); 1495 } 1496 groovyFile = groovyFile.replace('.', '/') + ".groovy"; 1497 1498 //System.out.println("Attempting to load: " + groovyFile); 1499 URL url = theClass.getClassLoader().getResource(groovyFile); 1500 if (url == null) { 1501 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile); 1502 } 1503 if (url != null) { 1504 try { 1505 1506 /** 1507 * todo there is no CompileUnit in scope so class name 1508 * checking won't work but that mostly affects the bytecode 1509 * generation rather than viewing the AST 1510 */ 1511 CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() { 1512 public void call( ClassVisitor writer, ClassNode node ) { 1513 if( node.getName().equals(theClass.getName()) ) { 1514 MetaClassImpl.this.classNode = node; 1515 } 1516 } 1517 }; 1518 1519 final ClassLoader parent = theClass.getClassLoader(); 1520 GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() { 1521 public Object run() { 1522 return new GroovyClassLoader(parent); 1523 } 1524 }); 1525 CompilationUnit unit = new CompilationUnit( ); 1526 unit.setClassgenCallback( search ); 1527 unit.addSource( url ); 1528 unit.compile( Phases.CLASS_GENERATION ); 1529 } 1530 catch (Exception e) { 1531 throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e); 1532 } 1533 } 1534 1535 } 1536 return classNode; 1537 } 1538 1539 public String toString() { 1540 return super.toString() + "[" + theClass + "]"; 1541 } 1542 1543 // Implementation methods 1544 //------------------------------------------------------------------------- 1545 1546 /** 1547 * Adds all the methods declared in the given class to the metaclass 1548 * ignoring any matching methods already defined by a derived class 1549 * 1550 * @param theClass 1551 */ 1552 private void addMethods(final Class theClass) { 1553 Map methodIndex = (Map) classMethodIndex.get(theClass); 1554 if (methodIndex==null) { 1555 methodIndex = new HashMap(); 1556 classMethodIndex.put(theClass,methodIndex); 1557 } 1558 // add methods directly declared in the class 1559 Method[] methodArray = (Method[]) AccessController.doPrivileged(new PrivilegedAction() { 1560 public Object run() { 1561 return theClass.getDeclaredMethods(); 1562 } 1563 }); 1564 for (int i = 0; i < methodArray.length; i++) { 1565 Method reflectionMethod = methodArray[i]; 1566 if ( reflectionMethod.getName().indexOf('+') >= 0 ) { 1567 // Skip Synthetic methods inserted by JDK 1.5 compilers and later 1568 continue; 1569 } else if (Modifier.isAbstract(reflectionMethod.getModifiers())) { 1570 continue; 1571 } 1572 MetaMethod method = createMetaMethod(reflectionMethod); 1573 addMetaMethod(method); 1574 } 1575 // add methods declared by DGM 1576 List methods = registry.getInstanceMethods(); 1577 for (Iterator iter = methods.iterator(); iter.hasNext();) { 1578 Method element = (Method) iter.next(); 1579 if (element.getParameterTypes()[0]!=theClass) continue; 1580 addNewInstanceMethod(element); 1581 } 1582 // add static methods declared by DGM 1583 methods = registry.getStaticMethods(); 1584 for (Iterator iter = methods.iterator(); iter.hasNext();) { 1585 Method element = (Method) iter.next(); 1586 if (element.getParameterTypes()[0]!=theClass) continue; 1587 addNewStaticMethod(element); 1588 } 1589 } 1590 1591 private void addToClassMethodIndex(MetaMethod method, Map classMethodIndex) { 1592 Map methodIndex = (Map) classMethodIndex.get(method.getDeclaringClass()); 1593 if (methodIndex==null) { 1594 methodIndex = new HashMap(); 1595 classMethodIndex.put(method.getDeclaringClass(),methodIndex); 1596 } 1597 String name = method.getName(); 1598 List list = (List) methodIndex.get(name); 1599 if (list == null) { 1600 list = new ArrayList(); 1601 methodIndex.put(name, list); 1602 list.add(method); 1603 } else { 1604 addMethodToList(list,method); 1605 } 1606 } 1607 1608 /** 1609 * adds a MetaMethod to this class. WARNING: this method will not 1610 * do the neccessary steps for multimethod logic and using this 1611 * method doesn't mean, that a method added here is replacing another 1612 * method from a parent class completely. These steps are usually done 1613 * by initalize, which means if you need these steps, you have to add 1614 * the method before running initialize the first time. 1615 * @see #initialize() 1616 * @param method the MetaMethod 1617 */ 1618 protected void addMetaMethod(MetaMethod method) { 1619 if (isInitialized()) { 1620 throw new RuntimeException("Already initialized, cannot add new method: " + method); 1621 } 1622 if (isGenericGetMethod(method) && genericGetMethod == null) { 1623 genericGetMethod = method; 1624 } 1625 else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) { 1626 genericSetMethod = method; 1627 } 1628 if (method.isStatic()) { 1629 addToClassMethodIndex(method,classStaticMethodIndex); 1630 } 1631 addToClassMethodIndex(method,classMethodIndex); 1632 } 1633 1634 protected boolean isInitialized(){ 1635 return initialized; 1636 } 1637 1638 private void addMethodToList(List list, MetaMethod method) { 1639 MetaMethod match = removeMatchingMethod(list,method); 1640 if (match==null) { 1641 list.add(method); 1642 } else if (match.isPrivate()){ 1643 // do not overwrite private methods 1644 // Note: private methods from parent classes are not shown here, 1645 // but when doing the multimethod connection step, we overwrite 1646 // methods of the parent class with methods of a subclass and 1647 // in that case we want to keep the private methods 1648 list.add(match); 1649 } else { 1650 Class methodC = method.getDeclaringClass(); 1651 Class matchC = match.getDeclaringClass(); 1652 if (methodC == matchC){ 1653 if (method instanceof NewInstanceMetaMethod) { 1654 // let DGM replace existing methods 1655 list.add(method); 1656 } else { 1657 list.add(match); 1658 } 1659 } else if (MetaClassHelper.isAssignableFrom(methodC,matchC)){ 1660 list.add(match); 1661 } else { 1662 list.add(method); 1663 } 1664 } 1665 } 1666 1667 /** 1668 * remove a method of the same matching prototype was found in the list 1669 */ 1670 private MetaMethod removeMatchingMethod(List list, MetaMethod method) { 1671 for (Iterator iter = list.iterator(); iter.hasNext();) { 1672 MetaMethod aMethod = (MetaMethod) iter.next(); 1673 Class[] params1 = aMethod.getParameterTypes(); 1674 Class[] params2 = method.getParameterTypes(); 1675 if (params1.length == params2.length) { 1676 boolean matches = true; 1677 for (int i = 0; i < params1.length; i++) { 1678 if (params1[i] != params2[i]) { 1679 matches = false; 1680 break; 1681 } 1682 } 1683 if (matches) { 1684 iter.remove(); 1685 return (MetaMethod) aMethod; 1686 } 1687 } 1688 } 1689 return null; 1690 } 1691 1692 /** 1693 * @return the matching method which should be found 1694 */ 1695 private MetaMethod findMethod(Method aMethod) { 1696 List methods = getMethods(theClass,aMethod.getName(),false); 1697 for (Iterator iter = methods.iterator(); iter.hasNext();) { 1698 MetaMethod method = (MetaMethod) iter.next(); 1699 if (method.isMethod(aMethod)) { 1700 return method; 1701 } 1702 } 1703 //log.warning("Creating reflection based dispatcher for: " + aMethod); 1704 return new ReflectionMetaMethod(aMethod); 1705 } 1706 1707 /** 1708 * @return the getter method for the given object 1709 */ 1710 private MetaMethod findGetter(Object object, String name) { 1711 List methods = getMethods(theClass,name,false); 1712 for (Iterator iter = methods.iterator(); iter.hasNext();) { 1713 MetaMethod method = (MetaMethod) iter.next(); 1714 if (method.getParameterTypes().length == 0) { 1715 return method; 1716 } 1717 } 1718 return null; 1719 } 1720 1721 /** 1722 * @return the Method of the given name with no parameters or null 1723 */ 1724 private MetaMethod findStaticGetter(Class type, String name) { 1725 List methods = getStaticMethods(type, name); 1726 for (Iterator iter = methods.iterator(); iter.hasNext();) { 1727 MetaMethod method = (MetaMethod) iter.next(); 1728 if (method.getParameterTypes().length == 0) { 1729 return method; 1730 } 1731 } 1732 1733 /** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */ 1734 try { 1735 Method method = type.getMethod(name, MetaClassHelper.EMPTY_TYPE_ARRAY); 1736 if ((method.getModifiers() & Modifier.STATIC) != 0) { 1737 return findMethod(method); 1738 } 1739 else { 1740 return null; 1741 } 1742 } 1743 catch (Exception e) { 1744 return null; 1745 } 1746 } 1747 1748 private static Object doConstructorInvoke(final Class at, Constructor constructor, Object[] argumentArray, boolean setAccessible) { 1749 if (log.isLoggable(Level.FINER)) { 1750 MetaClassHelper.logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray); 1751 } 1752 1753 if (setAccessible) { 1754 // To fix JIRA 435 1755 // Every constructor should be opened to the accessible classes. 1756 final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor); 1757 final Constructor ctor = constructor; 1758 AccessController.doPrivileged(new PrivilegedAction() { 1759 public Object run() { 1760 ctor.setAccessible(accessible); 1761 return null; 1762 } 1763 }); 1764 } 1765 return MetaClassHelper.doConstructorInvoke(constructor,argumentArray); 1766 } 1767 1768 /** 1769 * Chooses the correct method to use from a list of methods which match by 1770 * name. 1771 * 1772 * @param methods 1773 * the possible methods to choose from 1774 * @param arguments 1775 * the original argument to the method 1776 */ 1777 private Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) { 1778 int methodCount = methods.size(); 1779 if (methodCount <= 0) { 1780 return null; 1781 } 1782 else if (methodCount == 1) { 1783 Object method = methods.get(0); 1784 if (MetaClassHelper.isValidMethod(method, arguments, coerce)) { 1785 return method; 1786 } 1787 return null; 1788 } 1789 Object answer = null; 1790 if (arguments == null || arguments.length == 0) { 1791 answer = MetaClassHelper.chooseEmptyMethodParams(methods); 1792 } 1793 else if (arguments.length == 1 && arguments[0] == null) { 1794 answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods); 1795 } 1796 else { 1797 List matchingMethods = new ArrayList(); 1798 1799 for (Iterator iter = methods.iterator(); iter.hasNext();) { 1800 Object method = iter.next(); 1801 1802 // making this false helps find matches 1803 if (MetaClassHelper.isValidMethod(method, arguments, coerce)) { 1804 matchingMethods.add(method); 1805 } 1806 } 1807 if (matchingMethods.isEmpty()) { 1808 return null; 1809 } 1810 else if (matchingMethods.size() == 1) { 1811 return matchingMethods.get(0); 1812 } 1813 return chooseMostSpecificParams(methodName, matchingMethods, arguments); 1814 1815 } 1816 if (answer != null) { 1817 return answer; 1818 } 1819 throw new GroovyRuntimeException( 1820 "Could not find which method to invoke from this list: " 1821 + methods 1822 + " for arguments: " 1823 + InvokerHelper.toString(arguments)); 1824 } 1825 1826 private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) { 1827 1828 long matchesDistance = -1; 1829 LinkedList matches = new LinkedList(); 1830 for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) { 1831 Object method = iter.next(); 1832 Class[] paramTypes = MetaClassHelper.getParameterTypes(method); 1833 if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue; 1834 long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes); 1835 if (dist==0) return method; 1836 if (matches.size()==0) { 1837 matches.add(method); 1838 matchesDistance = dist; 1839 } else if (dist<matchesDistance) { 1840 matchesDistance=dist; 1841 matches.clear(); 1842 matches.add(method); 1843 } else if (dist==matchesDistance) { 1844 matches.add(method); 1845 } 1846 1847 } 1848 if (matches.size()==1) { 1849 return matches.getFirst(); 1850 } 1851 if (matches.size()==0) { 1852 return null; 1853 } 1854 1855 //more than one matching method found --> ambigous! 1856 String msg = "Ambiguous method overloading for method "; 1857 msg+= theClass.getName()+"#"+name; 1858 msg+= ".\nCannot resolve which method to invoke for "; 1859 msg+= InvokerHelper.toString(arguments); 1860 msg+= " due to overlapping prototypes between:"; 1861 for (Iterator iter = matches.iterator(); iter.hasNext();) { 1862 Class[] types=MetaClassHelper.getParameterTypes(iter.next()); 1863 msg+= "\n\t"+InvokerHelper.toString(types); 1864 } 1865 throw new GroovyRuntimeException(msg); 1866 } 1867 1868 private boolean isGenericGetMethod(MetaMethod method) { 1869 if (method.getName().equals("get")) { 1870 Class[] parameterTypes = method.getParameterTypes(); 1871 return parameterTypes.length == 1 && parameterTypes[0] == String.class; 1872 } 1873 return false; 1874 } 1875 1876 /** 1877 * Call this method when any mutation method is called, such as adding a new 1878 * method to this MetaClass so that any caching or bytecode generation can be 1879 * regenerated. 1880 */ 1881 private synchronized void onMethodChange() { 1882 reflector = null; 1883 } 1884 1885 1886 public synchronized void initialize() { 1887 if (!isInitialized()) { 1888 fillMethodIndex(); 1889 addProperties(); 1890 initialized = true; 1891 } 1892 if (reflector == null) { 1893 generateReflector(); 1894 } 1895 } 1896 1897 private void addProperties() { 1898 BeanInfo info; 1899 // introspect 1900 try { 1901 info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() { 1902 public Object run() throws IntrospectionException { 1903 return Introspector.getBeanInfo(theClass); 1904 } 1905 }); 1906 } catch (PrivilegedActionException pae) { 1907 throw new GroovyRuntimeException("exception while bean introspection",pae.getException()); 1908 } 1909 PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); 1910 1911 // build up the metaproperties based on the public fields, property descriptors, 1912 // and the getters and setters 1913 setupProperties(descriptors); 1914 1915 EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors(); 1916 for (int i = 0; i < eventDescriptors.length; i++) { 1917 EventSetDescriptor descriptor = eventDescriptors[i]; 1918 Method[] listenerMethods = descriptor.getListenerMethods(); 1919 for (int j = 0; j < listenerMethods.length; j++) { 1920 Method listenerMethod = listenerMethods[j]; 1921 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod()); 1922 String name = listenerMethod.getName(); 1923 if (listeners.containsKey(name)) { 1924 listeners.put(name, AMBIGOUS_LISTENER_METHOD); 1925 } else{ 1926 listeners.put(name, metaMethod); 1927 } 1928 } 1929 } 1930 } 1931 1932 private MetaMethod createMetaMethod(final Method method) { 1933 if (registry.useAccessible()) { 1934 AccessController.doPrivileged(new PrivilegedAction() { 1935 public Object run() { 1936 method.setAccessible(true); 1937 return null; 1938 } 1939 }); 1940 } 1941 1942 MetaMethod answer = new MetaMethod(method); 1943 if (isValidReflectorMethod(answer)) { 1944 allMethods.add(answer); 1945 answer.setMethodIndex(allMethods.size()); 1946 } 1947 else { 1948 //log.warning("Creating reflection based dispatcher for: " + method); 1949 answer = new ReflectionMetaMethod(method); 1950 } 1951 1952 if (useReflection) { 1953 //log.warning("Creating reflection based dispatcher for: " + method); 1954 return new ReflectionMetaMethod(method); 1955 } 1956 1957 return answer; 1958 } 1959 1960 private boolean isValidReflectorMethod(MetaMethod method) { 1961 // We cannot use a reflector if the method is private, protected, or package accessible only. 1962 if (!method.isPublic()) { 1963 return false; 1964 } 1965 // lets see if this method is implemented on an interface 1966 List interfaceMethods = getInterfaceMethods(); 1967 for (Iterator iter = interfaceMethods.iterator(); iter.hasNext();) { 1968 MetaMethod aMethod = (MetaMethod) iter.next(); 1969 if (method.isSame(aMethod)) { 1970 method.setInterfaceClass(aMethod.getCallClass()); 1971 return true; 1972 } 1973 } 1974 // it's no interface method, so try to find the highest class 1975 // in hierarchy defining this method 1976 Class declaringClass = method.getCallClass(); 1977 for (Class clazz=declaringClass; clazz!=null; clazz=clazz.getSuperclass()) { 1978 try { 1979 final Class klazz = clazz; 1980 final String mName = method.getName(); 1981 final Class[] parms = method.getParameterTypes(); 1982 try { 1983 Method m = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { 1984 public Object run() throws NoSuchMethodException { 1985 return klazz.getDeclaredMethod(mName, parms); 1986 } 1987 }); 1988 if (!Modifier.isPublic(clazz.getModifiers())) continue; 1989 if (!Modifier.isPublic(m.getModifiers())) continue; 1990 declaringClass = clazz; 1991 } catch (PrivilegedActionException pae) { 1992 if (pae.getException() instanceof NoSuchMethodException) { 1993 throw (NoSuchMethodException) pae.getException(); 1994 } else { 1995 throw new RuntimeException(pae.getException()); 1996 } 1997 } 1998 } catch (SecurityException e) { 1999 continue; 2000 } catch (NoSuchMethodException e) { 2001 continue; 2002 } 2003 } 2004 if (!Modifier.isPublic(declaringClass.getModifiers())) return false; 2005 method.setCallClass(declaringClass); 2006 2007 return true; 2008 } 2009 2010 private void generateReflector() { 2011 reflector = registry.loadReflector(theClass, allMethods); 2012 if (reflector == null) { 2013 throw new RuntimeException("Should have a reflector for "+theClass.getName()); 2014 } 2015 // lets set the reflector on all the methods 2016 for (Iterator iter = allMethods.iterator(); iter.hasNext();) { 2017 MetaMethod metaMethod = (MetaMethod) iter.next(); 2018 metaMethod.setReflector(reflector); 2019 } 2020 } 2021 2022 public List getMethods() { 2023 return allMethods; 2024 } 2025 2026 public List getMetaMethods() { 2027 return new ArrayList(newGroovyMethodsList); 2028 } 2029 2030 private synchronized List getInterfaceMethods() { 2031 if (interfaceMethods == null) { 2032 interfaceMethods = new ArrayList(); 2033 Class type = theClass; 2034 while (type != null) { 2035 Class[] interfaces = type.getInterfaces(); 2036 for (int i = 0; i < interfaces.length; i++) { 2037 Class iface = interfaces[i]; 2038 Method[] methods = iface.getMethods(); 2039 addInterfaceMethods(interfaceMethods, methods); 2040 } 2041 type = type.getSuperclass(); 2042 } 2043 } 2044 return interfaceMethods; 2045 } 2046 2047 private void addInterfaceMethods(List list, Method[] methods) { 2048 for (int i = 0; i < methods.length; i++) { 2049 list.add(createMetaMethod(methods[i])); 2050 } 2051 } 2052 2053 private static class MethodIndexAction { 2054 public void iterate(Map classMethodIndex){ 2055 for (Iterator iter = classMethodIndex.entrySet().iterator(); iter.hasNext();) { 2056 Map.Entry classEntry = (Map.Entry) iter.next(); 2057 Map methodIndex = (Map) classEntry.getValue(); 2058 Class clazz = (Class) classEntry.getKey(); 2059 if (skipClass(clazz)) continue; 2060 for (Iterator iterator = methodIndex.entrySet().iterator(); iterator.hasNext();) { 2061 Map.Entry nameEntry = (Map.Entry) iterator.next(); 2062 String name = (String) nameEntry.getKey(); 2063 List oldList = (List) nameEntry.getValue(); 2064 List newList = methodNameAction(clazz, name, oldList); 2065 if (replaceMethodList()) nameEntry.setValue(newList); 2066 } 2067 } 2068 } 2069 public List methodNameAction(Class clazz, String methodName, List methods) { 2070 List newList = new ArrayList(methods.size()); 2071 for (Iterator methodIter = methods.iterator(); methodIter.hasNext();) { 2072 MetaMethod method = (MetaMethod) methodIter.next(); 2073 methodListAction(clazz,methodName,method,methods,newList); 2074 } 2075 return newList; 2076 } 2077 public boolean skipClass(Class clazz) {return false;} 2078 public void methodListAction(Class clazz, String methodName, MetaMethod method, List oldList, List newList) {} 2079 public boolean replaceMethodList(){return true;} 2080 } 2081 2082 /** 2083 * @deprecated 2084 */ 2085 public Object getProperty(Object object, String property) { 2086 return getProperty(theClass,object,property,false,false); 2087 } 2088 2089 /** 2090 * @deprecated 2091 */ 2092 public void setProperty(Object object, String property, Object newValue) { 2093 setProperty(theClass,object,property,newValue,false,false); 2094 } 2095 2096 /** 2097 * @deprecated 2098 */ 2099 public Object getAttribute(Object object, String attribute) { 2100 return getAttribute(theClass,object,attribute,false,false); 2101 } 2102 2103 /** 2104 * @deprecated 2105 */ 2106 public void setAttribute(Object object, String attribute, Object newValue) { 2107 setAttribute(theClass,object,attribute,newValue,false,false); 2108 } 2109 2110 public MetaMethod pickMethod(String methodName, Class[] arguments) { 2111 return pickMethod(theClass,methodName,arguments,false); 2112 } 2113 2114 protected MetaMethod retrieveMethod(String methodName, Class[] arguments) { 2115 return retrieveMethod(theClass,methodName,arguments,false); 2116 } 2117 2118 /** 2119 * remove all method call cache entries. This should be done if a 2120 * method is added during runtime, but not by using a category. 2121 */ 2122 protected void clearInvocationCaches() { 2123 staticMethodCache.clear(); 2124 methodCache.clear(); 2125 } 2126 }