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 }