View Javadoc

    1   /*
    2    * Copyright 2005 John G. Wilson
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17
    18  package org.codehaus.groovy.runtime;
    19
    20  import groovy.lang.Closure;
    21  import groovy.lang.GString;
    22  import groovy.lang.GroovyRuntimeException;
    23  import groovy.lang.MetaMethod;
    24
    25  import java.lang.reflect.Array;
    26  import java.lang.reflect.Constructor;
    27  import java.lang.reflect.InvocationHandler;
    28  import java.lang.reflect.InvocationTargetException;
    29  import java.lang.reflect.Method;
    30  import java.lang.reflect.Modifier;
    31  import java.lang.reflect.Proxy;
    32  import java.math.BigDecimal;
    33  import java.math.BigInteger;
    34  import java.util.Iterator;
    35  import java.util.List;
    36  import java.util.logging.Level;
    37  import java.util.logging.Logger;
    38
    39  import org.codehaus.groovy.GroovyBugError;
    40  import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
    41  import org.codehaus.groovy.runtime.wrappers.Wrapper;
    42
    43  /***
    44   * @author John Wilson
    45   * @author Jochen Theodorou
    46   */
    47  public class MetaClassHelper {
    48
    49      public static final Object[] EMPTY_ARRAY = {};
    50      public static Class[] EMPTY_TYPE_ARRAY = {};
    51      protected static final Object[] ARRAY_WITH_NULL = { null };
    52      protected static final Logger log = Logger.getLogger(MetaClassHelper.class.getName());
    53      private static final int MAX_ARG_LEN = 12;
    54
    55      public static boolean accessibleToConstructor(final Class at, final Constructor constructor) {
    56          boolean accessible = false;
    57          if (Modifier.isPublic(constructor.getModifiers())) {
    58              accessible = true;
    59          }
    60          else if (Modifier.isPrivate(constructor.getModifiers())) {
    61              accessible = at.getName().equals(constructor.getName());
    62          }
    63          else if ( Modifier.isProtected(constructor.getModifiers()) ) {
    64              if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) {
    65                  accessible = true;
    66              }
    67              else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) {
    68                  accessible = false;
    69              }
    70              else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) {
    71                  accessible = false;
    72              }
    73              else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) {
    74                  accessible = true;
    75              }
    76              else {
    77                  boolean flag = false;
    78                  Class clazz = at;
    79                  while ( !flag && clazz != null ) {
    80                      if (clazz.equals(constructor.getDeclaringClass()) ) {
    81                          flag = true;
    82                          break;
    83                      }
    84                      if (clazz.equals(Object.class) ) {
    85                          break;
    86                      }
    87                      clazz = clazz.getSuperclass();
    88                  }
    89                  accessible = flag;
    90              }
    91          }
    92          else {
    93              if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) {
    94                  accessible = true;
    95              }
    96              else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) {
    97                  accessible = false;
    98              }
    99              else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) {
    100                 accessible = false;
    101             }
    102             else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) {
    103                 accessible = true;
    104             }
    105         }
    106         return accessible;
    107     }
    108
    109     public static Object[] asWrapperArray(Object parameters, Class componentType) {
    110         Object[] ret=null;
    111         if (componentType == boolean.class) {
    112             boolean[] array = (boolean[]) parameters;
    113             ret = new Object[array.length];
    114             for (int i=0; i<array.length; i++) {
    115                 ret[i] = new Boolean(array[i]);
    116             }
    117         } else if (componentType == char.class) {
    118             char[] array = (char[]) parameters;
    119             ret = new Object[array.length];
    120             for (int i=0; i<array.length; i++) {
    121                 ret[i] = new Character(array[i]);
    122             }
    123         } else if (componentType == byte.class) {
    124             byte[] array = (byte[]) parameters;
    125             ret = new Object[array.length];
    126             for (int i=0; i<array.length; i++) {
    127                 ret[i] = new Byte(array[i]);
    128             }
    129         } else if (componentType == int.class) {
    130             int[] array = (int[]) parameters;
    131             ret = new Object[array.length];
    132             for (int i=0; i<array.length; i++) {
    133                 ret[i] = new Integer(array[i]);
    134             }
    135         } else if (componentType == short.class) {
    136             short[] array = (short[]) parameters;
    137             ret = new Object[array.length];
    138             for (int i=0; i<array.length; i++) {
    139                 ret[i] = new Short(array[i]);
    140             }
    141         } else if (componentType == long.class) {
    142             long[] array = (long[]) parameters;
    143             ret = new Object[array.length];
    144             for (int i=0; i<array.length; i++) {
    145                 ret[i] = new Long(array[i]);
    146             }
    147         } else if (componentType == double.class) {
    148             double[] array = (double[]) parameters;
    149             ret = new Object[array.length];
    150             for (int i=0; i<array.length; i++) {
    151                 ret[i] = new Double(array[i]);
    152             }
    153         } else if (componentType == float.class) {
    154             float[] array = (float[]) parameters;
    155             ret = new Object[array.length];
    156             for (int i=0; i<array.length; i++) {
    157                 ret[i] = new Float(array[i]);
    158             }
    159         }
    160
    161         return ret;
    162     }
    163
    164
    165     /***
    166      * @param list
    167      * @param parameterType
    168      */
    169     public static Object asPrimitiveArray(List list, Class parameterType) {
    170         Class arrayType = parameterType.getComponentType();
    171         Object objArray = Array.newInstance(arrayType, list.size());
    172         for (int i = 0; i < list.size(); i++) {
    173             Object obj = list.get(i);
    174             if (arrayType.isPrimitive()) {
    175                 if (obj instanceof Integer) {
    176                     Array.setInt(objArray, i, ((Integer) obj).intValue());
    177                 }
    178                 else if (obj instanceof Double) {
    179                     Array.setDouble(objArray, i, ((Double) obj).doubleValue());
    180                 }
    181                 else if (obj instanceof Boolean) {
    182                     Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
    183                 }
    184                 else if (obj instanceof Long) {
    185                     Array.setLong(objArray, i, ((Long) obj).longValue());
    186                 }
    187                 else if (obj instanceof Float) {
    188                     Array.setFloat(objArray, i, ((Float) obj).floatValue());
    189                 }
    190                 else if (obj instanceof Character) {
    191                     Array.setChar(objArray, i, ((Character) obj).charValue());
    192                 }
    193                 else if (obj instanceof Byte) {
    194                     Array.setByte(objArray, i, ((Byte) obj).byteValue());
    195                 }
    196                 else if (obj instanceof Short) {
    197                     Array.setShort(objArray, i, ((Short) obj).shortValue());
    198                 }
    199             }
    200             else {
    201                 Array.set(objArray, i, obj);
    202             }
    203         }
    204         return objArray;
    205     }
    206
    207     protected static Class autoboxType(Class type) {
    208         if (type.isPrimitive()) {
    209             if (type == int.class) {
    210                 return Integer.class;
    211             }
    212             else if (type == double.class) {
    213                 return Double.class;
    214             }
    215             else if (type == long.class) {
    216                 return Long.class;
    217             }
    218             else if (type == boolean.class) {
    219                 return Boolean.class;
    220             }
    221             else if (type == float.class) {
    222                 return Float.class;
    223             }
    224             else if (type == char.class) {
    225                 return Character.class;
    226             }
    227             else if (type == byte.class) {
    228                 return Byte.class;
    229             }
    230             else if (type == short.class) {
    231                 return Short.class;
    232             }
    233         }
    234         return type;
    235     }
    236
    237     private static Class[] primitives = {
    238         byte.class, Byte.class, short.class, Short.class,
    239         int.class, Integer.class, long.class, Long.class,
    240         BigInteger.class, float.class, Float.class,
    241         double.class, Double.class, BigDecimal.class,
    242         Number.class, Object.class
    243     };
    244     private static int[][] primitiveDistanceTable = {
    245         //              byte    Byte    short   Short   int     Integer     long    Long    BigInteger  float   Float   double  Double  BigDecimal, Number, Object
    246         /* byte*/{      0,      1,      2,      3,      4,      5,          6,      7,      8,          9,      10,     11,     12,     13,         14,     15,         },
    247         /*Byte*/{       1,      0,      2,      3,      4,      5,          6,      7,      8,          9,      10,     11,     12,     13,         14,     15,         },
    248         /*short*/{      14,     15,     0,      1,      2,      3,          4,      5,      6,          7,      8,      9,      10,     11,         12,     13,         },
    249         /*Short*/{      14,     15,     1,      0,      2,      3,          4,      5,      6,          7,      8,      9,      10,     11,         12,     13,         },
    250         /*int*/{        14,     15,     12,     13,     0,      1,          2,      3,      4,          5,      6,      7,      8,      9,          10,     11,         },
    251         /*Integer*/{    14,     15,     12,     13,     1,      0,          2,      3,      4,          5,      6,      7,      8,      9,          10,     11,         },
    252         /*long*/{       14,     15,     12,     13,     10,     11,         0,      1,      2,          3,      4,      5,      6,      7,          8,      9,          },
    253         /*Long*/{       14,     15,     12,     13,     10,     11,         1,      0,      2,          3,      4,      5,      6,      7,          8,      9,          },
    254         /*BigInteger*/{ 14,     15,     12,     13,     10,     11,         8,      9,      0,          1,      2,      3,      4,      5,          6,      7,          },
    255         /*float*/{      14,     15,     12,     13,     10,     11,         8,      9,      7,          0,      1,      2,      3,      4,          5,      6,          },
    256         /*Float*/{      14,     15,     12,     13,     10,     11,         8,      9,      7,          1,      0,      2,      3,      4,          5,      6,          },
    257         /*double*/{     14,     15,     12,     13,     10,     11,         8,      9,      7,          5,      6,      0,      1,      2,          3,      4,          },
    258         /*Double*/{     14,     15,     12,     13,     10,     11,         8,      9,      7,          5,      6,      1,      0,      2,          3,      4,          },
    259         /*BigDecimal*/{ 14,     15,     12,     13,     10,     11,         8,      9,      7,          5,      6,      3,      4,      0,          1,      2,          },
    260         /*Numer*/{      14,     15,     12,     13,     10,     11,         8,      9,      7,          5,      6,      3,      4,      2,          0,      1,          },
    261         /*Object*/{     14,     15,     12,     13,     10,     11,         8,      9,      7,          5,      6,      3,      4,      2,          1,      0,          },
    262     };
    263
    264     private static int getPrimitiveIndex(Class c) {
    265         for (byte i=0; i< primitives.length; i++) {
    266             if (primitives[i] == c) return i;
    267         }
    268         return -1;
    269     }
    270
    271     private static int getPrimitiveDistance(Class from, Class to) {
    272         // we know here that from!=to, so a distance of 0 is never valid
    273         // get primitive type indexes
    274         int fromIndex = getPrimitiveIndex(from);
    275         int toIndex = getPrimitiveIndex(to);
    276         if (fromIndex==-1 || toIndex==-1) return -1;
    277         return primitiveDistanceTable[toIndex][fromIndex];
    278     }
    279
    280     private static int getMaximumInterfaceDistance(Class c, Class interfaceClass) {
    281         if (c==interfaceClass) return 0;
    282         Class[] interfaces = c.getInterfaces();
    283         int max = 0;
    284         for (int i=0; i<interfaces.length; i++) {
    285             int sub = 0;
    286             if (interfaces[i].isAssignableFrom(c)) {
    287                 sub = 1+ getMaximumInterfaceDistance(interfaces[i],interfaceClass);
    288             }
    289             max = Math.max(max,sub);
    290         }
    291         return max;
    292     }
    293
    294     public static long calculateParameterDistance(Class[] arguments, Class[] parameters) {
    295         int objectDistance=0, interfaceDistance=0;
    296         for (int i=0; i<arguments.length; i++) {
    297             if (parameters[i]==arguments[i]) continue;
    298
    299             if (parameters[i].isInterface()) {
    300                 objectDistance+=primitives.length;
    301                 interfaceDistance += getMaximumInterfaceDistance(arguments[i],parameters[i]);
    302                 continue;
    303             }
    304
    305             if (arguments[i]!=null) {
    306                 int pd = getPrimitiveDistance(parameters[i],arguments[i]);
    307                 if (pd!=-1) {
    308                     objectDistance += pd;
    309                     continue;
    310                 }
    311
    312                 // add one to dist to be sure interfaces are prefered
    313                 objectDistance += primitives.length+1;
    314                 Class clazz = autoboxType(arguments[i]);
    315                 while (clazz!=null) {
    316                     if (clazz==parameters[i]) break;
    317                     if (clazz==GString.class && parameters[i]==String.class) {
    318                         objectDistance+=2;
    319                         break;
    320                     }
    321                     clazz = clazz.getSuperclass();
    322                     objectDistance+=3;
    323                 }
    324             } else {
    325                 // choose the distance to Object if a parameter is null
    326                 // this will mean that Object is prefered over a more
    327                 // specific type
    328                 // remove one to dist to be sure Object is prefered
    329                 objectDistance--;
    330                 Class clazz = parameters[i];
    331                 if (clazz.isPrimitive()) {
    332                     objectDistance+=2;
    333                 } else {
    334                     while (clazz!=Object.class) {
    335                         clazz = clazz.getSuperclass();
    336                         objectDistance+=2;
    337                     }
    338                 }
    339             }
    340         }
    341         long ret = objectDistance;
    342         ret <<= 32;
    343         ret |= interfaceDistance;
    344         return ret;
    345     }
    346
    347     public static String capitalize(String property) {
    348         return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
    349     }
    350
    351     /***
    352      * @return the method with 1 parameter which takes the most general type of
    353      *         object (e.g. Object)
    354      */
    355     public static Object chooseEmptyMethodParams(List methods) {
    356         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    357             Object method = iter.next();
    358             Class[] paramTypes = getParameterTypes(method);
    359             int paramLength = paramTypes.length;
    360             if (paramLength == 0) {
    361                 return method;
    362             }
    363         }
    364         return null;
    365     }
    366
    367     /***
    368      * @return the method with 1 parameter which takes the most general type of
    369      *         object (e.g. Object) ignoring primitve types
    370      */
    371     public static Object chooseMostGeneralMethodWith1NullParam(List methods) {
    372         // lets look for methods with 1 argument which matches the type of the
    373         // arguments
    374         Class closestClass = null;
    375         Object answer = null;
    376
    377         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    378             Object method = iter.next();
    379             Class[] paramTypes = getParameterTypes(method);
    380             int paramLength = paramTypes.length;
    381             if (paramLength == 1) {
    382                 Class theType = paramTypes[0];
    383                 if (theType.isPrimitive()) continue;
    384                 if (closestClass == null || isAssignableFrom(theType, closestClass)) {
    385                     closestClass = theType;
    386                     answer = method;
    387                 }
    388             }
    389         }
    390         return answer;
    391     }
    392
    393     /***
    394      * Coerces a GString instance into String if needed
    395      *
    396      * @return the coerced argument
    397      */
    398     protected static Object coerceGString(Object argument, Class clazz) {
    399         if (clazz!=String.class) return argument;
    400         if (!(argument instanceof GString)) return argument;
    401         return argument.toString();
    402     }
    403
    404     protected static Object coerceNumber(Object argument, Class param) {
    405         if ((Number.class.isAssignableFrom(param) || param.isPrimitive()) && argument instanceof Number) { // Number types
    406             Object oldArgument = argument;
    407             boolean wasDouble = false;
    408             boolean wasFloat = false;
    409             if (param == Byte.class || param == Byte.TYPE ) {
    410                 argument = new Byte(((Number)argument).byteValue());
    411             } else if (param == Double.class || param == Double.TYPE) {
    412                 wasDouble = true;
    413                 argument = new Double(((Number)argument).doubleValue());
    414             } else if (param == Float.class || param == Float.TYPE) {
    415                 wasFloat = true;
    416                 argument = new Float(((Number)argument).floatValue());
    417             } else if (param == Integer.class || param == Integer.TYPE) {
    418                 argument = new Integer(((Number)argument).intValue());
    419             } else if (param == Long.class || param == Long.TYPE) {
    420                 argument = new Long(((Number)argument).longValue());
    421             } else if (param == Short.class || param == Short.TYPE) {
    422                 argument = new Short(((Number)argument).shortValue());
    423             } else if (param == BigDecimal.class ) {
    424                 argument = new BigDecimal(String.valueOf((Number)argument));
    425             } else if (param == BigInteger.class) {
    426                 argument = new BigInteger(String.valueOf((Number)argument));
    427             }
    428
    429             if (oldArgument instanceof BigDecimal) {
    430                 BigDecimal oldbd = (BigDecimal) oldArgument;
    431                 boolean throwException = false;
    432                 if (wasDouble) {
    433                     Double d = (Double) argument;
    434                     if (d.isInfinite()) throwException = true;
    435                 } else if (wasFloat) {
    436                     Float f = (Float) argument;
    437                     if (f.isInfinite()) throwException = true;
    438                 } else {
    439                     BigDecimal newbd = new BigDecimal(String.valueOf((Number)argument));
    440                     throwException = !oldArgument.equals(newbd);
    441                 }
    442
    443                 if (throwException) throw new IllegalArgumentException(param+" out of range while converting from BigDecimal");
    444             }
    445
    446         }
    447         return argument;
    448     }
    449
    450      protected static Object coerceArray(Object argument, Class param) {
    451          if (!param.isArray()) return argument;
    452          Class argumentClass = argument.getClass();
    453          if (!argumentClass.isArray()) return argument;
    454
    455          Class paramComponent = param.getComponentType();
    456          if (paramComponent.isPrimitive()) {
    457              if (paramComponent == boolean.class && argumentClass==Boolean[].class) {
    458                  argument = DefaultTypeTransformation.convertToBooleanArray(argument);
    459              } else if (paramComponent == byte.class && argumentClass==Byte[].class) {
    460                  argument = DefaultTypeTransformation.convertToByteArray(argument);
    461              } else if (paramComponent == char.class && argumentClass==Character[].class) {
    462                  argument = DefaultTypeTransformation.convertToCharArray(argument);
    463              } else if (paramComponent == short.class && argumentClass==Short[].class) {
    464                  argument = DefaultTypeTransformation.convertToShortArray(argument);
    465              } else if (paramComponent == int.class && argumentClass==Integer[].class) {
    466                  argument = DefaultTypeTransformation.convertToIntArray(argument);
    467              } else if (paramComponent == long.class &&
    468                         (argumentClass == Long[].class || argumentClass  == Integer[].class))
    469              {
    470                  argument = DefaultTypeTransformation.convertToLongArray(argument);
    471              } else if (paramComponent == float.class &&
    472                         (argumentClass == Float[].class || argumentClass == Integer[].class))
    473              {
    474                  argument = DefaultTypeTransformation.convertToFloatArray(argument);
    475              } else if (paramComponent == double.class &&
    476                         (argumentClass == Double[].class || argumentClass==Float[].class
    477                          || BigDecimal.class.isAssignableFrom(argumentClass)))
    478              {
    479                  argument = DefaultTypeTransformation.convertToDoubleArray(argument);
    480              }
    481          } else if (paramComponent==String.class && argument instanceof GString[]) {
    482              GString[] strings = (GString[]) argument;
    483              String[] ret = new String[strings.length];
    484              for (int i=0; i<strings.length; i++) {
    485                  ret[i] = strings[i].toString();
    486              }
    487              argument = ret;
    488          }
    489          return argument;
    490     }
    491
    492     /***
    493      * @return true if a method of the same matching prototype was found in the
    494      *         list
    495      */
    496     public static boolean containsMatchingMethod(List list, MetaMethod method) {
    497         for (Iterator iter = list.iterator(); iter.hasNext();) {
    498             MetaMethod aMethod = (MetaMethod) iter.next();
    499             Class[] params1 = aMethod.getParameterTypes();
    500             Class[] params2 = method.getParameterTypes();
    501             if (params1.length == params2.length) {
    502                 boolean matches = true;
    503                 for (int i = 0; i < params1.length; i++) {
    504                     if (params1[i] != params2[i]) {
    505                         matches = false;
    506                         break;
    507                     }
    508                 }
    509                 if (matches) {
    510                     return true;
    511                 }
    512             }
    513         }
    514         return false;
    515     }
    516
    517     /***
    518      * param instance array to the type array
    519      * @param args
    520      */
    521     public static Class[] convertToTypeArray(Object[] args) {
    522         if (args == null)
    523             return null;
    524         int s = args.length;
    525         Class[] ans = new Class[s];
    526         for (int i = 0; i < s; i++) {
    527             Object o = args[i];
    528             if (o == null) {
    529                 ans[i] = null;
    530             } else if (o instanceof Wrapper) {
    531                 ans[i] = ((Wrapper) o).getType();
    532             } else {
    533                 ans[i] = o.getClass();
    534             }
    535         }
    536         return ans;
    537     }
    538
    539     /***
    540      * @param listenerType
    541      *            the interface of the listener to proxy
    542      * @param listenerMethodName
    543      *            the name of the method in the listener API to call the
    544      *            closure on
    545      * @param closure
    546      *            the closure to invoke on the listenerMethodName method
    547      *            invocation
    548      * @return a dynamic proxy which calls the given closure on the given
    549      *         method name
    550      */
    551     public static Object createListenerProxy(Class listenerType, final String listenerMethodName, final Closure closure) {
    552         InvocationHandler handler = new ClosureListener(listenerMethodName, closure);
    553         return Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[] { listenerType }, handler);
    554     }
    555
    556     public static Object doConstructorInvoke(Constructor constructor, Object[] argumentArray) {
    557         if (log.isLoggable(Level.FINER)){
    558             logMethodCall(constructor.getDeclaringClass(), constructor.getName(), argumentArray);
    559         }
    560         argumentArray = coerceArgumentsToClasses(argumentArray,constructor.getParameterTypes());
    561         try {
    562             return constructor.newInstance(argumentArray);
    563         } catch (InvocationTargetException e) {
    564             throw new InvokerInvocationException(e);
    565         } catch (IllegalArgumentException e) {
    566             throw createExceptionText("failed to invoke constructor: ", constructor, argumentArray, e, false);
    567         } catch (IllegalAccessException e) {
    568             throw createExceptionText("could not access constructor: ", constructor, argumentArray, e, false);
    569         } catch (Exception e) {
    570             throw createExceptionText("failed to invoke constructor: ", constructor, argumentArray, e, true);
    571         }
    572     }
    573
    574     private static GroovyRuntimeException createExceptionText(String init, Constructor constructor, Object[] argumentArray, Throwable e, boolean setReason) {
    575         throw new GroovyRuntimeException(
    576                 init
    577                 + constructor
    578                 + " with arguments: "
    579                 + InvokerHelper.toString(argumentArray)
    580                 + " reason: "
    581                 + e,
    582                 setReason?e:null);
    583     }
    584
    585     public static Object[] coerceArgumentsToClasses(Object[] argumentArray, Class[] paramTypes) {
    586         // correct argumentArray's length
    587         if (argumentArray == null) {
    588             argumentArray = EMPTY_ARRAY;
    589         } else if (paramTypes.length == 1 && argumentArray.length == 0) {
    590             if (isVargsMethod(paramTypes,argumentArray))
    591                 argumentArray = new Object[]{Array.newInstance(paramTypes[0].getComponentType(),0)};
    592             else
    593                 argumentArray = ARRAY_WITH_NULL;
    594         } else if (isVargsMethod(paramTypes,argumentArray)) {
    595             argumentArray = fitToVargs(argumentArray, paramTypes);
    596         }
    597
    598         //correct Type
    599         for (int i=0; i<argumentArray.length; i++) {
    600             Object argument = argumentArray[i];
    601             if (argument==null) continue;
    602             Class parameterType = paramTypes[i];
    603             if (parameterType.isInstance(argument)) continue;
    604
    605             argument = coerceGString(argument,parameterType);
    606             argument = coerceNumber(argument,parameterType);
    607             argument = coerceArray(argument,parameterType);
    608             argumentArray[i] = argument;
    609         }
    610         return argumentArray;
    611     }
    612
    613     private static Object makeCommonArray(Object[] arguments, int offset, Class fallback) {
    614     	// arguments.leght>0 && !=null
    615     	Class baseClass = null;
    616     	for (int i = offset; i < arguments.length; i++) {
    617 			if (arguments[i]==null) continue;
    618 			Class argClass = arguments[i].getClass();
    619 			if (baseClass==null) {
    620 				baseClass = argClass;
    621 			} else {
    622 				for (;baseClass!=Object.class; baseClass=baseClass.getSuperclass()){
    623 					if (baseClass.isAssignableFrom(argClass)) break;
    624 				}
    625 			}
    626 		}
    627         if (baseClass==null) {
    628             // all arguments were null
    629             baseClass = fallback;
    630         }
    631     	Object result = makeArray(null,baseClass,arguments.length-offset);
    632     	System.arraycopy(arguments,offset,result,0,arguments.length-offset);
    633     	return result;
    634     }
    635
    636     private static Object makeArray(Object obj, Class secondary, int length) {
    637     	Class baseClass = secondary;
    638     	if (obj!=null) {
    639     		baseClass = obj.getClass();
    640     	}
    641     	/*if (GString.class.isAssignableFrom(baseClass)) {
    642     		baseClass = GString.class;
    643     	}*/
    644     	return Array.newInstance(baseClass,length);
    645     }
    646
    647     /***
    648      * this method is called when the number of arguments to a method is greater than 1
    649      * and if the method is a vargs method. This method will then transform the given
    650      * arguments to make the method callable
    651      *
    652      * @param argumentArray the arguments used to call the method
    653      * @param paramTypes the types of the paramters the method takes
    654      */
    655     private static Object[] fitToVargs(Object[] argumentArray, Class[] paramTypes) {
    656     	Class vargsClass = autoboxType(paramTypes[paramTypes.length-1].getComponentType());
    657
    658         if (argumentArray.length == paramTypes.length-1) {
    659             // the vargs argument is missing, so fill it with an empty array
    660             Object[] newArgs = new Object[paramTypes.length];
    661             System.arraycopy(argumentArray,0,newArgs,0,argumentArray.length);
    662             Object vargs = makeArray(null,vargsClass,0);
    663             newArgs[newArgs.length-1] = vargs;
    664             return newArgs;
    665         } else if (argumentArray.length==paramTypes.length) {
    666             // the number of arguments is correct, but if the last argument
    667             // is no array we have to wrap it in a array. if the last argument
    668             // is null, then we don't have to do anything
    669             Object lastArgument = argumentArray[argumentArray.length-1];
    670             if (lastArgument!=null && !lastArgument.getClass().isArray()) {
    671                 // no array so wrap it
    672                 Object vargs = makeArray(lastArgument,vargsClass,1);
    673                 System.arraycopy(argumentArray,argumentArray.length-1,vargs,0,1);
    674                 argumentArray[argumentArray.length-1]=vargs;
    675                 return argumentArray;
    676             } else {
    677                 // we may have to box the arguemnt!
    678                 return argumentArray;
    679             }
    680         } else if (argumentArray.length>paramTypes.length) {
    681             // the number of arguments is too big, wrap all exceeding elements
    682             // in an array, but keep the old elements that are no vargs
    683             Object[] newArgs = new Object[paramTypes.length];
    684             // copy arguments that are not a varg
    685             System.arraycopy(argumentArray,0,newArgs,0,paramTypes.length-1);
    686             // create a new array for the vargs and copy them
    687             int numberOfVargs = argumentArray.length-paramTypes.length;
    688             Object vargs = makeCommonArray(argumentArray,paramTypes.length-1,vargsClass);
    689             newArgs[newArgs.length-1] = vargs;
    690             return newArgs;
    691         } else {
    692             throw new GroovyBugError("trying to call a vargs method without enough arguments");
    693         }
    694     }
    695
    696     private static GroovyRuntimeException createExceptionText(String init, MetaMethod method, Object object, Object[] args, Throwable reason, boolean setReason) {
    697         return new GroovyRuntimeException(
    698                 init
    699                 + method
    700                 + " on: "
    701                 + object
    702                 + " with arguments: "
    703                 + InvokerHelper.toString(args)
    704                 + " reason: "
    705                 + reason,
    706                 setReason?reason:null);
    707     }
    708
    709     public static Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
    710         Class[] paramTypes = method.getParameterTypes();
    711         argumentArray = coerceArgumentsToClasses(argumentArray,paramTypes);
    712         try {
    713             return method.invoke(object, argumentArray);
    714         } catch (IllegalArgumentException e) {
    715             //TODO: test if this is ok with new MOP, should be changed!
    716             // we don't want the exception being unwrapped if it is a IllegalArgumentException
    717             // but in the case it is for example a IllegalThreadStateException, we want the unwrapping
    718             // from the runtime
    719             //Note: the reason we want unwrapping sometimes and sometimes not is that the method
    720             // invokation tries to invoke the method with and then reacts with type transformation
    721             // if the invokation failed here. This is ok for IllegalArgumentException, but it is
    722             // possible that a Reflector will be used to execute the call and then an Exception from inside
    723             // the method is not wrapped in a InvocationTargetException and we will end here.
    724             boolean setReason = e.getClass() != IllegalArgumentException.class;
    725             throw createExceptionText("failed to invoke method: ", method, object, argumentArray, e, setReason);
    726         } catch (RuntimeException e) {
    727             throw e;
    728         } catch (Exception e) {
    729             throw createExceptionText("failed to invoke method: ", method, object, argumentArray, e, true);
    730         }
    731     }
    732
    733     protected static String getClassName(Object object) {
    734         if (object==null) return null;
    735         return (object instanceof Class) ? ((Class)object).getName() : object.getClass().getName();
    736     }
    737
    738     /***
    739      * Returns a callable object for the given method name on the object.
    740      * The object acts like a Closure in that it can be called, like a closure
    741      * and passed around - though really its a method pointer, not a closure per se.
    742      */
    743     public static Closure getMethodPointer(Object object, String methodName) {
    744         return new MethodClosure(object, methodName);
    745     }
    746
    747     public static Class[] getParameterTypes(Object methodOrConstructor) {
    748         if (methodOrConstructor instanceof MetaMethod) {
    749             MetaMethod method = (MetaMethod) methodOrConstructor;
    750             return method.getParameterTypes();
    751         }
    752         if (methodOrConstructor instanceof Method) {
    753             Method method = (Method) methodOrConstructor;
    754             return method.getParameterTypes();
    755         }
    756         if (methodOrConstructor instanceof Constructor) {
    757             Constructor constructor = (Constructor) methodOrConstructor;
    758             return constructor.getParameterTypes();
    759         }
    760         throw new IllegalArgumentException("Must be a Method or Constructor");
    761     }
    762
    763     public static boolean isAssignableFrom(Class classToTransformTo, Class classToTransformFrom) {
    764         if (classToTransformFrom==null) return true;
    765         classToTransformTo = autoboxType(classToTransformTo);
    766         classToTransformFrom = autoboxType(classToTransformFrom);
    767
    768         if (classToTransformTo == classToTransformFrom) {
    769         	return true;
    770         }
    771         // note: there is not coercion for boolean and char. Range matters, precision doesn't
    772         else if (classToTransformTo == Integer.class) {
    773         	if (	classToTransformFrom == Integer.class
    774         			|| classToTransformFrom == Short.class
    775         			|| classToTransformFrom == Byte.class
    776                     || classToTransformFrom == BigInteger.class)
    777         	return true;
    778         }
    779         else if (classToTransformTo == Double.class) {
    780         	if (	classToTransformFrom == Double.class
    781         			|| classToTransformFrom == Integer.class
    782         			|| classToTransformFrom == Long.class
    783         			|| classToTransformFrom == Short.class
    784         			|| classToTransformFrom == Byte.class
    785         			|| classToTransformFrom == Float.class
    786                     || classToTransformFrom == BigDecimal.class
    787                     || classToTransformFrom == BigInteger.class)
    788         	return true;
    789         }
    790         else if (classToTransformTo == BigDecimal.class) {
    791             if (    classToTransformFrom == Double.class
    792                     || classToTransformFrom == Integer.class
    793                     || classToTransformFrom == Long.class
    794                     || classToTransformFrom == Short.class
    795                     || classToTransformFrom == Byte.class
    796                     || classToTransformFrom == Float.class
    797                     || classToTransformFrom == BigDecimal.class
    798                     || classToTransformFrom == BigInteger.class)
    799             return true;
    800         }
    801         else if (classToTransformTo == BigInteger.class) {
    802             if (    classToTransformFrom == Integer.class
    803                     || classToTransformFrom == Long.class
    804                     || classToTransformFrom == Short.class
    805                     || classToTransformFrom == Byte.class
    806                     || classToTransformFrom == BigInteger.class)
    807             return true;
    808         }
    809         else if (classToTransformTo == Long.class) {
    810         	if (	classToTransformFrom == Long.class
    811         			|| classToTransformFrom == Integer.class
    812         			|| classToTransformFrom == Short.class
    813         			|| classToTransformFrom == Byte.class)
    814         	return true;
    815         }
    816         else if (classToTransformTo == Float.class) {
    817         	if (	classToTransformFrom == Float.class
    818         			|| classToTransformFrom == Integer.class
    819         			|| classToTransformFrom == Long.class
    820         			|| classToTransformFrom == Short.class
    821         			|| classToTransformFrom == Byte.class)
    822         	return true;
    823         }
    824         else if (classToTransformTo == Short.class) {
    825         	if (	classToTransformFrom == Short.class
    826         			|| classToTransformFrom == Byte.class)
    827         	return true;
    828         }
    829         else if (classToTransformTo==String.class) {
    830             if (	classToTransformFrom == String.class ||
    831             		GString.class.isAssignableFrom(classToTransformFrom)) {
    832                 return true;
    833             }
    834         }
    835
    836         return classToTransformTo.isAssignableFrom(classToTransformFrom);
    837     }
    838
    839     public static boolean isGenericSetMethod(MetaMethod method) {
    840         return (method.getName().equals("set"))
    841         && method.getParameterTypes().length == 2;
    842     }
    843
    844     protected static boolean isSuperclass(Class claszz, Class superclass) {
    845         while (claszz!=null) {
    846             if (claszz==superclass) return true;
    847             claszz = claszz.getSuperclass();
    848         }
    849         return false;
    850     }
    851
    852     public static boolean isValidMethod(Class[] paramTypes, Class[] arguments, boolean includeCoerce) {
    853         if (arguments == null) {
    854             return true;
    855         }
    856         int size = arguments.length;
    857
    858         if (   (size>=paramTypes.length || size==paramTypes.length-1)
    859                 && paramTypes.length>0
    860                 && paramTypes[paramTypes.length-1].isArray())
    861         {
    862             // first check normal number of parameters
    863             for (int i = 0; i < paramTypes.length-1; i++) {
    864                 if (isAssignableFrom(paramTypes[i], arguments[i])) continue;
    865                 return false;
    866             }
    867             // check varged
    868             Class clazz = paramTypes[paramTypes.length-1].getComponentType();
    869             for (int i=paramTypes.length; i<size; i++) {
    870                 if (isAssignableFrom(clazz, arguments[i])) continue;
    871                 return false;
    872             }
    873             return true;
    874         } else if (paramTypes.length == size) {
    875             // lets check the parameter types match
    876             for (int i = 0; i < size; i++) {
    877                 if (isAssignableFrom(paramTypes[i], arguments[i])) continue;
    878                 return false;
    879             }
    880             return true;
    881         } else if (paramTypes.length == 1 && size == 0) {
    882             return true;
    883         }
    884         return false;
    885
    886     }
    887
    888     public static boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
    889         Class[] paramTypes = getParameterTypes(method);
    890         return isValidMethod(paramTypes, arguments, includeCoerce);
    891     }
    892
    893     public static boolean isVargsMethod(Class[] paramTypes, Object[] arguments) {
    894         if (paramTypes.length==0) return false;
    895         if (!paramTypes[paramTypes.length-1].isArray()) return false;
    896         // -1 because the varg part is optional
    897         if (paramTypes.length-1==arguments.length) return true;
    898         if (paramTypes.length-1>arguments.length) return false;
    899         if (arguments.length>paramTypes.length) return true;
    900
    901         // only case left is arguments.length==paramTypes.length
    902         Object last = arguments[arguments.length-1];
    903         if (last==null) return true;
    904         Class clazz = last.getClass();
    905         if (clazz.equals(paramTypes[paramTypes.length-1])) return false;
    906
    907         return true;
    908     }
    909
    910     public static void logMethodCall(Object object, String methodName, Object[] arguments) {
    911         String className = getClassName(object);
    912         String logname = "methodCalls." + className + "." + methodName;
    913         Logger objLog = Logger.getLogger(logname);
    914         if (! objLog.isLoggable(Level.FINER)) return;
    915         StringBuffer msg = new StringBuffer(methodName);
    916         msg.append("(");
    917         if (arguments != null){
    918             for (int i = 0; i < arguments.length;) {
    919                 msg.append(normalizedValue(arguments[i]));
    920                 if (++i < arguments.length) { msg.append(","); }
    921             }
    922         }
    923         msg.append(")");
    924         objLog.logp(Level.FINER, className, msg.toString(), "called from MetaClass.invokeMethod");
    925     }
    926
    927     protected static String normalizedValue(Object argument) {
    928         String value;
    929         try {
    930             value = argument.toString();
    931             if (value.length() > MAX_ARG_LEN){
    932                 value = value.substring(0,MAX_ARG_LEN-2) + "..";
    933             }
    934             if (argument instanceof String){
    935                 value = "\'"+value+"\'";
    936             }
    937         } catch (Exception e) {
    938             value = shortName(argument);
    939         }
    940         return value;
    941     }
    942
    943     public static boolean parametersAreCompatible(Class[] arguments, Class[] parameters) {
    944         if (arguments.length!=parameters.length) return false;
    945         for (int i=0; i<arguments.length; i++) {
    946             if (!isAssignableFrom(parameters[i],arguments[i])) return false;
    947         }
    948         return true;
    949     }
    950
    951     protected static String shortName(Object object) {
    952         if (object == null || object.getClass()==null) return "unknownClass";
    953         String name = getClassName(object);
    954         if (name == null) return "unknownClassName"; // *very* defensive...
    955         int lastDotPos = name.lastIndexOf('.');
    956         if (lastDotPos < 0 || lastDotPos >= name.length()-1) return name;
    957         return name.substring(lastDotPos+1);
    958     }
    959
    960     public static Class[] wrap(Class[] classes) {
    961         Class[] wrappedArguments = new Class[classes.length];
    962         for (int i = 0; i < wrappedArguments.length; i++) {
    963             Class c = classes[i];
    964             if (c==null) continue;
    965             if (c.isPrimitive()) {
    966                 if (c==Integer.TYPE) {
    967                     c=Integer.class;
    968                 } else if (c==Byte.TYPE) {
    969                     c=Byte.class;
    970                 } else if (c==Long.TYPE) {
    971                     c=Long.class;
    972                 } else if (c==Double.TYPE) {
    973                     c=Double.class;
    974                 } else if (c==Float.TYPE) {
    975                     c=Float.class;
    976                 }
    977             } else if (isSuperclass(c,GString.class)) {
    978                 c = String.class;
    979             }
    980             wrappedArguments[i]=c;
    981         }
    982         return wrappedArguments;
    983     }
    984 }