1 /* 2 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package java.beans; 26 27 import java.lang.reflect.AccessibleObject; 28 import java.lang.reflect.Array; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.security.AccessControlContext; 33 import java.security.AccessController; 34 import java.security.PrivilegedActionException; 35 import java.security.PrivilegedExceptionAction; 36 37 import com.sun.beans.finder.ClassFinder; 38 import com.sun.beans.finder.ConstructorFinder; 39 import com.sun.beans.finder.MethodFinder; 40 import sun.reflect.misc.MethodUtil; 41 42 /** 43 * A {@code Statement} object represents a primitive statement 44 * in which a single method is applied to a target and 45 * a set of arguments - as in {@code "a.setFoo(b)"}. 46 * Note that where this example uses names 47 * to denote the target and its argument, a statement 48 * object does not require a name space and is constructed with 49 * the values themselves. 50 * The statement object associates the named method 51 * with its environment as a simple set of values: 52 * the target and an array of argument values. 53 * 54 * @since 1.4 55 * 56 * @author Philip Milne 57 */ 58 public class Statement { 59 60 private static Object[] emptyArray = new Object[]{}; 61 62 static ExceptionListener defaultExceptionListener = new ExceptionListener() { 63 public void exceptionThrown(Exception e) { 64 System.err.println(e); 65 // e.printStackTrace(); 66 System.err.println("Continuing ..."); 67 } 68 }; 69 70 private final AccessControlContext acc = AccessController.getContext(); 71 private final Object target; 72 private final String methodName; 73 private final Object[] arguments; 74 ClassLoader loader; 75 76 /** 77 * Creates a new {@link Statement} object 78 * for the specified target object to invoke the method 79 * specified by the name and by the array of arguments. 80 * <p> 81 * The {@code target} and the {@code methodName} values should not be {@code null}. 82 * Otherwise an attempt to execute this {@code Expression} 83 * will result in a {@code NullPointerException}. 84 * If the {@code arguments} value is {@code null}, 85 * an empty array is used as the value of the {@code arguments} property. 86 * 87 * @param target the target object of this statement 88 * @param methodName the name of the method to invoke on the specified target 89 * @param arguments the array of arguments to invoke the specified method 90 */ 91 @ConstructorProperties({"target", "methodName", "arguments"}) 92 public Statement(Object target, String methodName, Object[] arguments) { 93 this.target = target; 94 this.methodName = methodName; 95 this.arguments = (arguments == null) ? emptyArray : arguments.clone(); 96 } 97 98 /** 99 * Returns the target object of this statement. 100 * If this method returns {@code null}, 101 * the {@link #execute} method 102 * throws a {@code NullPointerException}. 103 * 104 * @return the target object of this statement 105 */ 106 public Object getTarget() { 107 return target; 108 } 109 110 /** 111 * Returns the name of the method to invoke. 112 * If this method returns {@code null}, 113 * the {@link #execute} method 114 * throws a {@code NullPointerException}. 115 * 116 * @return the name of the method 117 */ 118 public String getMethodName() { 119 return methodName; 120 } 121 122 /** 123 * Returns the arguments for the method to invoke. 124 * The number of arguments and their types 125 * must match the method being called. 126 * {@code null} can be used as a synonym of an empty array. 127 * 128 * @return the array of arguments 129 */ 130 public Object[] getArguments() { 131 return this.arguments.clone(); 132 } 133 134 /** 135 * The {@code execute} method finds a method whose name is the same 136 * as the {@code methodName} property, and invokes the method on 137 * the target. 138 * 139 * When the target's class defines many methods with the given name 140 * the implementation should choose the most specific method using 141 * the algorithm specified in the Java Language Specification 142 * (15.11). The dynamic class of the target and arguments are used 143 * in place of the compile-time type information and, like the 144 * {@link java.lang.reflect.Method} class itself, conversion between 145 * primitive values and their associated wrapper classes is handled 146 * internally. 147 * <p> 148 * The following method types are handled as special cases: 149 * <ul> 150 * <li> 151 * Static methods may be called by using a class object as the target. 152 * <li> 153 * The reserved method name "new" may be used to call a class's constructor 154 * as if all classes defined static "new" methods. Constructor invocations 155 * are typically considered {@code Expression}s rather than {@code Statement}s 156 * as they return a value. 157 * <li> 158 * The method names "get" and "set" defined in the {@link java.util.List} 159 * interface may also be applied to array instances, mapping to 160 * the static methods of the same name in the {@code Array} class. 161 * </ul> 162 * 163 * @throws NullPointerException if the value of the {@code target} or 164 * {@code methodName} property is {@code null} 165 * @throws NoSuchMethodException if a matching method is not found 166 * @throws SecurityException if a security manager exists and 167 * it denies the method invocation 168 * @throws Exception that is thrown by the invoked method 169 * 170 * @see java.lang.reflect.Method 171 */ 172 public void execute() throws Exception { 173 invoke(); 174 } 175 176 Object invoke() throws Exception { 177 AccessControlContext acc = this.acc; 178 if ((acc == null) && (System.getSecurityManager() != null)) { 179 throw new SecurityException("AccessControlContext is not set"); 180 } 181 try { 182 return AccessController.doPrivileged( 183 new PrivilegedExceptionAction<Object>() { 184 public Object run() throws Exception { 185 return invokeInternal(); 186 } 187 }, 188 acc 189 ); 190 } 191 catch (PrivilegedActionException exception) { 192 throw exception.getException(); 193 } 194 } 195 196 private Object invokeInternal() throws Exception { 197 Object target = getTarget(); 198 String methodName = getMethodName(); 199 200 if (target == null || methodName == null) { 201 throw new NullPointerException((target == null ? "target" : 202 "methodName") + " should not be null"); 203 } 204 205 Object[] arguments = getArguments(); 206 if (arguments == null) { 207 arguments = emptyArray; 208 } 209 // Class.forName() won't load classes outside 210 // of core from a class inside core. Special 211 // case this method. 212 if (target == Class.class && methodName.equals("forName")) { 213 return ClassFinder.resolveClass((String)arguments[0], this.loader); 214 } 215 Class<?>[] argClasses = new Class<?>[arguments.length]; 216 for(int i = 0; i < arguments.length; i++) { 217 argClasses[i] = (arguments[i] == null) ? null : arguments[i].getClass(); 218 } 219 220 AccessibleObject m = null; 221 if (target instanceof Class) { 222 /* 223 For class methods, simluate the effect of a meta class 224 by taking the union of the static methods of the 225 actual class, with the instance methods of "Class.class" 226 and the overloaded "newInstance" methods defined by the 227 constructors. 228 This way "System.class", for example, will perform both 229 the static method getProperties() and the instance method 230 getSuperclass() defined in "Class.class". 231 */ 232 if (methodName.equals("new")) { 233 methodName = "newInstance"; 234 } 235 // Provide a short form for array instantiation by faking an nary-constructor. 236 if (methodName.equals("newInstance") && ((Class)target).isArray()) { 237 Object result = Array.newInstance(((Class)target).getComponentType(), arguments.length); 238 for(int i = 0; i < arguments.length; i++) { 239 Array.set(result, i, arguments[i]); 240 } 241 return result; 242 } 243 if (methodName.equals("newInstance") && arguments.length != 0) { 244 // The Character class, as of 1.4, does not have a constructor 245 // which takes a String. All of the other "wrapper" classes 246 // for Java's primitive types have a String constructor so we 247 // fake such a constructor here so that this special case can be 248 // ignored elsewhere. 249 if (target == Character.class && arguments.length == 1 && 250 argClasses[0] == String.class) { 251 return ((String)arguments[0]).charAt(0); 252 } 253 try { 254 m = ConstructorFinder.findConstructor((Class)target, argClasses); 255 } 256 catch (NoSuchMethodException exception) { 257 m = null; 258 } 259 } 260 if (m == null && target != Class.class) { 261 m = getMethod((Class)target, methodName, argClasses); 262 } 263 if (m == null) { 264 m = getMethod(Class.class, methodName, argClasses); 265 } 266 } 267 else { 268 /* 269 This special casing of arrays is not necessary, but makes files 270 involving arrays much shorter and simplifies the archiving infrastrcure. 271 The Array.set() method introduces an unusual idea - that of a static method 272 changing the state of an instance. Normally statements with side 273 effects on objects are instance methods of the objects themselves 274 and we reinstate this rule (perhaps temporarily) by special-casing arrays. 275 */ 276 if (target.getClass().isArray() && 277 (methodName.equals("set") || methodName.equals("get"))) { 278 int index = ((Integer)arguments[0]).intValue(); 279 if (methodName.equals("get")) { 280 return Array.get(target, index); 281 } 282 else { 283 Array.set(target, index, arguments[1]); 284 return null; 285 } 286 } 287 m = getMethod(target.getClass(), methodName, argClasses); 288 } 289 if (m != null) { 290 try { 291 if (m instanceof Method) { 292 return MethodUtil.invoke((Method)m, target, arguments); 293 } 294 else { 295 return ((Constructor)m).newInstance(arguments); 296 } 297 } 298 catch (IllegalAccessException iae) { 299 throw new Exception("Statement cannot invoke: " + 300 methodName + " on " + target.getClass(), 301 iae); 302 } 303 catch (InvocationTargetException ite) { 304 Throwable te = ite.getTargetException(); 305 if (te instanceof Exception) { 306 throw (Exception)te; 307 } 308 else { 309 throw ite; 310 } 311 } 312 } 313 throw new NoSuchMethodException(toString()); 314 } 315 316 String instanceName(Object instance) { 317 if (instance == null) { 318 return "null"; 319 } else if (instance.getClass() == String.class) { 320 return "\""+(String)instance + "\""; 321 } else { 322 // Note: there is a minor problem with using the non-caching 323 // NameGenerator method. The return value will not have 324 // specific information about the inner class name. For example, 325 // In 1.4.2 an inner class would be represented as JList$1 now 326 // would be named Class. 327 328 return NameGenerator.unqualifiedClassName(instance.getClass()); 329 } 330 } 331 332 /** 333 * Prints the value of this statement using a Java-style syntax. 334 */ 335 public String toString() { 336 // Respect a subclass's implementation here. 337 Object target = getTarget(); 338 String methodName = getMethodName(); 339 Object[] arguments = getArguments(); 340 if (arguments == null) { 341 arguments = emptyArray; 342 } 343 StringBuilder result = new StringBuilder(instanceName(target) + "." + methodName + "("); 344 int n = arguments.length; 345 for(int i = 0; i < n; i++) { 346 result.append(instanceName(arguments[i])); 347 if (i != n -1) { 348 result.append(", "); 349 } 350 } 351 result.append(");"); 352 return result.toString(); 353 } 354 355 static Method getMethod(Class<?> type, String name, Class<?>... args) { 356 try { 357 return MethodFinder.findMethod(type, name, args); 358 } 359 catch (NoSuchMethodException exception) { 360 return null; 361 } 362 } 363 }