You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
13 KiB
364 lines
13 KiB
/*
|
|
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
package java.beans;
|
|
|
|
import java.lang.reflect.AccessibleObject;
|
|
import java.lang.reflect.Array;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.security.AccessControlContext;
|
|
import java.security.AccessController;
|
|
import java.security.PrivilegedActionException;
|
|
import java.security.PrivilegedExceptionAction;
|
|
|
|
import com.sun.beans.finder.ClassFinder;
|
|
import com.sun.beans.finder.ConstructorFinder;
|
|
import com.sun.beans.finder.MethodFinder;
|
|
import sun.reflect.misc.MethodUtil;
|
|
|
|
/**
|
|
* A <code>Statement</code> object represents a primitive statement
|
|
* in which a single method is applied to a target and
|
|
* a set of arguments - as in <code>"a.setFoo(b)"</code>.
|
|
* Note that where this example uses names
|
|
* to denote the target and its argument, a statement
|
|
* object does not require a name space and is constructed with
|
|
* the values themselves.
|
|
* The statement object associates the named method
|
|
* with its environment as a simple set of values:
|
|
* the target and an array of argument values.
|
|
*
|
|
* @since 1.4
|
|
*
|
|
* @author Philip Milne
|
|
*/
|
|
public class Statement {
|
|
|
|
private static Object[] emptyArray = new Object[]{};
|
|
|
|
static ExceptionListener defaultExceptionListener = new ExceptionListener() {
|
|
public void exceptionThrown(Exception e) {
|
|
System.err.println(e);
|
|
// e.printStackTrace();
|
|
System.err.println("Continuing ...");
|
|
}
|
|
};
|
|
|
|
private final AccessControlContext acc = AccessController.getContext();
|
|
private final Object target;
|
|
private final String methodName;
|
|
private final Object[] arguments;
|
|
ClassLoader loader;
|
|
|
|
/**
|
|
* Creates a new {@link Statement} object
|
|
* for the specified target object to invoke the method
|
|
* specified by the name and by the array of arguments.
|
|
* <p>
|
|
* The {@code target} and the {@code methodName} values should not be {@code null}.
|
|
* Otherwise an attempt to execute this {@code Expression}
|
|
* will result in a {@code NullPointerException}.
|
|
* If the {@code arguments} value is {@code null},
|
|
* an empty array is used as the value of the {@code arguments} property.
|
|
*
|
|
* @param target the target object of this statement
|
|
* @param methodName the name of the method to invoke on the specified target
|
|
* @param arguments the array of arguments to invoke the specified method
|
|
*/
|
|
@ConstructorProperties({"target", "methodName", "arguments"})
|
|
public Statement(Object target, String methodName, Object[] arguments) {
|
|
this.target = target;
|
|
this.methodName = methodName;
|
|
this.arguments = (arguments == null) ? emptyArray : arguments.clone();
|
|
}
|
|
|
|
/**
|
|
* Returns the target object of this statement.
|
|
* If this method returns {@code null},
|
|
* the {@link #execute} method
|
|
* throws a {@code NullPointerException}.
|
|
*
|
|
* @return the target object of this statement
|
|
*/
|
|
public Object getTarget() {
|
|
return target;
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the method to invoke.
|
|
* If this method returns {@code null},
|
|
* the {@link #execute} method
|
|
* throws a {@code NullPointerException}.
|
|
*
|
|
* @return the name of the method
|
|
*/
|
|
public String getMethodName() {
|
|
return methodName;
|
|
}
|
|
|
|
/**
|
|
* Returns the arguments for the method to invoke.
|
|
* The number of arguments and their types
|
|
* must match the method being called.
|
|
* {@code null} can be used as a synonym of an empty array.
|
|
*
|
|
* @return the array of arguments
|
|
*/
|
|
public Object[] getArguments() {
|
|
return this.arguments.clone();
|
|
}
|
|
|
|
/**
|
|
* The {@code execute} method finds a method whose name is the same
|
|
* as the {@code methodName} property, and invokes the method on
|
|
* the target.
|
|
*
|
|
* When the target's class defines many methods with the given name
|
|
* the implementation should choose the most specific method using
|
|
* the algorithm specified in the Java Language Specification
|
|
* (15.11). The dynamic class of the target and arguments are used
|
|
* in place of the compile-time type information and, like the
|
|
* {@link java.lang.reflect.Method} class itself, conversion between
|
|
* primitive values and their associated wrapper classes is handled
|
|
* internally.
|
|
* <p>
|
|
* The following method types are handled as special cases:
|
|
* <ul>
|
|
* <li>
|
|
* Static methods may be called by using a class object as the target.
|
|
* <li>
|
|
* The reserved method name "new" may be used to call a class's constructor
|
|
* as if all classes defined static "new" methods. Constructor invocations
|
|
* are typically considered {@code Expression}s rather than {@code Statement}s
|
|
* as they return a value.
|
|
* <li>
|
|
* The method names "get" and "set" defined in the {@link java.util.List}
|
|
* interface may also be applied to array instances, mapping to
|
|
* the static methods of the same name in the {@code Array} class.
|
|
* </ul>
|
|
*
|
|
* @throws NullPointerException if the value of the {@code target} or
|
|
* {@code methodName} property is {@code null}
|
|
* @throws NoSuchMethodException if a matching method is not found
|
|
* @throws SecurityException if a security manager exists and
|
|
* it denies the method invocation
|
|
* @throws Exception that is thrown by the invoked method
|
|
*
|
|
* @see java.lang.reflect.Method
|
|
*/
|
|
public void execute() throws Exception {
|
|
invoke();
|
|
}
|
|
|
|
Object invoke() throws Exception {
|
|
AccessControlContext acc = this.acc;
|
|
if ((acc == null) && (System.getSecurityManager() != null)) {
|
|
throw new SecurityException("AccessControlContext is not set");
|
|
}
|
|
try {
|
|
return AccessController.doPrivileged(
|
|
new PrivilegedExceptionAction<Object>() {
|
|
public Object run() throws Exception {
|
|
return invokeInternal();
|
|
}
|
|
},
|
|
acc
|
|
);
|
|
}
|
|
catch (PrivilegedActionException exception) {
|
|
throw exception.getException();
|
|
}
|
|
}
|
|
|
|
private Object invokeInternal() throws Exception {
|
|
Object target = getTarget();
|
|
String methodName = getMethodName();
|
|
|
|
if (target == null || methodName == null) {
|
|
throw new NullPointerException((target == null ? "target" :
|
|
"methodName") + " should not be null");
|
|
}
|
|
|
|
Object[] arguments = getArguments();
|
|
if (arguments == null) {
|
|
arguments = emptyArray;
|
|
}
|
|
// Class.forName() won't load classes outside
|
|
// of core from a class inside core. Special
|
|
// case this method.
|
|
if (target == Class.class && methodName.equals("forName")) {
|
|
return ClassFinder.resolveClass((String)arguments[0], this.loader);
|
|
}
|
|
Class<?>[] argClasses = new Class<?>[arguments.length];
|
|
for(int i = 0; i < arguments.length; i++) {
|
|
argClasses[i] = (arguments[i] == null) ? null : arguments[i].getClass();
|
|
}
|
|
|
|
AccessibleObject m = null;
|
|
if (target instanceof Class) {
|
|
/*
|
|
For class methods, simluate the effect of a meta class
|
|
by taking the union of the static methods of the
|
|
actual class, with the instance methods of "Class.class"
|
|
and the overloaded "newInstance" methods defined by the
|
|
constructors.
|
|
This way "System.class", for example, will perform both
|
|
the static method getProperties() and the instance method
|
|
getSuperclass() defined in "Class.class".
|
|
*/
|
|
if (methodName.equals("new")) {
|
|
methodName = "newInstance";
|
|
}
|
|
// Provide a short form for array instantiation by faking an nary-constructor.
|
|
if (methodName.equals("newInstance") && ((Class)target).isArray()) {
|
|
Object result = Array.newInstance(((Class)target).getComponentType(), arguments.length);
|
|
for(int i = 0; i < arguments.length; i++) {
|
|
Array.set(result, i, arguments[i]);
|
|
}
|
|
return result;
|
|
}
|
|
if (methodName.equals("newInstance") && arguments.length != 0) {
|
|
// The Character class, as of 1.4, does not have a constructor
|
|
// which takes a String. All of the other "wrapper" classes
|
|
// for Java's primitive types have a String constructor so we
|
|
// fake such a constructor here so that this special case can be
|
|
// ignored elsewhere.
|
|
if (target == Character.class && arguments.length == 1 &&
|
|
argClasses[0] == String.class) {
|
|
return new Character(((String)arguments[0]).charAt(0));
|
|
}
|
|
try {
|
|
m = ConstructorFinder.findConstructor((Class)target, argClasses);
|
|
}
|
|
catch (NoSuchMethodException exception) {
|
|
m = null;
|
|
}
|
|
}
|
|
if (m == null && target != Class.class) {
|
|
m = getMethod((Class)target, methodName, argClasses);
|
|
}
|
|
if (m == null) {
|
|
m = getMethod(Class.class, methodName, argClasses);
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
This special casing of arrays is not necessary, but makes files
|
|
involving arrays much shorter and simplifies the archiving infrastrcure.
|
|
The Array.set() method introduces an unusual idea - that of a static method
|
|
changing the state of an instance. Normally statements with side
|
|
effects on objects are instance methods of the objects themselves
|
|
and we reinstate this rule (perhaps temporarily) by special-casing arrays.
|
|
*/
|
|
if (target.getClass().isArray() &&
|
|
(methodName.equals("set") || methodName.equals("get"))) {
|
|
int index = ((Integer)arguments[0]).intValue();
|
|
if (methodName.equals("get")) {
|
|
return Array.get(target, index);
|
|
}
|
|
else {
|
|
Array.set(target, index, arguments[1]);
|
|
return null;
|
|
}
|
|
}
|
|
m = getMethod(target.getClass(), methodName, argClasses);
|
|
}
|
|
if (m != null) {
|
|
try {
|
|
if (m instanceof Method) {
|
|
return MethodUtil.invoke((Method)m, target, arguments);
|
|
}
|
|
else {
|
|
return ((Constructor)m).newInstance(arguments);
|
|
}
|
|
}
|
|
catch (IllegalAccessException iae) {
|
|
throw new Exception("Statement cannot invoke: " +
|
|
methodName + " on " + target.getClass(),
|
|
iae);
|
|
}
|
|
catch (InvocationTargetException ite) {
|
|
Throwable te = ite.getTargetException();
|
|
if (te instanceof Exception) {
|
|
throw (Exception)te;
|
|
}
|
|
else {
|
|
throw ite;
|
|
}
|
|
}
|
|
}
|
|
throw new NoSuchMethodException(toString());
|
|
}
|
|
|
|
String instanceName(Object instance) {
|
|
if (instance == null) {
|
|
return "null";
|
|
} else if (instance.getClass() == String.class) {
|
|
return "\""+(String)instance + "\"";
|
|
} else {
|
|
// Note: there is a minor problem with using the non-caching
|
|
// NameGenerator method. The return value will not have
|
|
// specific information about the inner class name. For example,
|
|
// In 1.4.2 an inner class would be represented as JList$1 now
|
|
// would be named Class.
|
|
|
|
return NameGenerator.unqualifiedClassName(instance.getClass());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prints the value of this statement using a Java-style syntax.
|
|
*/
|
|
public String toString() {
|
|
// Respect a subclass's implementation here.
|
|
Object target = getTarget();
|
|
String methodName = getMethodName();
|
|
Object[] arguments = getArguments();
|
|
if (arguments == null) {
|
|
arguments = emptyArray;
|
|
}
|
|
StringBuffer result = new StringBuffer(instanceName(target) + "." + methodName + "(");
|
|
int n = arguments.length;
|
|
for(int i = 0; i < n; i++) {
|
|
result.append(instanceName(arguments[i]));
|
|
if (i != n -1) {
|
|
result.append(", ");
|
|
}
|
|
}
|
|
result.append(");");
|
|
return result.toString();
|
|
}
|
|
|
|
static Method getMethod(Class<?> type, String name, Class<?>... args) {
|
|
try {
|
|
return MethodFinder.findMethod(type, name, args);
|
|
}
|
|
catch (NoSuchMethodException exception) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|