Clover coverage report - groovy - 1.0-beta-6
Coverage timestamp: Thu Jul 15 2004 13:18:22 BST
file stats: LOC: 2,272   Methods: 79
NCLOC: 1,636   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
MetaClass.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  $Id: MetaClass.java,v 1.78 2004/07/13 19:43:08 jstrachan Exp $
 3   
 
 4   
  Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
 5   
 
 6   
  Redistribution and use of this software and associated documentation
 7   
  ("Software"), with or without modification, are permitted provided
 8   
  that the following conditions are met:
 9   
 
 10   
  1. Redistributions of source code must retain copyright
 11   
     statements and notices.  Redistributions must also contain a
 12   
     copy of this document.
 13   
 
 14   
  2. Redistributions in binary form must reproduce the
 15   
     above copyright notice, this list of conditions and the
 16   
     following disclaimer in the documentation and/or other
 17   
     materials provided with the distribution.
 18   
 
 19   
  3. The name "groovy" must not be used to endorse or promote
 20   
     products derived from this Software without prior written
 21   
     permission of The Codehaus.  For written permission,
 22   
     please contact info@codehaus.org.
 23   
 
 24   
  4. Products derived from this Software may not be called "groovy"
 25   
     nor may "groovy" appear in their names without prior written
 26   
     permission of The Codehaus. "groovy" is a registered
 27   
     trademark of The Codehaus.
 28   
 
 29   
  5. Due credit should be given to The Codehaus -
 30   
     http://groovy.codehaus.org/
 31   
 
 32   
  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
 33   
  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
 34   
  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 35   
  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 36   
  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 37   
  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 38   
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 39   
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 40   
  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 41   
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 42   
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 43   
  OF THE POSSIBILITY OF SUCH DAMAGE.
 44   
 
 45   
  */
 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.ArrayList;
 70   
 import java.util.Arrays;
 71   
 import java.util.Collection;
 72   
 import java.util.Collections;
 73   
 import java.util.HashMap;
 74   
 import java.util.Iterator;
 75   
 import java.util.List;
 76   
 import java.util.Map;
 77   
 import java.util.logging.Logger;
 78   
 
 79   
 import org.codehaus.groovy.ast.ClassNode;
 80   
 import org.codehaus.groovy.classgen.ReflectorGenerator;
 81   
 import org.codehaus.groovy.control.CompilationUnit;
 82   
 import org.codehaus.groovy.control.Phases;
 83   
 import org.codehaus.groovy.runtime.ClosureListener;
 84   
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 85   
 import org.codehaus.groovy.runtime.GroovyCategorySupport;
 86   
 import org.codehaus.groovy.runtime.InvokerHelper;
 87   
 import org.codehaus.groovy.runtime.InvokerInvocationException;
 88   
 import org.codehaus.groovy.runtime.MethodClosure;
 89   
 import org.codehaus.groovy.runtime.MethodHelper;
 90   
 import org.codehaus.groovy.runtime.MethodKey;
 91   
 import org.codehaus.groovy.runtime.NewInstanceMetaMethod;
 92   
 import org.codehaus.groovy.runtime.NewStaticMetaMethod;
 93   
 import org.codehaus.groovy.runtime.ReflectionMetaMethod;
 94   
 import org.codehaus.groovy.runtime.Reflector;
 95   
 import org.codehaus.groovy.runtime.TemporaryMethodKey;
 96   
 import org.codehaus.groovy.runtime.TransformMetaMethod;
 97   
 import org.objectweb.asm.ClassVisitor;
 98   
 import org.objectweb.asm.ClassWriter;
 99   
 
 100   
 /**
 101   
  * Allows methods to be dynamically added to existing classes at runtime
 102   
  * 
 103   
  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
 104   
  * @version $Revision: 1.78 $
 105   
  */
 106   
 public class MetaClass {
 107   
 
 108   
     private static final Logger log = Logger.getLogger(MetaClass.class.getName());
 109   
 
 110   
     public static final Object[] EMPTY_ARRAY = {
 111   
     };
 112   
     public static Class[] EMPTY_TYPE_ARRAY = {
 113   
     };
 114   
     protected static final Object[] ARRAY_WITH_NULL = { null };
 115   
 
 116   
     private static boolean useReflection = false;
 117   
 
 118   
     private MetaClassRegistry registry;
 119   
     private Class theClass;
 120   
     private ClassNode classNode;
 121   
     private Map methodIndex = new HashMap();
 122   
     private Map staticMethodIndex = new HashMap();
 123   
     private List newGroovyMethodsList = new ArrayList();
 124   
     //private Map propertyDescriptors = Collections.synchronizedMap(new HashMap());
 125   
     private Map propertyMap = Collections.synchronizedMap(new HashMap());
 126   
     private Map listeners = new HashMap();
 127   
     private Map methodCache = Collections.synchronizedMap(new HashMap());
 128   
     private Map staticMethodCache = Collections.synchronizedMap(new HashMap());
 129   
     private MetaMethod genericGetMethod;
 130   
     private MetaMethod genericSetMethod;
 131   
     private List constructors;
 132   
     private List allMethods = new ArrayList();
 133   
     private List interfaceMethods;
 134   
     private Reflector reflector;
 135   
     private boolean initialised;
 136   
 
 137  0
     public MetaClass(MetaClassRegistry registry, final Class theClass) throws IntrospectionException {
 138  0
         this.registry = registry;
 139  0
         this.theClass = theClass;
 140   
 
 141  0
         constructors = Arrays.asList(theClass.getDeclaredConstructors());
 142  0
         addMethods(theClass);
 143   
 
 144   
         // introspect
 145  0
         BeanInfo info = null;
 146  0
         try {
 147  0
             info =(BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
 148  0
                 public Object run() throws IntrospectionException {
 149  0
                     return Introspector.getBeanInfo(theClass);
 150   
                 }
 151   
             });
 152   
         } catch (PrivilegedActionException pae) {
 153  0
             if (pae.getException() instanceof IntrospectionException) {
 154  0
                 throw (IntrospectionException) pae.getException();
 155   
             } else {
 156  0
                 throw new RuntimeException(pae.getException());
 157   
             }
 158   
         }
 159   
 
 160  0
         PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
 161   
         
 162   
         // build up the metaproperties based on the public fields, property descriptors,
 163   
         // and the getters and setters
 164  0
         setupProperties(descriptors);
 165   
         
 166   
         /* old code
 167   
         for (int i = 0; i < descriptors.length; i++) {
 168   
             PropertyDescriptor descriptor = descriptors[i];
 169   
             propertyDescriptors.put(descriptor.getName(), descriptor);
 170   
         }
 171   
         */
 172   
         
 173  0
         EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
 174  0
         for (int i = 0; i < eventDescriptors.length; i++) {
 175  0
             EventSetDescriptor descriptor = eventDescriptors[i];
 176  0
             Method[] listenerMethods = descriptor.getListenerMethods();
 177  0
             for (int j = 0; j < listenerMethods.length; j++) {
 178  0
                 Method listenerMethod = listenerMethods[j];
 179  0
                 MetaMethod metaMethod = createMetaMethod(descriptor.getAddListenerMethod());
 180  0
                 listeners.put(listenerMethod.getName(), metaMethod);
 181   
             }
 182   
         }
 183   
     }
 184   
 
 185  0
     public static boolean isUseReflection() {
 186  0
         return useReflection;
 187   
     }
 188   
 
 189   
     /**
 190   
      * Allows reflection to be enabled in situations where bytecode generation
 191   
      * of method invocations causes issues.
 192   
      * 
 193   
      * @param useReflection
 194   
      */
 195  0
     public static void setUseReflection(boolean useReflection) {
 196  0
         MetaClass.useReflection = useReflection;
 197   
     }
 198   
 
 199  0
     private void addInheritedMethods(Class theClass) {
 200   
         // lets add all the base class methods
 201  0
         Class c = theClass;
 202  0
         if (c != Object.class) {
 203  0
             while (true) {
 204  0
                 c = c.getSuperclass();
 205  0
                 if (c == Object.class || c == null) {
 206  0
                     break;
 207   
                 }
 208  0
                 addMethods(c);
 209  0
                 addNewStaticMethodsFrom(c);
 210   
 
 211   
             }
 212   
         }
 213   
 
 214   
         // now lets see if there are any methods on one of my interfaces
 215  0
         Class[] interfaces = theClass.getInterfaces();
 216  0
         for (int i = 0; i < interfaces.length; i++) {
 217  0
             addNewStaticMethodsFrom(interfaces[i]);
 218   
         }
 219   
 
 220   
         // lets add Object methods after interfaces, as all interfaces derive from Object. 
 221   
         // this ensures List and Collection methods come before Object etc
 222  0
         if (theClass != Object.class) {
 223  0
             addMethods(Object.class);
 224  0
             addNewStaticMethodsFrom(Object.class);
 225   
         }
 226   
 
 227  0
         if (theClass.isArray() && !theClass.equals(Object[].class)) {
 228  0
             addNewStaticMethodsFrom(Object[].class);
 229   
         }
 230   
     }
 231   
 
 232   
     /**
 233   
      * @return all the normal instance methods avaiable on this class for the
 234   
      *         given name
 235   
      */
 236  0
     public List getMethods(String name) {
 237  0
         List answer = (List) methodIndex.get(name);
 238  0
         List used = GroovyCategorySupport.getCategoryMethods(theClass, name);
 239  0
         if (used != null) {
 240  0
             if (answer != null) {
 241  0
                 answer.addAll(used);
 242   
             } else{
 243  0
                 answer = used;
 244   
             }
 245   
         }
 246  0
         if (answer == null) {
 247  0
             answer = Collections.EMPTY_LIST;
 248   
         }
 249  0
         return answer;
 250   
     }
 251   
 
 252   
     /**
 253   
      * @return all the normal static methods avaiable on this class for the
 254   
      *         given name
 255   
      */
 256  0
     public List getStaticMethods(String name) {
 257  0
         List answer = (List) staticMethodIndex.get(name);
 258  0
         if (answer == null) {
 259  0
             return Collections.EMPTY_LIST;
 260   
         }
 261  0
         return answer;
 262   
     }
 263   
 
 264   
     /**
 265   
      * Allows static method definitions to be added to a meta class as if it
 266   
      * was an instance method
 267   
      * 
 268   
      * @param method
 269   
      */
 270  0
     protected void addNewInstanceMethod(Method method) {
 271  0
         if (initialised) {
 272  0
             throw new RuntimeException("Already initialized, cannot add new method: " + method);
 273   
         }
 274   
         else {
 275  0
             NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(createMetaMethod(method));
 276  0
             addMethod(newMethod);
 277  0
             addNewInstanceMethod(newMethod);
 278   
         }
 279   
     }
 280   
 
 281  0
     protected void addNewInstanceMethod(MetaMethod method) {
 282  0
         newGroovyMethodsList.add(method);
 283   
     }
 284   
 
 285  0
     protected void addNewStaticMethod(Method method) {
 286  0
         if (initialised) {
 287  0
             throw new RuntimeException("Already initialized, cannot add new method: " + method);
 288   
         }
 289   
         else {
 290  0
             NewStaticMetaMethod newMethod = new NewStaticMetaMethod(createMetaMethod(method));
 291  0
             addMethod(newMethod);
 292  0
             addNewStaticMethod(newMethod);
 293   
         }
 294   
     }
 295   
 
 296  0
     protected void addNewStaticMethod(MetaMethod method) {
 297  0
         newGroovyMethodsList.add(method);
 298   
     }
 299   
 
 300  0
     public Object invokeMethod(Object object, String methodName, Object arguments) {
 301  0
         return invokeMethod(object, methodName, asArray(arguments));
 302   
     }
 303   
 
 304   
     /**
 305   
      * Invokes the given method on the object.
 306   
      *  
 307   
      */
 308  0
     public Object invokeMethod(Object object, String methodName, Object[] arguments) {
 309  0
         if (object == null) {
 310  0
             throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
 311   
         }
 312   
 
 313  0
         MetaMethod method = retrieveMethod(object, methodName, arguments);
 314   
 
 315  0
         if (method != null) {
 316  0
             return doMethodInvoke(object, method, arguments);
 317   
         } else {
 318  0
             throw new MissingMethodException(methodName, theClass, arguments);
 319   
         }
 320   
     }
 321   
 
 322  0
     protected MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
 323   
         // lets try use the cache to find the method
 324  0
         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
 325  0
         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
 326  0
         if (method == null) {
 327  0
             method = pickMethod(owner, methodName, arguments);
 328  0
             if (method != null && method.isCacheable()) {
 329  0
                 methodCache.put(methodKey.createCopy(), method);
 330   
             }
 331   
         }
 332  0
         return method;
 333   
     }
 334   
 
 335  0
     public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
 336   
         // lets try use the cache to find the method
 337  0
         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
 338  0
         MetaMethod method = (MetaMethod) methodCache.get(methodKey);
 339  0
         if (method == null) {
 340  0
             method = pickMethod(methodName, arguments); // todo shall call pickStaticMethod also?
 341  0
             if (method != null && method.isCacheable()) {
 342  0
                 methodCache.put(methodKey.createCopy(), method);
 343   
             }
 344   
         }
 345  0
         return method;
 346   
     }
 347   
 
 348  0
     public Constructor retrieveConstructor(Class[] arguments) {
 349  0
         Constructor constructor = (Constructor) chooseMethod("<init>", constructors, arguments, false);
 350  0
         if (constructor != null) {
 351  0
             return constructor;
 352   
         }
 353   
         else {
 354  0
             constructor = (Constructor) chooseMethod("<init>", constructors, arguments, true);
 355  0
             if (constructor != null) {
 356  0
                 return constructor;
 357   
             }
 358   
         }
 359  0
         return null;
 360   
     }
 361   
 
 362  0
     public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
 363  0
         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
 364  0
         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
 365  0
         if (method == null) {
 366  0
             method = pickStaticMethod(methodName, arguments);
 367  0
             if (method != null) {
 368  0
                 staticMethodCache.put(methodKey.createCopy(), method);
 369   
             }
 370   
         }
 371  0
         return method;
 372   
     }
 373   
     /**
 374   
      * Picks which method to invoke for the given object, method name and arguments
 375   
      */
 376  0
     protected MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
 377  0
         MetaMethod method = null;
 378  0
         List methods = getMethods(methodName);
 379  0
         if (!methods.isEmpty()) {
 380  0
             Class[] argClasses = convertToTypeArray(arguments);
 381  0
             method = (MetaMethod) chooseMethod(methodName, methods, argClasses, false);
 382  0
             if (method == null) {
 383  0
                 method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
 384  0
                 if (method == null) {
 385  0
                     int size = (arguments != null) ? arguments.length : 0;
 386  0
                     if (size == 1) {
 387  0
                         Object firstArgument = arguments[0];
 388  0
                         if (firstArgument instanceof List) {
 389   
                             // lets coerce the list arguments into an array of
 390   
                             // arguments
 391   
                             // e.g. calling JFrame.setLocation( [100, 100] )
 392   
 
 393  0
                             List list = (List) firstArgument;
 394  0
                             arguments = list.toArray();
 395  0
                             argClasses = convertToTypeArray(arguments);
 396  0
                             method = (MetaMethod) chooseMethod(methodName, methods, argClasses, true);
 397  0
                             return new TransformMetaMethod(method) {
 398  0
                                 public Object invoke(Object object, Object[] arguments) throws Exception {
 399  0
                                     Object firstArgument = arguments[0];
 400  0
                                     List list = (List) firstArgument;
 401  0
                                     arguments = list.toArray();
 402  0
                                     return super.invoke(object, arguments);
 403   
                                 }
 404   
                             };
 405   
                         }
 406   
                     }
 407   
                 }
 408   
             }
 409   
         }
 410  0
         return method;
 411   
     }
 412   
 
 413   
     /**
 414   
      * pick a method in a strict manner, i.e., without reinterpreting the first List argument.
 415   
      * this method is used only by ClassGenerator for static binding
 416   
      * @param methodName
 417   
      * @param arguments
 418   
      * @return
 419   
      */
 420  0
     protected MetaMethod pickMethod(String methodName, Class[] arguments) {
 421  0
         MetaMethod method = null;
 422  0
         List methods = getMethods(methodName);
 423  0
         if (!methods.isEmpty()) {
 424  0
             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
 425   
 // no coersion at classgen time.
 426   
 //            if (method == null) {
 427   
 //                method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
 428   
 //            }
 429   
         }
 430  0
         return method;
 431   
     }
 432   
 
 433  0
     public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
 434   
         //        System.out.println("Calling static method: " + methodName + " on args: " + InvokerHelper.toString(arguments));
 435   
         //        Class type = arguments == null ? null : arguments.getClass();
 436   
         //        System.out.println("Argument  type: " + type);
 437   
         //        System.out.println("Type of first arg: " + arguments[0] + " type: " + arguments[0].getClass());
 438   
 
 439   
         // lets try use the cache to find the method
 440  0
         MethodKey methodKey = new TemporaryMethodKey(methodName, arguments);
 441  0
         MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
 442  0
         if (method == null) {
 443  0
             method = pickStaticMethod(object, methodName, arguments);
 444  0
             if (method != null) {
 445  0
                 staticMethodCache.put(methodKey.createCopy(), method);
 446   
             }
 447   
         }
 448   
 
 449  0
         if (method != null) {
 450  0
             return doMethodInvoke(object, method, arguments);
 451   
         }
 452   
         /*
 453   
         List methods = getStaticMethods(methodName);
 454   
         
 455   
         if (!methods.isEmpty()) {
 456   
             MetaMethod method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
 457   
             if (method != null) {
 458   
                 return doMethodInvoke(theClass, method, arguments);
 459   
             }
 460   
         }
 461   
         
 462   
         if (theClass != Class.class) {
 463   
             try {
 464   
                 return registry.getMetaClass(Class.class).invokeMethod(object, methodName, arguments);
 465   
             }
 466   
             catch (GroovyRuntimeException e) {
 467   
                 // throw our own exception
 468   
             }
 469   
         }
 470   
         */
 471  0
         throw new MissingMethodException(methodName, theClass, arguments);
 472   
     }
 473   
 
 474  0
     protected MetaMethod pickStaticMethod(Object object, String methodName, Object[] arguments) {
 475  0
         MetaMethod method = null;
 476  0
         List methods = getStaticMethods(methodName);
 477   
 
 478  0
         if (!methods.isEmpty()) {
 479  0
             method = (MetaMethod) chooseMethod(methodName, methods, convertToTypeArray(arguments), false);
 480   
         }
 481   
 
 482  0
         if (method == null && theClass != Class.class) {
 483  0
             MetaClass classMetaClass = registry.getMetaClass(Class.class);
 484  0
             method = classMetaClass.pickMethod(object, methodName, arguments);
 485   
         }
 486  0
         return method;
 487   
     }
 488   
 
 489  0
     protected MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
 490  0
         MetaMethod method = null;
 491  0
         List methods = getStaticMethods(methodName);
 492   
 
 493  0
         if (!methods.isEmpty()) {
 494  0
             method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
 495   
 // disabled to keep consistant with the original version of pickStatciMethod
 496   
 //            if (method == null) {
 497   
 //                method = (MetaMethod) chooseMethod(methodName, methods, arguments, true);
 498   
 //            }
 499   
         }
 500   
 
 501  0
         if (method == null && theClass != Class.class) {
 502  0
             MetaClass classMetaClass = registry.getMetaClass(Class.class);
 503  0
             method = classMetaClass.pickMethod(methodName, arguments);
 504   
         }
 505  0
         return method;
 506   
     }
 507   
 
 508  0
     public Object invokeConstructor(Object[] arguments) {
 509  0
         Class[] argClasses = convertToTypeArray(arguments);
 510  0
         Constructor constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, false);
 511  0
         if (constructor != null) {
 512  0
             return doConstructorInvoke(constructor, arguments);
 513   
         }
 514   
         else {
 515  0
             constructor = (Constructor) chooseMethod("<init>", constructors, argClasses, true);
 516  0
             if (constructor != null) {
 517  0
                 return doConstructorInvoke(constructor, arguments);
 518   
             }
 519   
         }
 520   
 
 521  0
         if (arguments.length == 1) {
 522  0
             Object firstArgument = arguments[0];
 523  0
             if (firstArgument instanceof Map) {
 524  0
                 constructor = (Constructor) chooseMethod("<init>", constructors, EMPTY_TYPE_ARRAY, false);
 525  0
                 if (constructor != null) {
 526  0
                     Object bean = doConstructorInvoke(constructor, EMPTY_ARRAY);
 527  0
                     setProperties(bean, ((Map) firstArgument));
 528  0
                     return bean;
 529   
                 }
 530   
             }
 531   
         }
 532  0
         throw new GroovyRuntimeException(
 533   
                     "Could not find matching constructor for: "
 534   
                         + theClass.getName()
 535   
                         + "("+InvokerHelper.toTypeString(arguments)+")");
 536   
     }
 537   
 
 538   
     /**
 539   
      * Sets a number of bean properties from the given Map where the keys are
 540   
      * the String names of properties and the values are the values of the
 541   
      * properties to set
 542   
      */
 543  0
     public void setProperties(Object bean, Map map) {
 544  0
         for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
 545  0
             Map.Entry entry = (Map.Entry) iter.next();
 546  0
             String key = entry.getKey().toString();
 547   
             
 548   
             // do we have this property?
 549  0
             if(propertyMap.get(key) == null)
 550  0
                 continue;
 551   
             
 552  0
             Object value = entry.getValue();
 553  0
             try {
 554  0
                 setProperty(bean, key, value);
 555   
             }
 556   
             catch (GroovyRuntimeException e) {
 557   
                 // lets ignore missing properties
 558   
                 /** todo should replace this code with a getMetaProperty(key) != null check
 559   
                  i.e. don't try and set a non-existent property
 560   
                  */
 561   
             }
 562   
         }
 563   
     }
 564   
 
 565   
     /**
 566   
      * @return the given property's value on the object
 567   
      */
 568  0
     public Object getProperty(final Object object, final String property) {
 569   
         /* old code; we now use the metaProperty map to handle properties
 570   
         MetaMethod metaMethod = null;
 571   
         PropertyDescriptor descriptor = (PropertyDescriptor) propertyDescriptors.get(property);
 572   
         if (descriptor != null) {
 573   
             Method method = descriptor.getReadMethod();
 574   
             if (method == null) {
 575   
                 throw new GroovyRuntimeException("Cannot read property: " + property);
 576   
             }
 577   
             metaMethod = findMethod(method);
 578   
             if (metaMethod == null) {
 579   
                 // lets try invoke a static getter method
 580   
                 metaMethod = findGetter(object, "get" + capitalize(property));
 581   
             }
 582   
             if (metaMethod != null) {
 583   
                 return doMethodInvoke(object, metaMethod, EMPTY_ARRAY);
 584   
             }
 585   
         }
 586   
         */
 587   
         
 588   
         // look for the property in our map
 589  0
         MetaProperty mp = (MetaProperty) propertyMap.get(property);
 590  0
         if(mp != null) {
 591  0
             try {
 592   
                 //System.out.println("we found a metaproperty for " + theClass.getName() +
 593   
                 //  "." + property);
 594   
                 // delegate the get operation to the metaproperty
 595  0
                 return mp.getProperty(object);
 596   
             }
 597   
             catch(Exception e) {
 598  0
                 throw new GroovyRuntimeException("Cannot read property: " + property);
 599   
             }
 600   
         }
 601   
 
 602  0
         if (genericGetMethod == null) {
 603   
             // Make sure there isn't a generic method in the "use" cases
 604  0
             List possibleGenericMethods = getMethods("get");
 605  0
             if (possibleGenericMethods != null) {
 606  0
                 for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
 607  0
                     MetaMethod mmethod = (MetaMethod) i.next();
 608  0
                     Class[] paramTypes = mmethod.getParameterTypes();
 609  0
                     if (paramTypes.length == 1 && paramTypes[0] == String.class) {
 610  0
                         Object[] arguments = {property};
 611  0
                         Object answer = doMethodInvoke(object, mmethod, arguments);
 612  0
                         return answer;
 613   
                     }
 614   
                 }
 615   
             }
 616   
         } 
 617   
         else {
 618  0
             Object[] arguments = { property };
 619  0
             Object answer = doMethodInvoke(object, genericGetMethod, arguments);
 620   
             // jes bug? a property retrieved via a generic get() can't have a null value?
 621  0
             if (answer != null) {
 622  0
                 return answer;
 623   
             }
 624   
         }
 625   
 
 626   
         // is the property the name of a method - in which case return a
 627   
         // closure
 628  0
         List methods = getMethods(property);
 629  0
         if (!methods.isEmpty()) {
 630  0
             return new MethodClosure(object, property);
 631   
         }
 632   
 
 633   
         // lets try invoke a static getter method
 634   
         // this case is for protected fields. I wish there was a better way...
 635  0
         Exception lastException = null;
 636  0
         try {
 637  0
             MetaMethod method = findGetter(object, "get" + capitalize(property));
 638  0
             if (method != null) {
 639  0
                 return doMethodInvoke(object, method, EMPTY_ARRAY);
 640   
             }
 641   
         }
 642   
         catch (GroovyRuntimeException e) {
 643  0
             lastException = e;
 644   
         }
 645   
 
 646   
         /** todo or are we an extensible groovy class? */
 647  0
         if (genericGetMethod != null) {
 648  0
             return null;
 649   
         }
 650   
         else {
 651   
             /** todo these special cases should be special MetaClasses maybe */
 652  0
             if (object instanceof Class) {
 653   
                 // lets try a static field
 654  0
                 return getStaticProperty((Class) object, property);
 655   
             }
 656  0
             if (object instanceof Collection) {
 657  0
                 return DefaultGroovyMethods.getAt((Collection) object, property);
 658   
             }
 659  0
             if (object instanceof Object[]) {
 660  0
                 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), property);
 661   
             }
 662  0
             if (object instanceof Object) {
 663  0
                 try {
 664   
                     // lets try a public field
 665  0
                     Field field = object.getClass().getDeclaredField(property);
 666  0
                     return field.get(object);
 667   
                 }
 668   
                 catch (Exception e1) {
 669   
                     // fall through
 670   
                 }
 671   
             }
 672   
             
 673  0
             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
 674  0
             if (addListenerMethod != null) {
 675   
                 /* @todo one day we could try return the previously registered Closure listener for easy removal */
 676  0
                 return null;
 677   
             }
 678   
 
 679  0
             if (lastException == null)
 680  0
                 throw new MissingPropertyException(property, theClass);
 681   
             else
 682  0
                 throw new MissingPropertyException(property, theClass, lastException);
 683   
         }
 684   
     }
 685   
     
 686   
     /**
 687   
      * Get all the properties defined for this type
 688   
      * @return a list of MetaProperty objects
 689   
      */
 690  0
     public List getProperties() {
 691   
         // simply return the values of the metaproperty map as a List
 692  0
         return new ArrayList(propertyMap.values());
 693   
     }
 694   
     
 695   
     /**
 696   
      * This will build up the property map (Map of MetaProperty objects, keyed on 
 697   
      * property name).
 698   
      */
 699  0
     protected void setupProperties(PropertyDescriptor[] propertyDescriptors) {
 700  0
         MetaProperty mp;
 701  0
         Method method;
 702  0
         MetaMethod getter = null;
 703  0
         MetaMethod setter = null;
 704  0
         Class klass;
 705   
         
 706   
         // first get the public fields and create MetaFieldProperty objects
 707  0
         klass = theClass;
 708  0
         while(klass != null) {
 709  0
             Field[] fields = klass.getDeclaredFields();
 710  0
             for(int i = 0; i < fields.length; i++) {
 711   
                 // we're only interested in publics
 712  0
                 if((fields[i].getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0)
 713  0
                     continue;
 714   
                 
 715   
                 // see if we already got this
 716  0
                 if(propertyMap.get(fields[i].getName()) != null)
 717  0
                     continue;
 718   
                 
 719   
                 //System.out.println("adding field " + fields[i].getName() + 
 720   
                 //  " for class " + klass.getName());
 721   
                 // stick it in there!
 722  0
                 propertyMap.put(fields[i].getName(), new MetaFieldProperty(fields[i]));
 723   
             }
 724   
             
 725   
             // now get the super class
 726  0
             klass = klass.getSuperclass();
 727   
         }
 728   
         
 729   
         // now iterate over the map of property descriptors and generate 
 730   
         // MetaBeanProperty objects
 731  0
         for(int i=0; i<propertyDescriptors.length; i++) {
 732  0
             PropertyDescriptor pd = propertyDescriptors[i];
 733   
             // skip if the field already exists in the map
 734  0
             if(propertyMap.get(pd.getName()) != null)
 735  0
                 continue;
 736   
             
 737   
             // skip if the property type is unknown (this seems to be the case if the 
 738   
             // property descriptor is based on a setX() method that has two parameters,
 739   
             // which is not a valid property)
 740  0
             if(pd.getPropertyType() == null)
 741  0
                 continue;
 742   
             
 743   
             // get the getter method
 744  0
             method = pd.getReadMethod();
 745  0
             if(method != null)
 746  0
                 getter = findMethod(method);
 747   
             else
 748  0
                 getter = null;
 749   
             
 750   
             // get the setter method
 751  0
             method = pd.getWriteMethod();
 752  0
             if(method != null)
 753  0
                 setter = findMethod(method);
 754   
             else
 755  0
                 setter = null;
 756   
             
 757   
             // now create the MetaProperty object
 758   
             //System.out.println("creating a bean property for class " +
 759   
             //  theClass.getName() + ": " + pd.getName());
 760   
                 
 761  0
             mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
 762   
             
 763   
             // put it in the list
 764  0
             propertyMap.put(pd.getName(), mp);
 765   
         }
 766   
         
 767   
         // now look for any stray getters that may be used to define a property
 768  0
         klass = theClass;
 769  0
         while(klass != null) {
 770  0
             Method[] methods = klass.getDeclaredMethods();
 771  0
             for (int i = 0; i < methods.length; i++) {
 772   
                 // filter out the privates
 773  0
                 if(Modifier.isPublic(methods[i].getModifiers()) == false)
 774  0
                     continue;
 775   
                 
 776  0
                 method = methods[i];
 777   
                 
 778  0
                 String methodName = method.getName();
 779   
                 
 780   
                 // is this a getter?
 781  0
                 if(methodName.startsWith("get") && 
 782   
                     methodName.length() > 3 &&
 783   
                     method.getParameterTypes().length == 0) {
 784   
                     
 785   
                     // get the name of the property
 786  0
                     String propName = methodName.substring(3).toLowerCase();
 787   
                     
 788   
                     // is this property already accounted for?
 789  0
                     mp = (MetaProperty) propertyMap.get(propName);
 790  0
                     if(mp != null) {
 791   
                         // we may have already found the setter for this
 792  0
                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getGetter() == null) {
 793   
                             // update the getter method to this one
 794  0
                             ((MetaBeanProperty) mp).setGetter(findMethod(method));
 795   
                         }
 796   
                     }
 797   
                     else {
 798   
                         // we need to create a new property object
 799   
                         // type of the property is what the get method returns
 800  0
                         MetaBeanProperty mbp = new MetaBeanProperty(propName, 
 801   
                             method.getReturnType(),
 802   
                             findMethod(method), null);
 803   
                             
 804   
                         // add it to the map
 805  0
                         propertyMap.put(propName, mbp);
 806   
                     }
 807   
                 }
 808  0
                 else if(methodName.startsWith("set") && 
 809   
                     methodName.length() > 3 &&
 810   
                     method.getParameterTypes().length == 1) {
 811   
                     
 812   
                     // get the name of the property
 813  0
                     String propName = methodName.substring(3).toLowerCase();
 814   
                     
 815   
                     // did we already find the getter of this?
 816  0
                     mp = (MetaProperty) propertyMap.get(propName);
 817  0
                     if(mp != null) {
 818  0
                         if(mp instanceof MetaBeanProperty && ((MetaBeanProperty) mp).getSetter() == null) {
 819   
                             // update the setter method to this one
 820  0
                             ((MetaBeanProperty) mp).setSetter(findMethod(method));
 821   
                         }
 822   
                     }
 823   
                     else {
 824   
                         // this is a new property to add
 825  0
                         MetaBeanProperty mbp = new MetaBeanProperty(propName, 
 826   
                                                                     method.getParameterTypes()[0],
 827   
                                                                     null,
 828   
                                                                     findMethod(method));
 829   
                             
 830   
                         // add it to the map
 831  0
                         propertyMap.put(propName, mbp);
 832   
                     }
 833   
                 }
 834   
             }
 835   
             
 836   
             // now get the super class
 837  0
             klass = klass.getSuperclass();
 838   
         }
 839   
     }
 840   
     
 841   
     /**
 842   
      * Sets the property value on an object
 843   
      */
 844  0
     public void setProperty(Object object, String property, Object newValue) {
 845   
         /* old code, replaced with new MetaProperty stuff...
 846   
         PropertyDescriptor descriptor = (PropertyDescriptor) propertyDescriptors.get(property);
 847   
 
 848   
         if (descriptor != null) {
 849   
             Method method = descriptor.getWriteMethod();
 850   
             if (method == null) {
 851   
                 throw new ReadOnlyPropertyException(property, theClass);
 852   
             }
 853   
             MetaMethod metaMethod = findMethod(method);
 854   
             Object[] arguments = { newValue };
 855   
             try {
 856   
                 doMethodInvoke(object, metaMethod, arguments);
 857   
             }
 858   
             catch (GroovyRuntimeException e) {
 859   
                 // if the value is a List see if we can construct the value
 860   
                 // from a constructor
 861   
                 if (newValue instanceof List) {
 862   
                     List list = (List) newValue;
 863   
                     int params = list.size();
 864   
                     Constructor[] propConstructors = descriptor.getPropertyType().getConstructors();
 865   
                     for (int i = 0; i < propConstructors.length; i++) {
 866   
                         Constructor constructor = propConstructors[i];
 867   
                         if (constructor.getParameterTypes().length == params) {
 868   
                             Object value = doConstructorInvoke(constructor, list.toArray());
 869   
                             doMethodInvoke(object, metaMethod, new Object[] { value });
 870   
                             return;
 871   
                         }
 872   
                     }
 873   
                     
 874   
                     // if value is an array  
 875   
                     Class parameterType = method.getParameterTypes()[0];
 876   
                     if (parameterType.isArray()) {
 877   
                         Object objArray = asPrimitiveArray(list, parameterType);
 878   
                         doMethodInvoke(object, metaMethod, new Object[]{
 879   
                             objArray
 880   
                         });
 881   
                         return;
 882   
                     }
 883   
                 }
 884   
                 
 885   
                 // if value is an multidimensional array  
 886   
                 if (newValue.getClass().isArray()) {
 887   
                     List list = Arrays.asList((Object[])newValue);
 888   
                     
 889   
                     Class parameterType = method.getParameterTypes()[0];
 890   
                     Class arrayType = parameterType.getComponentType();
 891   
                     Object objArray = Array.newInstance(arrayType, list.size());
 892   
                     
 893   
                     for (int i = 0; i < list.size(); i++) {
 894   
                         List list2 =Arrays.asList((Object[]) list.get(i));
 895   
                         Object objArray2 = asPrimitiveArray(list2, arrayType);
 896   
                         Array.set(objArray, i, objArray2);
 897   
                     }
 898   
 
 899   
                     doMethodInvoke(object, metaMethod, new Object[]{
 900   
                         objArray
 901   
                     });
 902   
                     return;
 903   
                 }
 904   
                 
 905   
                 throw new MissingPropertyException(property, theClass, e);
 906   
             }
 907   
             return;
 908   
         }
 909   
         */
 910   
         
 911  0
         MetaProperty mp = (MetaProperty) propertyMap.get(property);
 912  0
         if(mp != null) {
 913  0
             try {
 914  0
                 mp.setProperty(object, newValue);
 915  0
                 return;
 916   
             }
 917   
             catch (Exception e) {
 918   
                 // if the value is a List see if we can construct the value
 919   
                 // from a constructor
 920  0
                 if (newValue instanceof List) {
 921  0
                     List list = (List) newValue;
 922  0
                     int params = list.size();
 923  0
                     Constructor[] constructors = mp.getType().getConstructors();
 924  0
                     for (int i = 0; i < constructors.length; i++) {
 925  0
                         Constructor constructor = constructors[i];
 926  0
                         if (constructor.getParameterTypes().length == params) {
 927  0
                             Object value = doConstructorInvoke(constructor, list.toArray());
 928  0
                             mp.setProperty(object, value);
 929  0
                             return;
 930   
                         }
 931   
                     }
 932   
                     
 933   
                     // if value is an array  
 934  0
                     Class parameterType = mp.getType();
 935  0
                     if (parameterType.isArray()) {
 936  0
                         Object objArray = asPrimitiveArray(list, parameterType);
 937  0
                         mp.setProperty(object, objArray);
 938  0
                         return;
 939   
                     }
 940   
                 }
 941   
 
 942   
                 // if value is an multidimensional array  
 943   
                 // jes currently this logic only supports metabeansproperties and
 944   
                 // not metafieldproperties. It shouldn't be too hard to support
 945   
                 // the latter...
 946  0
                 if (newValue.getClass().isArray() && mp instanceof MetaBeanProperty) {
 947  0
                     MetaBeanProperty mbp = (MetaBeanProperty) mp;
 948  0
                     List list = Arrays.asList((Object[])newValue);
 949  0
                     MetaMethod setter = mbp.getSetter();
 950   
                     
 951  0
                     Class parameterType = setter.getParameterTypes()[0];
 952  0
                     Class arrayType = parameterType.getComponentType();
 953  0
                     Object objArray = Array.newInstance(arrayType, list.size());
 954   
                     
 955  0
                     for (int i = 0; i < list.size(); i++) {
 956  0
                         List list2 =Arrays.asList((Object[]) list.get(i));
 957  0
                         Object objArray2 = asPrimitiveArray(list2, arrayType);
 958  0
                         Array.set(objArray, i, objArray2);
 959   
                     }
 960   
 
 961  0
                     doMethodInvoke(object, setter, new Object[]{
 962   
                         objArray
 963   
                     });
 964  0
                     return;
 965   
                 }
 966   
                 
 967  0
                 throw new MissingPropertyException(property, theClass, e);
 968   
             }
 969   
         }
 970   
         
 971  0
         try {
 972  0
             MetaMethod addListenerMethod = (MetaMethod) listeners.get(property);
 973  0
             if (addListenerMethod != null && newValue instanceof Closure) {
 974   
                 // lets create a dynamic proxy
 975  0
                 Object proxy =
 976   
                     createListenerProxy(addListenerMethod.getParameterTypes()[0], property, (Closure) newValue);
 977  0
                 doMethodInvoke(object, addListenerMethod, new Object[] { proxy });
 978  0
                 return;
 979   
             }
 980   
 
 981  0
             if (genericSetMethod == null) {
 982   
                 //jes do we still need this logic??
 983   
                 // Make sure there isn't a generic method in the "use" cases
 984  0
                 List possibleGenericMethods = getMethods("set");
 985  0
                 if (possibleGenericMethods != null) {
 986  0
                     for (Iterator i = possibleGenericMethods.iterator(); i.hasNext(); ) {
 987  0
                         MetaMethod mmethod = (MetaMethod) i.next();
 988  0
                         Class[] paramTypes = mmethod.getParameterTypes();
 989  0
                         if (paramTypes.length == 2 && paramTypes[0] == String.class) {
 990  0
                             Object[] arguments = {property, newValue};
 991  0
                             Object answer = doMethodInvoke(object, mmethod, arguments);
 992  0
                             return;
 993   
                         }
 994   
                     }
 995   
                 }
 996   
             } 
 997   
             else {
 998  0
                 Object[] arguments = { property, newValue };
 999  0
                 doMethodInvoke(object, genericSetMethod, arguments);
 1000  0
                 return;
 1001   
             }
 1002   
 
 1003   
             /** todo or are we an extensible class? */
 1004   
 
 1005   
             // lets try invoke the set method
 1006   
             // this is kind of ugly: if it is a protected field, we fall
 1007   
             // all the way down to this klunky code. Need a better
 1008   
             // way to handle this situation...
 1009   
 
 1010  0
             String method = "set" + capitalize(property);
 1011  0
             try {
 1012  0
                 invokeMethod(object, method, new Object[] { newValue });
 1013   
             }
 1014   
             catch (MissingMethodException e1) {
 1015  0
                 try {
 1016  0
                     Field field = object.getClass().getDeclaredField(property);
 1017  0
                     field.set(object, newValue);
 1018   
                 }
 1019   
                 catch (Exception e2) {
 1020  0
                     throw new MissingPropertyException(property, theClass, e2);
 1021   
                 }
 1022   
             }
 1023   
             
 1024   
         }
 1025   
         catch (GroovyRuntimeException e) {
 1026  0
             throw new MissingPropertyException(property, theClass, e);
 1027   
         }
 1028   
         
 1029   
         // if we got here, the damn thing just aint there...
 1030  0
         throw new MissingPropertyException(property, theClass);
 1031   
     }
 1032   
 
 1033   
     /**
 1034   
      * @param list
 1035   
      * @param parameterType
 1036   
      * @return
 1037   
      */
 1038  0
     private Object asPrimitiveArray(List list, Class parameterType) {
 1039  0
         Class arrayType = parameterType.getComponentType();
 1040  0
         Object objArray = Array.newInstance(arrayType, list.size());
 1041  0
         for (int i = 0; i < list.size(); i++) {
 1042  0
             Object obj = list.get(i);
 1043  0
             if (arrayType.isPrimitive()) {
 1044  0
                 if (obj instanceof Integer) {
 1045  0
                     Array.setInt(objArray, i, ((Integer) obj).intValue());
 1046   
                 }
 1047  0
                 else if (obj instanceof Double) {
 1048  0
                     Array.setDouble(objArray, i, ((Double) obj).doubleValue());
 1049   
                 }
 1050  0
                 else if (obj instanceof Boolean) {
 1051  0
                     Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
 1052   
                 }
 1053  0
                 else if (obj instanceof Long) {
 1054  0
                     Array.setLong(objArray, i, ((Long) obj).longValue());
 1055   
                 }
 1056  0
                 else if (obj instanceof Float) {
 1057  0
                     Array.setFloat(objArray, i, ((Float) obj).floatValue());
 1058   
                 }
 1059  0
                 else if (obj instanceof Character) {
 1060  0
                     Array.setChar(objArray, i, ((Character) obj).charValue());
 1061   
                 }
 1062  0
                 else if (obj instanceof Byte) {
 1063  0
                     Array.setByte(objArray, i, ((Byte) obj).byteValue());
 1064   
                 }
 1065  0
                 else if (obj instanceof Short) {
 1066  0
                     Array.setShort(objArray, i, ((Short) obj).shortValue());
 1067   
                 }
 1068   
             }
 1069   
             else {
 1070  0
                 Array.set(objArray, i, obj);
 1071   
             }
 1072   
         }
 1073  0
         return objArray;
 1074   
     }
 1075   
     
 1076  0
     public ClassNode getClassNode() {
 1077  0
         if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
 1078   
             // lets try load it from the classpath
 1079  0
             String className = theClass.getName();
 1080  0
             String groovyFile = className;
 1081  0
             int idx = groovyFile.indexOf('$');
 1082  0
             if (idx > 0) {
 1083  0
                 groovyFile = groovyFile.substring(0, idx);
 1084   
             }
 1085  0
             groovyFile = groovyFile.replace('.', '/') + ".groovy";
 1086   
 
 1087   
             //System.out.println("Attempting to load: " + groovyFile);
 1088  0
             URL url = theClass.getClassLoader().getResource(groovyFile);
 1089  0
             if (url == null) {
 1090  0
                 url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
 1091   
             }
 1092  0
             if (url != null) {
 1093  0
                 try {
 1094   
 
 1095   
                     /**
 1096   
                      * todo there is no CompileUnit in scope so class name
 1097   
                      * checking won't work but that mostly affects the bytecode
 1098   
                      * generation rather than viewing the AST
 1099   
                      */
 1100   
 
 1101  0
                     CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
 1102  0
                         public void call( ClassVisitor writer, ClassNode node ) {
 1103  0
                             if( node.getName().equals(theClass.getName()) ) {
 1104  0
                                 MetaClass.this.classNode = node;
 1105   
                             }
 1106   
                         }
 1107   
                     };
 1108   
                     
 1109   
                     
 1110  0
                     CompilationUnit unit = new CompilationUnit( getClass().getClassLoader() );
 1111  0
                     unit.setClassgenCallback( search );
 1112  0
                     unit.addSource( url );
 1113  0
                     unit.compile( Phases.CLASS_GENERATION );
 1114   
                 }
 1115   
                 catch (Exception e) {
 1116  0
                     throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
 1117   
                 }
 1118   
             }
 1119   
 
 1120   
         }
 1121  0
         return classNode;
 1122   
     }
 1123   
 
 1124  0
     public String toString() {
 1125  0
         return super.toString() + "[" + theClass + "]";
 1126   
     }
 1127   
 
 1128   
     // Implementation methods
 1129   
     //-------------------------------------------------------------------------
 1130   
 
 1131   
     /**
 1132   
      * Converts the given object into an array; if its an array then just cast
 1133   
      * otherwise wrap it in an array
 1134   
      */
 1135  0
     protected Object[] asArray(Object arguments) {
 1136  0
         if (arguments == null) {
 1137  0
             return EMPTY_ARRAY;
 1138   
         }
 1139  0
         if (arguments instanceof Tuple) {
 1140  0
             Tuple tuple = (Tuple) arguments;
 1141  0
             return tuple.toArray();
 1142   
         }
 1143  0
         if (arguments instanceof Object[]) {
 1144  0
             return (Object[]) arguments;
 1145   
         }
 1146   
         else {
 1147  0
             return new Object[] { arguments };
 1148   
         }
 1149   
     }
 1150   
     /**
 1151   
      * @param listenerType
 1152   
      *            the interface of the listener to proxy
 1153   
      * @param listenerMethodName
 1154   
      *            the name of the method in the listener API to call the
 1155   
      *            closure on
 1156   
      * @param closure
 1157   
      *            the closure to invoke on the listenerMethodName method
 1158   
      *            invocation
 1159   
      * @return a dynamic proxy which calls the given closure on the given
 1160   
      *         method name
 1161   
      */
 1162  0
     protected Object createListenerProxy(Class listenerType, final String listenerMethodName, final Closure closure) {
 1163  0
         InvocationHandler handler = new ClosureListener(listenerMethodName, closure);
 1164  0
         return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[] { listenerType }, handler);
 1165   
     }
 1166   
 
 1167   
     /**
 1168   
      * Adds all the methods declared in the given class to the metaclass
 1169   
      * ignoring any matching methods already defined by a derived class
 1170   
      * 
 1171   
      * @param theClass
 1172   
      */
 1173  0
     protected void addMethods(Class theClass) {
 1174  0
         Method[] methodArray = theClass.getDeclaredMethods();
 1175  0
         for (int i = 0; i < methodArray.length; i++) {
 1176  0
             Method reflectionMethod = methodArray[i];
 1177  0
             if ( reflectionMethod.getName().indexOf('+') >= 0 ) {
 1178  0
                 continue;
 1179   
             }
 1180  0
             MetaMethod method = createMetaMethod(reflectionMethod);
 1181  0
             addMethod(method);
 1182   
         }
 1183   
     }
 1184   
 
 1185  0
     protected void addMethod(MetaMethod method) {
 1186  0
         String name = method.getName();
 1187   
 
 1188   
         //System.out.println(theClass.getName() + " == " + name + Arrays.asList(method.getParameterTypes()));
 1189   
 
 1190  0
         if (isGenericGetMethod(method) && genericGetMethod == null) {
 1191  0
             genericGetMethod = method;
 1192   
         }
 1193  0
         else if (isGenericSetMethod(method) && genericSetMethod == null) {
 1194  0
             genericSetMethod = method;
 1195   
         }
 1196  0
         if (method.isStatic()) {
 1197  0
             List list = (List) staticMethodIndex.get(name);
 1198  0
             if (list == null) {
 1199  0
                 list = new ArrayList();
 1200  0
                 staticMethodIndex.put(name, list);
 1201  0
                 list.add(method);
 1202   
             }
 1203   
             else {
 1204  0
                 if (!containsMatchingMethod(list, method)) {
 1205  0
                     list.add(method);
 1206   
                 }
 1207   
             }
 1208   
         }
 1209   
 
 1210  0
         List list = (List) methodIndex.get(name);
 1211  0
         if (list == null) {
 1212  0
             list = new ArrayList();
 1213  0
             methodIndex.put(name, list);
 1214  0
             list.add(method);
 1215   
         }
 1216   
         else {
 1217  0
             if (!containsMatchingMethod(list, method)) {
 1218  0
                 list.add(method);
 1219   
             }
 1220   
         }
 1221   
     }
 1222   
 
 1223   
     /**
 1224   
      * @return true if a method of the same matching prototype was found in the
 1225   
      *         list
 1226   
      */
 1227  0
     protected boolean containsMatchingMethod(List list, MetaMethod method) {
 1228  0
         for (Iterator iter = list.iterator(); iter.hasNext();) {
 1229  0
             MetaMethod aMethod = (MetaMethod) iter.next();
 1230  0
             Class[] params1 = aMethod.getParameterTypes();
 1231  0
             Class[] params2 = method.getParameterTypes();
 1232  0
             if (params1.length == params2.length) {
 1233  0
                 boolean matches = true;
 1234  0
                 for (int i = 0; i < params1.length; i++) {
 1235  0
                     if (params1[i] != params2[i]) {
 1236  0
                         matches = false;
 1237  0
                         break;
 1238   
                     }
 1239   
                 }
 1240  0
                 if (matches) {
 1241  0
                     return true;
 1242   
                 }
 1243   
             }
 1244   
         }
 1245  0
         return false;
 1246   
     }
 1247   
 
 1248   
     /**
 1249   
      * Adds all of the newly defined methods from the given class to this
 1250   
      * metaclass
 1251   
      * 
 1252   
      * @param theClass
 1253   
      */
 1254  0
     protected void addNewStaticMethodsFrom(Class theClass) {
 1255  0
         MetaClass interfaceMetaClass = registry.getMetaClass(theClass);
 1256  0
         Iterator iter = interfaceMetaClass.newGroovyMethodsList.iterator();
 1257  0
         while (iter.hasNext()) {
 1258  0
             MetaMethod method = (MetaMethod) iter.next();
 1259  0
             addMethod(method);
 1260  0
             newGroovyMethodsList.add(method);
 1261   
         }
 1262   
     }
 1263   
 
 1264   
     /**
 1265   
      * @return the value of the static property of the given class
 1266   
      */
 1267  0
     protected Object getStaticProperty(Class aClass, String property) {
 1268   
         //System.out.println("Invoking property: " + property + " on class: "
 1269   
         // + aClass);
 1270   
 
 1271  0
         Exception lastException = null;
 1272  0
         try {
 1273  0
             Field field = aClass.getField(property);
 1274  0
             if (field != null) {
 1275  0
                 if ((field.getModifiers() & Modifier.STATIC) != 0) {
 1276  0
                     return field.get(null);
 1277   
                 }
 1278   
             }
 1279   
         }
 1280   
         catch (Exception e) {
 1281  0
             lastException = e;
 1282   
         }
 1283   
 
 1284   
         // lets try invoke a static getter method
 1285  0
         try {
 1286  0
             MetaMethod method = findStaticGetter(aClass, "get" + capitalize(property));
 1287  0
             if (method != null) {
 1288  0
                 return doMethodInvoke(aClass, method, EMPTY_ARRAY);
 1289   
             }
 1290   
         }
 1291   
         catch (GroovyRuntimeException e) {
 1292  0
             throw new MissingPropertyException(property, aClass, e);
 1293   
         }
 1294   
 
 1295  0
         if (lastException == null) {
 1296  0
             throw new MissingPropertyException(property, aClass);
 1297   
         }
 1298   
         else {
 1299  0
             throw new MissingPropertyException(property, aClass, lastException);
 1300   
         }
 1301   
     }
 1302   
 
 1303   
     /**
 1304   
      * @return the matching method which should be found
 1305   
      */
 1306  0
     protected MetaMethod findMethod(Method aMethod) {
 1307  0
         List methods = getMethods(aMethod.getName());
 1308  0
         for (Iterator iter = methods.iterator(); iter.hasNext();) {
 1309  0
             MetaMethod method = (MetaMethod) iter.next();
 1310  0
             if (method.isMethod(aMethod)) {
 1311  0
                 return method;
 1312   
             }
 1313   
         }
 1314   
         //log.warning("Creating reflection based dispatcher for: " + aMethod);
 1315  0
         return new ReflectionMetaMethod(aMethod);
 1316   
     }
 1317   
 
 1318   
     /**
 1319   
      * @return the getter method for the given object
 1320   
      */
 1321  0
     protected MetaMethod findGetter(Object object, String name) {
 1322  0
         List methods = getMethods(name);
 1323  0
         for (Iterator iter = methods.iterator(); iter.hasNext();) {
 1324  0
             MetaMethod method = (MetaMethod) iter.next();
 1325  0
             if (method.getParameterTypes().length == 0) {
 1326  0
                 return method;
 1327   
             }
 1328   
         }
 1329  0
         return null;
 1330   
     }
 1331   
 
 1332   
     /**
 1333   
      * @return the Method of the given name with no parameters or null
 1334   
      */
 1335  0
     protected MetaMethod findStaticGetter(Class type, String name) {
 1336  0
         List methods = getStaticMethods(name);
 1337  0
         for (Iterator iter = methods.iterator(); iter.hasNext();) {
 1338  0
             MetaMethod method = (MetaMethod) iter.next();
 1339  0
             if (method.getParameterTypes().length == 0) {
 1340  0
                 return method;
 1341   
             }
 1342   
         }
 1343   
 
 1344   
         /** todo dirty hack - don't understand why this code is necessary - all methods should be in the allMethods list! */
 1345  0
         try {
 1346  0
             Method method = type.getMethod(name, EMPTY_TYPE_ARRAY);
 1347  0
             if ((method.getModifiers() & Modifier.STATIC) != 0) {
 1348  0
                 return findMethod(method);
 1349   
             }
 1350   
             else {
 1351  0
                 return null;
 1352   
             }
 1353   
         }
 1354   
         catch (Exception e) {
 1355  0
             return null;
 1356   
         }
 1357   
     }
 1358   
 
 1359  0
     protected Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
 1360   
         //System.out.println("Evaluating method: " + method);
 1361   
         //System.out.println("on object: " + object + " with arguments: " +
 1362   
         // InvokerHelper.toString(argumentArray));
 1363   
         //System.out.println(this.theClass);
 1364   
 
 1365  0
         try {
 1366  0
             if (argumentArray == null) {
 1367  0
                 argumentArray = EMPTY_ARRAY;
 1368   
             }
 1369  0
             else if (method.getParameterTypes().length == 1 && argumentArray.length == 0) {
 1370  0
                 argumentArray = ARRAY_WITH_NULL;
 1371   
             }
 1372  0
             return method.invoke(object, argumentArray);
 1373   
         }
 1374   
         catch (ClassCastException e) {
 1375  0
             if (coerceGStrings(argumentArray)) {
 1376  0
                 try {
 1377  0
                     return doMethodInvoke(object, method, argumentArray);
 1378   
                 }
 1379   
                 catch (Exception e2) {
 1380   
                     // allow fall through
 1381   
                 }
 1382   
             }
 1383  0
             throw new GroovyRuntimeException(
 1384   
                 "failed to invoke method: "
 1385   
                     + method
 1386   
                     + " on: "
 1387   
                     + object
 1388   
                     + " with arguments: "
 1389   
                     + InvokerHelper.toString(argumentArray)
 1390   
                     + " reason: "
 1391   
                     + e,
 1392   
                 e);
 1393   
         }
 1394   
         catch (InvocationTargetException e) {
 1395  0
             Throwable t = e.getTargetException();
 1396  0
             if (t instanceof Error) {
 1397  0
                 Error error = (Error) t;
 1398  0
                 throw error;
 1399   
             }
 1400  0
             if (t instanceof RuntimeException) {
 1401  0
                 RuntimeException runtimeEx = (RuntimeException) t;
 1402  0
                 throw runtimeEx;
 1403   
             }
 1404  0
             throw new InvokerInvocationException(e);
 1405   
         }
 1406   
         catch (IllegalAccessException e) {
 1407  0
             throw new GroovyRuntimeException(
 1408   
                 "could not access method: "
 1409   
                     + method
 1410   
                     + " on: "
 1411   
                     + object
 1412   
                     + " with arguments: "
 1413   
                     + InvokerHelper.toString(argumentArray)
 1414   
                     + " reason: "
 1415   
                     + e,
 1416   
                 e);
 1417   
         }
 1418   
         catch (IllegalArgumentException e) {
 1419  0
             if (coerceGStrings(argumentArray)) {
 1420  0
                 try {
 1421  0
                     return doMethodInvoke(object, method, argumentArray);
 1422   
                 }
 1423   
                 catch (Exception e2) {
 1424   
                     // allow fall through
 1425   
                 }
 1426   
             }
 1427  0
             Object[] args = coerceNumbers(method, argumentArray);
 1428  0
             if (args != null) {
 1429  0
                 try {
 1430  0
                     return doMethodInvoke(object, method, args);
 1431   
                 }
 1432   
                 catch (Exception e3) {
 1433   
                     // allow fall through
 1434   
                 }
 1435   
             }
 1436   
 
 1437  0
             throw new GroovyRuntimeException(
 1438   
                     "failed to invoke method: "
 1439   
                     + method
 1440   
                     + " on: "
 1441   
                     + object
 1442   
                     + " with arguments: "
 1443   
                     + InvokerHelper.toString(argumentArray)
 1444   
                     + " reason: "
 1445   
                     + e,
 1446   
                     e);
 1447   
         }
 1448   
         catch (RuntimeException e) {
 1449  0
             throw e;
 1450   
         }
 1451   
         catch (Exception e) {
 1452  0
             throw new GroovyRuntimeException(
 1453   
                 "failed to invoke method: "
 1454   
                     + method
 1455   
                     + " on: "
 1456   
                     + object
 1457   
                     + " with arguments: "
 1458   
                     + InvokerHelper.toString(argumentArray)
 1459   
                     + " reason: "
 1460   
                     + e,
 1461   
                 e);
 1462   
         }
 1463   
     }
 1464   
 
 1465  0
     private static Object[] coerceNumbers(MetaMethod method, Object[] arguments) {
 1466  0
         Object[] ans = null;
 1467  0
         boolean coerced = false; // to indicate that at least one param is coerced
 1468   
 
 1469  0
         Class[] params = method.getParameterTypes();
 1470   
 
 1471  0
         if (params.length != arguments.length) {
 1472  0
             return null;
 1473   
         }
 1474   
 
 1475  0
         ans = new Object[arguments.length];
 1476   
 
 1477  0
         for (int i = 0, size = arguments.length; i < size; i++) {
 1478  0
             Object argument = arguments[i];
 1479  0
             Class param = params[i];
 1480  0
             if ((Number.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number) { // Number types
 1481  0
                 if (param == Byte.class || param == Byte.TYPE ) {
 1482  0
                     ans[i] = new Byte(((Number)argument).byteValue());
 1483  0
                     coerced = true; continue;
 1484   
                 }
 1485  0
                 if (param == Double.class || param == Double.TYPE) {
 1486  0
                     ans[i] = new Double(((Number)argument).doubleValue());
 1487  0
                     coerced = true; continue;
 1488   
                 }
 1489  0
                 if (param == Float.class || param == Float.TYPE) {
 1490  0
                     ans[i] = new Float(((Number)argument).floatValue());
 1491  0
                     coerced = true; continue;
 1492   
                 }
 1493  0
                 if (param == Integer.class || param == Integer.TYPE) {
 1494  0
                     ans[i] = new Integer(((Number)argument).intValue());
 1495  0
                     coerced = true; continue;
 1496   
                 }
 1497  0
                 if (param == Long.class || param == Long.TYPE) {
 1498  0
                     ans[i] = new Long(((Number)argument).longValue());
 1499  0
                     coerced = true; continue;
 1500   
                 }
 1501  0
                 if (param == Short.class || param == Short.TYPE) {
 1502  0
                     ans[i] = new Short(((Number)argument).shortValue());
 1503  0
                     coerced = true; continue;
 1504   
                 }
 1505  0
                 if (param == BigDecimal.class ) {
 1506  0
                     ans[i] = new BigDecimal(((Number)argument).doubleValue());
 1507  0
                     coerced = true; continue;
 1508   
                 }
 1509  0
                 if (param == BigInteger.class) {
 1510  0
                     ans[i] = new BigInteger(String.valueOf(((Number)argument).longValue()));
 1511  0
                     coerced = true; continue;
 1512   
                 }
 1513   
             }
 1514  0
             else if (param.isArray() && argument.getClass().isArray()) {
 1515  0
                 Class paramElem = param.getComponentType();
 1516  0
                 if (paramElem.isPrimitive()) {
 1517  0
                     if (paramElem == boolean.class && argument.getClass().getName().equals("[Ljava.lang.Boolean;")) {
 1518  0
                         ans[i] = InvokerHelper.convertToBooleanArray(argument);
 1519  0
                         coerced = true;
 1520  0
                         continue;
 1521   
                     }
 1522  0
                     if (paramElem == byte.class && argument.getClass().getName().equals("[Ljava.lang.Byte;")) {
 1523  0
                         ans[i] = InvokerHelper.convertToByteArray(argument);
 1524  0
                         coerced = true;
 1525  0
                         continue;
 1526   
                     }
 1527  0
                     if (paramElem == char.class && argument.getClass().getName().equals("[Ljava.lang.Character;")) {
 1528  0
                         ans[i] = InvokerHelper.convertToCharArray(argument);
 1529  0
                         coerced = true;
 1530  0
                         continue;
 1531   
                     }
 1532  0
                     if (paramElem == short.class && argument.getClass().getName().equals("[Ljava.lang.Short;")) {
 1533  0
                         ans[i] = InvokerHelper.convertToShortArray(argument);
 1534  0
                         coerced = true;
 1535  0
                         continue;
 1536   
                     }
 1537  0
                     if (paramElem == int.class && argument.getClass().getName().equals("[Ljava.lang.Integer;")) {
 1538  0
                         ans[i] = InvokerHelper.convertToIntArray(argument);
 1539  0
                         coerced = true;
 1540  0
                         continue;
 1541   
                     }
 1542  0
                     if (paramElem == long.class
 1543   
                             && argument.getClass().getName().equals("[Ljava.lang.Long;")
 1544   
                             && argument.getClass().getName().equals("[Ljava.lang.Integer;")
 1545   
                                                             ) {
 1546  0
                         ans[i] = InvokerHelper.convertToLongArray(argument);
 1547  0
                         coerced = true;
 1548  0
                         continue;
 1549   
                     }
 1550  0
                     if (paramElem == float.class
 1551   
                             && argument.getClass().getName().equals("[Ljava.lang.Float;")
 1552   
                             && argument.getClass().getName().equals("[Ljava.lang.Integer;")
 1553   
                                                             ) {
 1554  0
                         ans[i] = InvokerHelper.convertToFloatArray(argument);
 1555  0
                         coerced = true;
 1556  0
                         continue;
 1557   
                     }
 1558  0
                     if (paramElem == double.class &&
 1559   
                             argument.getClass().getName().equals("[Ljava.lang.Double;") &&
 1560   
                             argument.getClass().getName().equals("[Ljava.lang.BigDecimal;") &&
 1561   
                             argument.getClass().getName().equals("[Ljava.lang.Float;")) {
 1562  0
                         ans[i] = InvokerHelper.convertToDoubleArray(argument);
 1563  0
                         coerced = true;
 1564  0
                         continue;
 1565   
                     }
 1566   
                 }
 1567   
             }
 1568   
         }
 1569  0
         return coerced ? ans : null;
 1570   
     }
 1571   
 
 1572  0
     protected Object doConstructorInvoke(Constructor constructor, Object[] argumentArray) {
 1573   
         //System.out.println("Evaluating constructor: " + constructor + " with
 1574   
         // arguments: " + InvokerHelper.toString(argumentArray));
 1575   
         //System.out.println(this.theClass);
 1576   
 
 1577  0
         try {
 1578  0
             return constructor.newInstance(argumentArray);
 1579   
         }
 1580   
         catch (InvocationTargetException e) {
 1581  0
             Throwable t = e.getTargetException();
 1582  0
             if (t instanceof Error) {
 1583  0
                 Error error = (Error) t;
 1584  0
                 throw error;
 1585   
             }
 1586  0
             if (t instanceof RuntimeException) {
 1587  0
                 RuntimeException runtimeEx = (RuntimeException) t;
 1588  0
                 throw runtimeEx;
 1589   
             }
 1590  0
             throw new InvokerInvocationException(e);
 1591   
         }
 1592   
         catch (IllegalArgumentException e) {
 1593  0
             if (coerceGStrings(argumentArray)) {
 1594  0
                 try {
 1595  0
                     return constructor.newInstance(argumentArray);
 1596   
                 }
 1597   
                 catch (Exception e2) {
 1598   
                     // allow fall through
 1599   
                 }
 1600   
             }
 1601  0
             throw new GroovyRuntimeException(
 1602   
                 "failed to invoke constructor: "
 1603   
                     + constructor
 1604   
                     + " with arguments: "
 1605   
                     + InvokerHelper.toString(argumentArray)
 1606   
                     + " reason: "
 1607   
                     + e,
 1608   
                 e);
 1609   
         }
 1610   
         catch (IllegalAccessException e) {
 1611  0
             throw new GroovyRuntimeException(
 1612   
                 "could not access constructor: "
 1613   
                     + constructor
 1614   
                     + " with arguments: "
 1615   
                     + InvokerHelper.toString(argumentArray)
 1616   
                     + " reason: "
 1617   
                     + e,
 1618   
                 e);
 1619   
         }
 1620   
         catch (Exception e) {
 1621  0
             throw new GroovyRuntimeException(
 1622   
                 "failed to invoke constructor: "
 1623   
                     + constructor
 1624   
                     + " with arguments: "
 1625   
                     + InvokerHelper.toString(argumentArray)
 1626   
                     + " reason: "
 1627   
                     + e,
 1628   
                 e);
 1629   
         }
 1630   
     }
 1631   
 
 1632   
     /**
 1633   
      * Chooses the correct method to use from a list of methods which match by
 1634   
      * name.
 1635   
      * 
 1636   
      * @param methods
 1637   
      *            the possible methods to choose from
 1638   
      * @param arguments
 1639   
      *            the original argument to the method
 1640   
      * @return
 1641   
      */
 1642  0
     protected Object chooseMethod(String methodName, List methods, Class[] arguments, boolean coerce) {
 1643  0
         int methodCount = methods.size();
 1644  0
         if (methodCount <= 0) {
 1645  0
             return null;
 1646   
         }
 1647  0
         else if (methodCount == 1) {
 1648  0
             Object method = methods.get(0);
 1649  0
             if (isValidMethod(method, arguments, coerce)) {
 1650  0
                 return method;
 1651   
             }
 1652  0
             return null;
 1653   
         }
 1654  0
         Object answer = null;
 1655  0
         if (arguments == null || arguments.length == 0) {
 1656  0
             answer = chooseEmptyMethodParams(methods);
 1657   
         }
 1658  0
         else if (arguments.length == 1 && arguments[0] == null) {
 1659  0
             answer = chooseMostGeneralMethodWith1Param(methods);
 1660   
         }
 1661   
         else {
 1662  0
             List matchingMethods = new ArrayList();
 1663   
 
 1664  0
             for (Iterator iter = methods.iterator(); iter.hasNext();) {
 1665  0
                 Object method = iter.next();
 1666  0
                 Class[] paramTypes;
 1667   
 
 1668   
                 // making this false helps find matches
 1669  0
                 if (isValidMethod(method, arguments, coerce)) {
 1670  0
                     matchingMethods.add(method);
 1671   
                 }
 1672   
             }
 1673  0
             if (matchingMethods.isEmpty()) {
 1674  0
                 return null;
 1675   
             }
 1676  0
             else if (matchingMethods.size() == 1) {
 1677  0
                 return matchingMethods.get(0);
 1678   
             }
 1679  0
             return chooseMostSpecificParams(methodName, matchingMethods, arguments);
 1680   
 
 1681   
         }
 1682  0
         if (answer != null) {
 1683  0
             return answer;
 1684   
         }
 1685  0
         throw new GroovyRuntimeException(
 1686   
             "Could not find which method to invoke from this list: "
 1687   
                 + methods
 1688   
                 + " for arguments: "
 1689   
                 + InvokerHelper.toString(arguments));
 1690   
     }
 1691   
 
 1692  0
     protected boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
 1693  0
         Class[] paramTypes = getParameterTypes(method);
 1694  0
         return isValidMethod(paramTypes, arguments, includeCoerce);
 1695   
     }
 1696   
 
 1697  0
     public static boolean isValidMethod(Class[] paramTypes, Class[] arguments, boolean includeCoerce) {
 1698  0
         if (arguments == null) {
 1699  0
             return true;
 1700   
         }
 1701  0
         int size = arguments.length;
 1702  0
         boolean validMethod = false;
 1703  0
         if (paramTypes.length == size) {
 1704   
             // lets check the parameter types match
 1705  0
             validMethod = true;
 1706  0
             for (int i = 0; i < size; i++) {
 1707  0
                 if (!isCompatibleClass(paramTypes[i], arguments[i], includeCoerce)) {
 1708  0
                     validMethod = false;
 1709   
                 }
 1710   
             }
 1711   
         }
 1712   
         else {
 1713  0
             if (paramTypes.length == 1 && size == 0) {
 1714  0
                 return true;
 1715   
             }
 1716   
         }
 1717  0
         return validMethod;
 1718   
     }
 1719   
 
 1720  0
     protected Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
 1721  0
         Object answer = null;
 1722  0
         int size = arguments.length;
 1723  0
         Class[] mostSpecificTypes = null;
 1724  0
         for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
 1725  0
             Object method = iter.next();
 1726  0
             Class[] paramTypes = getParameterTypes(method);
 1727  0
             if (answer == null) {
 1728  0
                 answer = method;
 1729  0
                 mostSpecificTypes = paramTypes;
 1730   
             }
 1731   
             else {
 1732  0
                 boolean useThisMethod = false;
 1733  0
                 for (int i = 0; i < size; i++) {
 1734  0
                     Class mostSpecificType = mostSpecificTypes[i];
 1735  0
                     Class type = paramTypes[i];
 1736   
 
 1737  0
                     if (!isAssignableFrom(mostSpecificType, type)) {
 1738   
 
 1739  0
                         useThisMethod = true;
 1740  0
                         break;
 1741   
                     }
 1742   
 
 1743   
                 }
 1744  0
                 if (useThisMethod) {
 1745   
 
 1746  0
                     if (size > 1) {
 1747  0
                         checkForInvalidOverloading(name, mostSpecificTypes, paramTypes);
 1748   
                     }
 1749   
 
 1750  0
                     answer = method;
 1751  0
                     mostSpecificTypes = paramTypes;
 1752   
                 }
 1753   
             }
 1754   
         }
 1755  0
         return answer;
 1756   
     }
 1757   
 
 1758   
     /**
 1759   
      * Checks that one of the parameter types is a superset of the other and
 1760   
      * that the two lists of types don't conflict. e.g. foo(String, Object) and
 1761   
      * foo(Object, String) would conflict if called with foo("a", "b").
 1762   
      * 
 1763   
      * Note that this method is only called with 2 possible signatures. i.e.
 1764   
      * possible invalid combinations will already have been filtered out. So if
 1765   
      * there were methods foo(String, Object) and foo(Object, String) then one
 1766   
      * of these would be already filtered out if foo was called as foo(12, "a")
 1767   
      */
 1768  0
     protected void checkForInvalidOverloading(String name, Class[] baseTypes, Class[] derivedTypes) {
 1769  0
         for (int i = 0, size = baseTypes.length; i < size; i++) {
 1770  0
             Class baseType = baseTypes[i];
 1771  0
             Class derivedType = derivedTypes[i];
 1772  0
             if (!isAssignableFrom(derivedType, baseType)) {
 1773  0
                 throw new GroovyRuntimeException(
 1774   
                     "Ambiguous method overloading for method: "
 1775   
                         + name
 1776   
                         + ". Cannot resolve which method to invoke due to overlapping prototypes between: "
 1777   
                         + InvokerHelper.toString(baseTypes)
 1778   
                         + " and: "
 1779   
                         + InvokerHelper.toString(derivedTypes));
 1780   
             }
 1781   
         }
 1782   
     }
 1783   
 
 1784  0
     protected Class[] getParameterTypes(Object methodOrConstructor) {
 1785  0
         if (methodOrConstructor instanceof MetaMethod) {
 1786  0
             MetaMethod method = (MetaMethod) methodOrConstructor;
 1787  0
             return method.getParameterTypes();
 1788   
         }
 1789  0
         if (methodOrConstructor instanceof Method) {
 1790  0
             Method method = (Method) methodOrConstructor;
 1791  0
             return method.getParameterTypes();
 1792   
         }
 1793  0
         if (methodOrConstructor instanceof Constructor) {
 1794  0
             Constructor constructor = (Constructor) methodOrConstructor;
 1795  0
             return constructor.getParameterTypes();
 1796   
         }
 1797  0
         throw new IllegalArgumentException("Must be a Method or Constructor");
 1798   
     }
 1799   
 
 1800   
     /**
 1801   
      * @return the method with 1 parameter which takes the most general type of
 1802   
      *         object (e.g. Object)
 1803   
      */
 1804  0
     protected Object chooseMostGeneralMethodWith1Param(List methods) {
 1805   
         // lets look for methods with 1 argument which matches the type of the
 1806   
         // arguments
 1807  0
         Class closestClass = null;
 1808  0
         Object answer = null;
 1809   
 
 1810  0
         for (Iterator iter = methods.iterator(); iter.hasNext();) {
 1811  0
             Object method = iter.next();
 1812  0
             Class[] paramTypes = getParameterTypes(method);
 1813  0
             int paramLength = paramTypes.length;
 1814  0
             if (paramLength == 1) {
 1815  0
                 Class theType = paramTypes[0];
 1816  0
                 if (closestClass == null || isAssignableFrom(closestClass, theType)) {
 1817  0
                     closestClass = theType;
 1818  0
                     answer = method;
 1819   
                 }
 1820   
             }
 1821   
         }
 1822  0
         return answer;
 1823   
     }
 1824   
 
 1825   
     /**
 1826   
      * @return the method with 1 parameter which takes the most general type of
 1827   
      *         object (e.g. Object)
 1828   
      */
 1829  0
     protected Object chooseEmptyMethodParams(List methods) {
 1830  0
         for (Iterator iter = methods.iterator(); iter.hasNext();) {
 1831  0
             Object method = iter.next();
 1832  0
             Class[] paramTypes = getParameterTypes(method);
 1833  0
             int paramLength = paramTypes.length;
 1834  0
             if (paramLength == 0) {
 1835  0
                 return method;
 1836   
             }
 1837   
         }
 1838  0
         return null;
 1839   
     }
 1840   
 
 1841  0
     protected static boolean isCompatibleInstance(Class type, Object value, boolean includeCoerce) {
 1842  0
         boolean answer = value == null || type.isInstance(value);
 1843  0
         if (!answer) {
 1844  0
             if (type.isPrimitive()) {
 1845  0
                 if (type == int.class) {
 1846  0
                     return value instanceof Integer;
 1847   
                 }
 1848  0
                 else if (type == double.class) {
 1849  0
                     return value instanceof Double || value instanceof Float || value instanceof Integer || value instanceof BigDecimal;
 1850   
                 }
 1851  0
                 else if (type == boolean.class) {
 1852  0
                     return value instanceof Boolean;
 1853   
                 }
 1854  0
                 else if (type == long.class) {
 1855  0
                     return value instanceof Long || value instanceof Integer;
 1856   
                 }
 1857  0
                 else if (type == float.class) {
 1858  0
                     return value instanceof Float || value instanceof Integer;
 1859   
                 }
 1860  0
                 else if (type == char.class) {
 1861  0
                     return value instanceof Character;
 1862   
                 }
 1863  0
                 else if (type == byte.class) {
 1864  0
                     return value instanceof Byte;
 1865   
                 }
 1866  0
                 else if (type == short.class) {
 1867  0
                     return value instanceof Short;
 1868   
                 }
 1869   
             }
 1870  0
             else if(type.isArray() && value.getClass().isArray()) {
 1871  0
                 return isCompatibleClass(type.getComponentType(), value.getClass().getComponentType(), false);
 1872   
             }
 1873  0
             else if (includeCoerce) {
 1874  0
                 if (type == String.class && value instanceof GString) {
 1875  0
                     return true;
 1876   
                 }
 1877  0
                 else if (value instanceof Number) {
 1878   
                     // lets allow numbers to be coerced downwards?
 1879  0
                     return Number.class.isAssignableFrom(type);
 1880   
                 }
 1881   
             }
 1882   
         }
 1883  0
         return answer;
 1884   
     }
 1885  0
     protected static boolean isCompatibleClass(Class type, Class value, boolean includeCoerce) {
 1886  0
         boolean answer = value == null || type.isAssignableFrom(value); // this might have taken care of primitive types, rendering part of the following code unnecessary
 1887  0
         if (!answer) {
 1888  0
             if (type.isPrimitive()) {
 1889  0
                 if (type == int.class) {
 1890  0
                     return value == Integer.class;// || value == BigDecimal.class; //br added BigDecimal
 1891   
                 }
 1892  0
                 else if (type == double.class) {
 1893  0
                     return value == Double.class || value == Float.class || value == Integer.class || value == BigDecimal.class;
 1894   
                 }
 1895  0
                 else if (type == boolean.class) {
 1896  0
                     return value == Boolean.class;
 1897   
                 }
 1898  0
                 else if (type == long.class) {
 1899  0
                     return value == Long.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal
 1900   
                 }
 1901  0
                 else if (type == float.class) {
 1902  0
                     return value == Float.class || value == Integer.class; // || value == BigDecimal.class;//br added BigDecimal
 1903   
                 }
 1904  0
                 else if (type == char.class) {
 1905  0
                     return value == Character.class;
 1906   
                 }
 1907  0
                 else if (type == byte.class) {
 1908  0
                     return value == Byte.class;
 1909   
                 }
 1910  0
                 else if (type == short.class) {
 1911  0
                     return value == Short.class;
 1912   
                 }
 1913   
             }
 1914  0
             else if(type.isArray() && value.isArray()) {
 1915  0
                 return isCompatibleClass(type.getComponentType(), value.getComponentType(), false);
 1916   
             }
 1917  0
             else if (includeCoerce) {
 1918   
 //if (type == String.class && value == GString.class) {
 1919  0
                 if (type == String.class && GString.class.isAssignableFrom(value)) {
 1920  0
                     return true;
 1921   
                 }
 1922  0
                 else if (value == Number.class) {
 1923   
                     // lets allow numbers to be coerced downwards?
 1924  0
                     return Number.class.isAssignableFrom(type);
 1925   
                 }
 1926   
             }
 1927   
         }
 1928  0
         return answer;
 1929   
     }
 1930   
 
 1931  0
     protected boolean isAssignableFrom(Class mostSpecificType, Class type) {
 1932   
         // let's handle primitives
 1933  0
         if (mostSpecificType.isPrimitive() && type.isPrimitive()) {
 1934  0
             if (mostSpecificType == type) {
 1935  0
                 return true;
 1936   
             }
 1937   
             else {  // note: there is not coercion for boolean and char. Range matters, precision doesn't
 1938  0
                 if (type == int.class) {
 1939  0
                     return
 1940   
                             mostSpecificType == int.class
 1941   
                             || mostSpecificType == short.class
 1942   
                             || mostSpecificType == byte.class;
 1943   
                 }
 1944  0
                 else if (type == double.class) {
 1945  0
                     return
 1946   
                             mostSpecificType == double.class
 1947   
                             || mostSpecificType == int.class
 1948   
                             || mostSpecificType == long.class
 1949   
                             || mostSpecificType == short.class
 1950   
                             || mostSpecificType == byte.class
 1951   
                             || mostSpecificType == float.class;
 1952   
                 }
 1953  0
                 else if (type == long.class) {
 1954  0
                     return
 1955   
                             mostSpecificType == long.class
 1956   
                             || mostSpecificType == int.class
 1957   
                             || mostSpecificType == short.class
 1958   
                             || mostSpecificType == byte.class;
 1959   
                 }
 1960  0
                 else if (type == float.class) {
 1961  0
                     return
 1962   
                             mostSpecificType == float.class
 1963   
                             || mostSpecificType == int.class
 1964   
                             || mostSpecificType == long.class
 1965   
                             || mostSpecificType == short.class
 1966   
                             || mostSpecificType == byte.class;
 1967   
                 }
 1968  0
                 else if (type == short.class) {
 1969  0
                     return
 1970   
                             mostSpecificType == short.class
 1971   
                             || mostSpecificType == byte.class;
 1972   
                 }
 1973   
                 else {
 1974  0
                     return false;
 1975   
                 }
 1976   
             }
 1977   
         }
 1978   
 
 1979  0
         boolean answer = type.isAssignableFrom(mostSpecificType);
 1980  0
         if (!answer) {
 1981  0
             answer = autoboxType(type).isAssignableFrom(autoboxType(mostSpecificType));
 1982   
         }
 1983  0
         return answer;
 1984   
     }
 1985   
 
 1986  0
     private Class autoboxType(Class type) {
 1987  0
         if (type.isPrimitive()) {
 1988  0
             if (type == int.class) {
 1989  0
                 return Integer.class;
 1990   
             }
 1991  0
             else if (type == double.class) {
 1992  0
                 return Double.class;
 1993   
             }
 1994  0
             else if (type == long.class) {
 1995  0
                 return Long.class;
 1996   
             }
 1997  0
             else if (type == boolean.class) {
 1998  0
                 return Boolean.class;
 1999   
             }
 2000  0
             else if (type == float.class) {
 2001  0
                 return Float.class;
 2002   
             }
 2003  0
             else if (type == char.class) {
 2004  0
                 return Character.class;
 2005   
             }
 2006  0
             else if (type == byte.class) {
 2007  0
                 return Byte.class;
 2008   
             }
 2009  0
             else if (type == short.class) {
 2010  0
                 return Short.class;
 2011   
             }
 2012   
         }
 2013  0
         return type;
 2014   
     }
 2015   
 
 2016   
     /**
 2017   
      * Coerces any GString instances into Strings
 2018   
      * 
 2019   
      * @return true if some coercion was done. 
 2020   
      */
 2021  0
     protected static boolean coerceGStrings(Object[] arguments) {
 2022  0
         boolean coerced = false;
 2023  0
         for (int i = 0, size = arguments.length; i < size; i++) {
 2024  0
             Object argument = arguments[i];
 2025  0
             if (argument instanceof GString) {
 2026  0
                 arguments[i] = argument.toString();
 2027  0
                 coerced = true;
 2028   
             }
 2029   
         }
 2030  0
         return coerced;
 2031   
     }
 2032   
 
 2033  0
     protected boolean isGenericSetMethod(MetaMethod method) {
 2034  0
         return (method.getName().equals("set"))
 2035   
             && method.getParameterTypes().length == 2;
 2036   
     }
 2037   
 
 2038  0
     protected boolean isGenericGetMethod(MetaMethod method) {
 2039  0
         if (method.getName().equals("get")) {
 2040  0
             Class[] parameterTypes = method.getParameterTypes();
 2041  0
             return parameterTypes.length == 1 && parameterTypes[0] == String.class;
 2042   
         }
 2043  0
         return false;
 2044   
     }
 2045   
 
 2046  0
     private void registerMethods(boolean instanceMethods) {
 2047  0
         Method[] methods = theClass.getMethods();
 2048  0
         for (int i = 0; i < methods.length; i++) {
 2049  0
             Method method = methods[i];
 2050  0
             if (MethodHelper.isStatic(method)) {
 2051  0
                 Class[] paramTypes = method.getParameterTypes();
 2052  0
                 if (paramTypes.length > 0) {
 2053  0
                     Class owner = paramTypes[0];
 2054  0
                     if (instanceMethods) {
 2055  0
                         registry.lookup(owner).addNewInstanceMethod(method);
 2056   
                     } else {
 2057  0
                         registry.lookup(owner).addNewStaticMethod(method);
 2058   
                     }
 2059   
                 }
 2060   
             }
 2061   
         }
 2062   
     }
 2063   
 
 2064  0
     protected void registerStaticMethods() {
 2065  0
         registerMethods(false);
 2066   
     }
 2067   
 
 2068  0
     protected void registerInstanceMethods() {
 2069  0
         registerMethods(true);
 2070   
     }
 2071   
 
 2072  0
     protected String capitalize(String property) {
 2073  0
         return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
 2074   
     }
 2075   
 
 2076   
     /**
 2077   
      * Call this method when any mutation method is called, such as adding a new
 2078   
      * method to this MetaClass so that any caching or bytecode generation can be
 2079   
      * regenerated.
 2080   
      */
 2081  0
     protected synchronized void onMethodChange() {
 2082  0
         reflector = null;
 2083   
     }
 2084   
     
 2085  0
     protected synchronized void checkInitialised() {
 2086  0
         if (!initialised) {
 2087  0
             initialised = true;
 2088  0
             addInheritedMethods(theClass);
 2089   
         }
 2090  0
         if (reflector == null) {
 2091  0
             generateReflector();
 2092   
         }
 2093   
     }
 2094   
 
 2095  0
     protected MetaMethod createMetaMethod(final Method method) {
 2096  0
         if (registry.useAccessible()) {
 2097  0
             AccessController.doPrivileged(new PrivilegedAction() {
 2098  0
                 public Object run() {
 2099  0
                     method.setAccessible(true);
 2100  0
                     return null;
 2101   
                 }
 2102   
             });
 2103   
         }
 2104  0
         if (useReflection) {
 2105   
             //log.warning("Creating reflection based dispatcher for: " + method);
 2106  0
             return new ReflectionMetaMethod(method);
 2107   
         }
 2108  0
         MetaMethod answer = new MetaMethod(method);
 2109  0
         if (isValidReflectorMethod(answer)) {
 2110  0
             allMethods.add(answer);
 2111  0
             answer.setMethodIndex(allMethods.size());
 2112   
         }
 2113   
         else {
 2114   
             //log.warning("Creating reflection based dispatcher for: " + method);
 2115  0
             answer = new ReflectionMetaMethod(method);
 2116   
         }
 2117  0
         return answer;
 2118   
     }
 2119   
 
 2120  0
     protected boolean isValidReflectorMethod(MetaMethod method) {
 2121   
         // We cannot use a reflector if the method is private, protected, or package accessible only.
 2122  0
         if (!method.isPublic()) {
 2123  0
             return false;
 2124   
         }
 2125  0
         Class declaringClass = method.getDeclaringClass();
 2126  0
         if (!Modifier.isPublic(declaringClass.getModifiers())) {
 2127   
             // lets see if this method is implemented on an interface
 2128  0
             List list = getInterfaceMethods();
 2129  0
             for (Iterator iter = list.iterator(); iter.hasNext();) {
 2130  0
                 MetaMethod aMethod = (MetaMethod) iter.next();
 2131  0
                 if (method.isSame(aMethod)) {
 2132  0
                     method.setInterfaceClass(aMethod.getDeclaringClass());
 2133  0
                     return true;
 2134   
                 }
 2135   
             }
 2136   
             /** todo */
 2137   
             //log.warning("Cannot invoke method on protected/private class which isn't visible on an interface so must use reflection instead: " + method);
 2138  0
             return false;
 2139   
         }
 2140  0
         return true;
 2141   
     }
 2142   
 
 2143  0
     protected void generateReflector() {
 2144  0
         reflector = loadReflector(allMethods);
 2145  0
         if (reflector == null) {
 2146  0
             throw new RuntimeException("Should have a reflector!");
 2147   
         }
 2148   
         // lets set the reflector on all the methods
 2149  0
         for (Iterator iter = allMethods.iterator(); iter.hasNext();) {
 2150  0
             MetaMethod metaMethod = (MetaMethod) iter.next();
 2151   
             //System.out.println("Setting reflector for method: " + metaMethod + " with index: " + metaMethod.getMethodIndex());
 2152  0
             metaMethod.setReflector(reflector);
 2153   
         }
 2154   
     }
 2155   
 
 2156  0
     protected Reflector loadReflector(List methods) {
 2157  0
         ReflectorGenerator generator = new ReflectorGenerator(methods);
 2158  0
         String className = theClass.getName();
 2159  0
         String packagePrefix = "gjdk.";
 2160   
         /*
 2161   
         if (className.startsWith("java.")) {
 2162   
             packagePrefix = "gjdk.";
 2163   
         }
 2164   
         */
 2165  0
         String name = packagePrefix + className + "_GroovyReflector";
 2166  0
         if (theClass.isArray()) {
 2167  0
             String componentName = theClass.getComponentType().getName();
 2168   
             /*
 2169   
             if (componentName.startsWith("java.")) {
 2170   
                 packagePrefix = "gjdk.";
 2171   
             }
 2172   
             */
 2173  0
             name = packagePrefix + componentName + "_GroovyReflectorArray";
 2174   
         }
 2175   
         // lets see if its already loaded
 2176  0
         try {
 2177  0
             Class type = loadReflectorClass(name);
 2178  0
             return (Reflector) type.newInstance();
 2179   
         }
 2180   
         catch (AccessControlException ace) {
 2181   
             //Don't ignore this exception type
 2182  0
             throw ace;
 2183   
         } 
 2184   
         catch (Exception e) {
 2185   
             // lets ignore, lets generate it && load it
 2186   
         }
 2187   
 
 2188  0
         ClassWriter cw = new ClassWriter(true);
 2189  0
         generator.generate(cw, name);
 2190   
 
 2191  0
         byte[] bytecode = cw.toByteArray();
 2192   
 
 2193  0
         try {
 2194  0
             Class type = loadReflectorClass(name, bytecode);
 2195  0
             return (Reflector) type.newInstance();
 2196   
         }
 2197   
         catch (Exception e) {
 2198  0
             throw new GroovyRuntimeException("Could not load the reflector for class: " + name + ". Reason: " + e, e);
 2199   
         }
 2200   
     }
 2201   
 
 2202  0
     protected Class loadReflectorClass(final String name, final byte[] bytecode) throws ClassNotFoundException {
 2203  0
         ClassLoader loader = theClass.getClassLoader();
 2204  0
         if (loader instanceof GroovyClassLoader) {
 2205  0
             final GroovyClassLoader gloader = (GroovyClassLoader) loader;
 2206  0
             return (Class) AccessController.doPrivileged(new PrivilegedAction() {
 2207  0
                 public Object run() {
 2208  0
                     return gloader.defineClass(name, bytecode, getClass().getProtectionDomain());
 2209   
                 }
 2210   
             });
 2211   
         }
 2212  0
         return registry.loadClass(name, bytecode);
 2213   
     }
 2214   
 
 2215  0
     protected Class loadReflectorClass(String name) throws ClassNotFoundException {
 2216  0
         ClassLoader loader = theClass.getClassLoader();
 2217  0
         if (loader instanceof GroovyClassLoader) {
 2218  0
             GroovyClassLoader gloader = (GroovyClassLoader) loader;
 2219  0
             return gloader.loadClass(name);
 2220   
         }
 2221  0
         return registry.loadClass(name);
 2222   
     }
 2223   
 
 2224  0
     public List getMethods() {
 2225  0
         return allMethods;
 2226   
     }
 2227   
 
 2228  0
     protected synchronized List getInterfaceMethods() {
 2229  0
         if (interfaceMethods == null) {
 2230  0
             interfaceMethods = new ArrayList();
 2231  0
             Class type = theClass;
 2232  0
             while (type != null) {
 2233  0
                 Class[] interfaces = type.getInterfaces();
 2234  0
                 for (int i = 0; i < interfaces.length; i++) {
 2235  0
                     Class iface = interfaces[i];
 2236  0
                     Method[] methods = iface.getMethods();
 2237  0
                     addInterfaceMethods(interfaceMethods, methods);
 2238   
                 }
 2239  0
                 type = type.getSuperclass();
 2240   
             }
 2241   
         }
 2242  0
         return interfaceMethods;
 2243   
     }
 2244   
 
 2245  0
     private void addInterfaceMethods(List list, Method[] methods) {
 2246  0
         for (int i = 0; i < methods.length; i++) {
 2247  0
             list.add(createMetaMethod(methods[i]));
 2248   
         }
 2249   
     }
 2250   
 
 2251   
     /**
 2252   
      * param instance array to the type array
 2253   
      * @param args
 2254   
      * @return
 2255   
      */
 2256  0
     Class[] convertToTypeArray(Object[] args) {
 2257  0
         if (args == null)
 2258  0
             return null;
 2259  0
         int s = args.length;
 2260  0
         Class[] ans = new Class[s];
 2261  0
         for (int i = 0; i < s; i++) {
 2262  0
             Object o = args[i];
 2263  0
             if (o != null) {
 2264  0
                 ans[i] = o.getClass();
 2265   
             } else {
 2266  0
                 ans[i] = null;
 2267   
             }
 2268   
         }
 2269  0
         return ans;
 2270   
     }
 2271   
 }
 2272