GNU Classpath (0.95) | |
Frames | No Frames |
1: /* Statement.java 2: Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.beans; 40: 41: import java.lang.reflect.Array; 42: import java.lang.reflect.Constructor; 43: import java.lang.reflect.Method; 44: 45: /** 46: * <p>A Statement captures the execution of an object method. It stores 47: * the object, the method to call, and the arguments to the method and 48: * provides the ability to execute the method on the object, using the 49: * provided arguments.</p> 50: * 51: * @author Jerry Quinn (jlquinn@optonline.net) 52: * @author Robert Schuster (robertschuster@fsfe.org) 53: * @since 1.4 54: */ 55: public class Statement 56: { 57: private Object target; 58: private String methodName; 59: private Object[] arguments; 60: 61: /** 62: * One or the other of these will get a value after execute is 63: * called once, but not both. 64: */ 65: private transient Method method; 66: private transient Constructor ctor; 67: 68: /** 69: * <p>Constructs a statement representing the invocation of 70: * object.methodName(arg[0], arg[1], ...);</p> 71: * 72: * <p>If the argument array is null it is replaced with an 73: * array of zero length.</p> 74: * 75: * @param target The object to invoke the method on. 76: * @param methodName The object method to invoke. 77: * @param arguments An array of arguments to pass to the method. 78: */ 79: public Statement(Object target, String methodName, Object[] arguments) 80: { 81: this.target = target; 82: this.methodName = methodName; 83: this.arguments = (arguments != null) ? arguments : new Object[0]; 84: } 85: 86: /** 87: * Execute the statement. 88: * 89: * <p>Finds the specified method in the target object and calls it with 90: * the arguments given in the constructor.</p> 91: * 92: * <p>The most specific method according to the JLS(15.11) is used when 93: * there are multiple methods with the same name.</p> 94: * 95: * <p>Execute performs some special handling for methods and 96: * parameters: 97: * <ul> 98: * <li>Static methods can be executed by providing the class as a 99: * target.</li> 100: * 101: * <li>The method name new is reserved to call the constructor 102: * new() will construct an object and return it. Not useful unless 103: * an expression :-)</li> 104: * 105: * <li>If the target is an array, get and set as defined in 106: * java.util.List are recognized as valid methods and mapped to the 107: * methods of the same name in java.lang.reflect.Array.</li> 108: * 109: * <li>The native datatype wrappers Boolean, Byte, Character, Double, 110: * Float, Integer, Long, and Short will map to methods that have 111: * native datatypes as parameters, in the same way as Method.invoke. 112: * However, these wrappers also select methods that actually take 113: * the wrapper type as an argument.</li> 114: * </ul> 115: * </p> 116: * 117: * <p>The Sun spec doesn't deal with overloading between int and 118: * Integer carefully. If there are two methods, one that takes an 119: * Integer and the other taking an int, the method chosen is not 120: * specified, and can depend on the order in which the methods are 121: * declared in the source file.</p> 122: * 123: * @throws Exception if an exception occurs while locating or 124: * invoking the method. 125: */ 126: public void execute() throws Exception 127: { 128: doExecute(); 129: } 130: 131: private static Class wrappers[] = 132: { 133: Boolean.class, Byte.class, Character.class, Double.class, Float.class, 134: Integer.class, Long.class, Short.class 135: }; 136: 137: private static Class natives[] = 138: { 139: Boolean.TYPE, Byte.TYPE, Character.TYPE, Double.TYPE, Float.TYPE, 140: Integer.TYPE, Long.TYPE, Short.TYPE 141: }; 142: 143: /** Given a wrapper class, return the native class for it. 144: * <p>For example, if <code>c</code> is <code>Integer</code>, 145: * <code>Integer.TYPE</code> is returned.</p> 146: */ 147: private Class unwrap(Class c) 148: { 149: for (int i = 0; i < wrappers.length; i++) 150: if (c == wrappers[i]) 151: return natives[i]; 152: return null; 153: } 154: 155: /** Returns <code>true</code> if all args can be assigned to 156: * <code>params</code>, <code>false</code> otherwise. 157: * 158: * <p>Arrays are guaranteed to be the same length.</p> 159: */ 160: private boolean compatible(Class[] params, Class[] args) 161: { 162: for (int i = 0; i < params.length; i++) 163: { 164: // Argument types are derived from argument values. If one of them was 165: // null then we cannot deduce its type. However null can be assigned to 166: // any type. 167: if (args[i] == null) 168: continue; 169: 170: // Treat Integer like int if appropriate 171: Class nativeType = unwrap(args[i]); 172: if (nativeType != null && params[i].isPrimitive() 173: && params[i].isAssignableFrom(nativeType)) 174: continue; 175: if (params[i].isAssignableFrom(args[i])) 176: continue; 177: 178: return false; 179: } 180: return true; 181: } 182: 183: /** 184: * Returns <code>true</code> if the method arguments in first are 185: * more specific than the method arguments in second, i.e. all 186: * arguments in <code>first</code> can be assigned to those in 187: * <code>second</code>. 188: * 189: * <p>A method is more specific if all parameters can also be fed to 190: * the less specific method, because, e.g. the less specific method 191: * accepts a base class of the equivalent argument for the more 192: * specific one.</p> 193: * 194: * @param first a <code>Class[]</code> value 195: * @param second a <code>Class[]</code> value 196: * @return a <code>boolean</code> value 197: */ 198: private boolean moreSpecific(Class[] first, Class[] second) 199: { 200: for (int j=0; j < first.length; j++) 201: { 202: if (second[j].isAssignableFrom(first[j])) 203: continue; 204: return false; 205: } 206: return true; 207: } 208: 209: final Object doExecute() throws Exception 210: { 211: Class klazz = (target instanceof Class) 212: ? (Class) target : target.getClass(); 213: Object args[] = (arguments == null) ? new Object[0] : arguments; 214: Class argTypes[] = new Class[args.length]; 215: 216: // Retrieve type or use null if the argument is null. The null argument 217: // type is later used in compatible(). 218: for (int i = 0; i < args.length; i++) 219: argTypes[i] = (args[i] != null) ? args[i].getClass() : null; 220: 221: if (target.getClass().isArray()) 222: { 223: // FIXME: invoke may have to be used. For now, cast to Number 224: // and hope for the best. If caller didn't behave, we go boom 225: // and throw the exception. 226: if (methodName.equals("get") && argTypes.length == 1) 227: return Array.get(target, ((Number)args[0]).intValue()); 228: if (methodName.equals("set") && argTypes.length == 2) 229: { 230: Object obj = Array.get(target, ((Number)args[0]).intValue()); 231: Array.set(target, ((Number)args[0]).intValue(), args[1]); 232: return obj; 233: } 234: throw new NoSuchMethodException("No matching method for statement " + toString()); 235: } 236: 237: // If we already cached the method, just use it. 238: if (method != null) 239: return method.invoke(target, args); 240: else if (ctor != null) 241: return ctor.newInstance(args); 242: 243: // Find a matching method to call. JDK seems to go through all 244: // this to find the method to call. 245: 246: // if method name or length don't match, skip 247: // Need to go through each arg 248: // If arg is wrapper - check if method arg is matchable builtin 249: // or same type or super 250: // - check that method arg is same or super 251: 252: if (methodName.equals("new") && target instanceof Class) 253: { 254: Constructor ctors[] = klazz.getConstructors(); 255: for (int i = 0; i < ctors.length; i++) 256: { 257: // Skip methods with wrong number of args. 258: Class ptypes[] = ctors[i].getParameterTypes(); 259: 260: if (ptypes.length != args.length) 261: continue; 262: 263: // Check if method matches 264: if (!compatible(ptypes, argTypes)) 265: continue; 266: 267: // Use method[i] if it is more specific. 268: // FIXME: should this check both directions and throw if 269: // neither is more specific? 270: if (ctor == null) 271: { 272: ctor = ctors[i]; 273: continue; 274: } 275: Class mptypes[] = ctor.getParameterTypes(); 276: if (moreSpecific(ptypes, mptypes)) 277: ctor = ctors[i]; 278: } 279: if (ctor == null) 280: throw new InstantiationException("No matching constructor for statement " + toString()); 281: return ctor.newInstance(args); 282: } 283: 284: Method methods[] = klazz.getMethods(); 285: 286: for (int i = 0; i < methods.length; i++) 287: { 288: // Skip methods with wrong name or number of args. 289: if (!methods[i].getName().equals(methodName)) 290: continue; 291: Class ptypes[] = methods[i].getParameterTypes(); 292: if (ptypes.length != args.length) 293: continue; 294: 295: // Check if method matches 296: if (!compatible(ptypes, argTypes)) 297: continue; 298: 299: // Use method[i] if it is more specific. 300: // FIXME: should this check both directions and throw if 301: // neither is more specific? 302: if (method == null) 303: { 304: method = methods[i]; 305: continue; 306: } 307: Class mptypes[] = method.getParameterTypes(); 308: if (moreSpecific(ptypes, mptypes)) 309: method = methods[i]; 310: } 311: if (method == null) 312: throw new NoSuchMethodException("No matching method for statement " + toString()); 313: 314: // If we were calling Class.forName(String) we intercept and call the 315: // forName-variant that allows a ClassLoader argument. We take the 316: // system classloader (aka application classloader) here to make sure 317: // that application defined classes can be resolved. If we would not 318: // do that the Class.forName implementation would use the class loader 319: // of java.beans.Statement which is <null> and cannot resolve application 320: // defined classes. 321: if (method.equals( 322: Class.class.getMethod("forName", new Class[] { String.class }))) 323: return Class.forName( 324: (String) args[0], true, ClassLoader.getSystemClassLoader()); 325: 326: try { 327: return method.invoke(target, args); 328: } catch(IllegalArgumentException iae){ 329: System.err.println("method: " + method); 330: 331: for(int i=0;i<args.length;i++){ 332: System.err.println("args[" + i + "]: " + args[i]); 333: } 334: throw iae; 335: } 336: } 337: 338: 339: 340: /** Return the statement arguments. */ 341: public Object[] getArguments() { return arguments; } 342: 343: /** Return the statement method name. */ 344: public String getMethodName() { return methodName; } 345: 346: /** Return the statement object. */ 347: public Object getTarget() { return target; } 348: 349: /** 350: * Returns a string representation of this <code>Statement</code>. 351: * 352: * @return A string representation of this <code>Statement</code>. 353: */ 354: public String toString() 355: { 356: StringBuffer result = new StringBuffer(); 357: 358: String targetName; 359: if (target != null) 360: targetName = target.getClass().getSimpleName(); 361: else 362: targetName = "null"; 363: 364: result.append(targetName); 365: result.append("."); 366: result.append(methodName); 367: result.append("("); 368: 369: String sep = ""; 370: for (int i = 0; i < arguments.length; i++) 371: { 372: result.append(sep); 373: result.append( 374: ( arguments[i] == null ) ? "null" : 375: ( arguments[i] instanceof String ) ? "\"" + arguments[i] + "\"" : 376: arguments[i].getClass().getSimpleName()); 377: sep = ", "; 378: } 379: result.append(");"); 380: 381: return result.toString(); 382: } 383: 384: }
GNU Classpath (0.95) |