001 /* 002 * Copyright 2005 John G. Wilson 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017 018 package org.codehaus.groovy.runtime; 019 020 import groovy.lang.Closure; 021 import groovy.lang.GString; 022 import groovy.lang.GroovyRuntimeException; 023 import groovy.lang.MetaMethod; 024 025 import java.lang.reflect.Array; 026 import java.lang.reflect.Constructor; 027 import java.lang.reflect.InvocationHandler; 028 import java.lang.reflect.InvocationTargetException; 029 import java.lang.reflect.Method; 030 import java.lang.reflect.Modifier; 031 import java.lang.reflect.Proxy; 032 import java.math.BigDecimal; 033 import java.math.BigInteger; 034 import java.util.Iterator; 035 import java.util.List; 036 import java.util.logging.Level; 037 import java.util.logging.Logger; 038 039 import org.codehaus.groovy.GroovyBugError; 040 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; 041 import org.codehaus.groovy.runtime.wrappers.Wrapper; 042 043 /** 044 * @author John Wilson 045 * @author Jochen Theodorou 046 */ 047 public class MetaClassHelper { 048 049 public static final Object[] EMPTY_ARRAY = {}; 050 public static Class[] EMPTY_TYPE_ARRAY = {}; 051 protected static final Object[] ARRAY_WITH_NULL = { null }; 052 protected static final Logger log = Logger.getLogger(MetaClassHelper.class.getName()); 053 private static final int MAX_ARG_LEN = 12; 054 055 public static boolean accessibleToConstructor(final Class at, final Constructor constructor) { 056 boolean accessible = false; 057 if (Modifier.isPublic(constructor.getModifiers())) { 058 accessible = true; 059 } 060 else if (Modifier.isPrivate(constructor.getModifiers())) { 061 accessible = at.getName().equals(constructor.getName()); 062 } 063 else if ( Modifier.isProtected(constructor.getModifiers()) ) { 064 if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) { 065 accessible = true; 066 } 067 else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) { 068 accessible = false; 069 } 070 else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) { 071 accessible = false; 072 } 073 else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) { 074 accessible = true; 075 } 076 else { 077 boolean flag = false; 078 Class clazz = at; 079 while ( !flag && clazz != null ) { 080 if (clazz.equals(constructor.getDeclaringClass()) ) { 081 flag = true; 082 break; 083 } 084 if (clazz.equals(Object.class) ) { 085 break; 086 } 087 clazz = clazz.getSuperclass(); 088 } 089 accessible = flag; 090 } 091 } 092 else { 093 if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) { 094 accessible = true; 095 } 096 else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) { 097 accessible = false; 098 } 099 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 }