Path: blob/master/src/java.desktop/share/classes/java/beans/Statement.java
41152 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package java.beans;2526import java.lang.reflect.AccessibleObject;27import java.lang.reflect.Array;28import java.lang.reflect.Constructor;29import java.lang.reflect.InvocationTargetException;30import java.lang.reflect.Method;31import java.security.AccessControlContext;32import java.security.AccessController;33import java.security.PrivilegedActionException;34import java.security.PrivilegedExceptionAction;3536import com.sun.beans.finder.ClassFinder;37import com.sun.beans.finder.ConstructorFinder;38import com.sun.beans.finder.MethodFinder;39import sun.reflect.misc.MethodUtil;4041import static sun.reflect.misc.ReflectUtil.checkPackageAccess;4243/**44* A {@code Statement} object represents a primitive statement45* in which a single method is applied to a target and46* a set of arguments - as in {@code "a.setFoo(b)"}.47* Note that where this example uses names48* to denote the target and its argument, a statement49* object does not require a name space and is constructed with50* the values themselves.51* The statement object associates the named method52* with its environment as a simple set of values:53* the target and an array of argument values.54*55* @since 1.456*57* @author Philip Milne58*/59public class Statement {6061private static Object[] emptyArray = new Object[]{};6263static ExceptionListener defaultExceptionListener = new ExceptionListener() {64public void exceptionThrown(Exception e) {65System.err.println(e);66// e.printStackTrace();67System.err.println("Continuing ...");68}69};7071@SuppressWarnings("removal")72private final AccessControlContext acc = AccessController.getContext();73private final Object target;74private final String methodName;75private final Object[] arguments;76ClassLoader loader;7778/**79* Creates a new {@link Statement} object80* for the specified target object to invoke the method81* specified by the name and by the array of arguments.82* <p>83* The {@code target} and the {@code methodName} values should not be {@code null}.84* Otherwise an attempt to execute this {@code Expression}85* will result in a {@code NullPointerException}.86* If the {@code arguments} value is {@code null},87* an empty array is used as the value of the {@code arguments} property.88*89* @param target the target object of this statement90* @param methodName the name of the method to invoke on the specified target91* @param arguments the array of arguments to invoke the specified method92*/93@ConstructorProperties({"target", "methodName", "arguments"})94public Statement(Object target, String methodName, Object[] arguments) {95this.target = target;96this.methodName = methodName;97this.arguments = (arguments == null) ? emptyArray : arguments.clone();98}99100/**101* Returns the target object of this statement.102* If this method returns {@code null},103* the {@link #execute} method104* throws a {@code NullPointerException}.105*106* @return the target object of this statement107*/108public Object getTarget() {109return target;110}111112/**113* Returns the name of the method to invoke.114* If this method returns {@code null},115* the {@link #execute} method116* throws a {@code NullPointerException}.117*118* @return the name of the method119*/120public String getMethodName() {121return methodName;122}123124/**125* Returns the arguments for the method to invoke.126* The number of arguments and their types127* must match the method being called.128* {@code null} can be used as a synonym of an empty array.129*130* @return the array of arguments131*/132public Object[] getArguments() {133return this.arguments.clone();134}135136/**137* The {@code execute} method finds a method whose name is the same138* as the {@code methodName} property, and invokes the method on139* the target.140*141* When the target's class defines many methods with the given name142* the implementation should choose the most specific method using143* the algorithm specified in the Java Language Specification144* (15.11). The dynamic class of the target and arguments are used145* in place of the compile-time type information and, like the146* {@link java.lang.reflect.Method} class itself, conversion between147* primitive values and their associated wrapper classes is handled148* internally.149* <p>150* The following method types are handled as special cases:151* <ul>152* <li>153* Static methods may be called by using a class object as the target.154* <li>155* The reserved method name "new" may be used to call a class's constructor156* as if all classes defined static "new" methods. Constructor invocations157* are typically considered {@code Expression}s rather than {@code Statement}s158* as they return a value.159* <li>160* The method names "get" and "set" defined in the {@link java.util.List}161* interface may also be applied to array instances, mapping to162* the static methods of the same name in the {@code Array} class.163* </ul>164*165* @throws NullPointerException if the value of the {@code target} or166* {@code methodName} property is {@code null}167* @throws NoSuchMethodException if a matching method is not found168* @throws SecurityException if a security manager exists and169* it denies the method invocation170* @throws Exception that is thrown by the invoked method171*172* @see java.lang.reflect.Method173*/174public void execute() throws Exception {175invoke();176}177178@SuppressWarnings("removal")179Object invoke() throws Exception {180AccessControlContext acc = this.acc;181if ((acc == null) && (System.getSecurityManager() != null)) {182throw new SecurityException("AccessControlContext is not set");183}184try {185return AccessController.doPrivileged(186new PrivilegedExceptionAction<Object>() {187public Object run() throws Exception {188return invokeInternal();189}190},191acc192);193}194catch (PrivilegedActionException exception) {195throw exception.getException();196}197}198199private Object invokeInternal() throws Exception {200Object target = getTarget();201String methodName = getMethodName();202203if (target == null || methodName == null) {204throw new NullPointerException((target == null ? "target" :205"methodName") + " should not be null");206}207208Object[] arguments = getArguments();209if (arguments == null) {210arguments = emptyArray;211} else {212arguments = arguments.clone();213}214if (target == Class.class && methodName.equals("forName")) {215final String name = (String) arguments[0];216if (arguments.length == 1) {217// Class.forName(String className) won't load classes outside218// of core from a class inside core. Special219// case this method.220// checkPackageAccess(name) will be called by ClassFinder221return ClassFinder.resolveClass(name, this.loader);222}223// The 3 args Class.forName(String className, boolean, classloader)224// requires getClassLoader permission, but we will be stricter and225// will require access to the package as well.226checkPackageAccess(name);227}228Class<?>[] argClasses = new Class<?>[arguments.length];229for(int i = 0; i < arguments.length; i++) {230argClasses[i] = (arguments[i] == null) ? null : arguments[i].getClass();231}232233AccessibleObject m = null;234if (target instanceof Class) {235/*236For class methods, simluate the effect of a meta class237by taking the union of the static methods of the238actual class, with the instance methods of "Class.class"239and the overloaded "newInstance" methods defined by the240constructors.241This way "System.class", for example, will perform both242the static method getProperties() and the instance method243getSuperclass() defined in "Class.class".244*/245if (methodName.equals("new")) {246methodName = "newInstance";247}248// Provide a short form for array instantiation by faking an nary-constructor.249if (methodName.equals("newInstance") && ((Class)target).isArray()) {250Object result = Array.newInstance(((Class)target).getComponentType(), arguments.length);251for(int i = 0; i < arguments.length; i++) {252Array.set(result, i, arguments[i]);253}254return result;255}256if (methodName.equals("newInstance") && arguments.length != 0) {257// The Character class, as of 1.4, does not have a constructor258// which takes a String. All of the other "wrapper" classes259// for Java's primitive types have a String constructor so we260// fake such a constructor here so that this special case can be261// ignored elsewhere.262if (target == Character.class && arguments.length == 1 &&263argClasses[0] == String.class) {264return ((String)arguments[0]).charAt(0);265}266try {267m = ConstructorFinder.findConstructor((Class)target, argClasses);268}269catch (NoSuchMethodException exception) {270m = null;271}272}273if (m == null && target != Class.class) {274m = getMethod((Class)target, methodName, argClasses);275}276if (m == null) {277m = getMethod(Class.class, methodName, argClasses);278}279}280else {281/*282This special casing of arrays is not necessary, but makes files283involving arrays much shorter and simplifies the archiving infrastrcure.284The Array.set() method introduces an unusual idea - that of a static method285changing the state of an instance. Normally statements with side286effects on objects are instance methods of the objects themselves287and we reinstate this rule (perhaps temporarily) by special-casing arrays.288*/289if (target.getClass().isArray() &&290(methodName.equals("set") || methodName.equals("get"))) {291int index = ((Integer)arguments[0]).intValue();292if (methodName.equals("get")) {293return Array.get(target, index);294}295else {296Array.set(target, index, arguments[1]);297return null;298}299}300m = getMethod(target.getClass(), methodName, argClasses);301}302if (m != null) {303try {304if (m instanceof Method) {305return MethodUtil.invoke((Method)m, target, arguments);306}307else {308return ((Constructor)m).newInstance(arguments);309}310}311catch (IllegalAccessException iae) {312throw new Exception("Statement cannot invoke: " +313methodName + " on " + target.getClass(),314iae);315}316catch (InvocationTargetException ite) {317Throwable te = ite.getCause();318if (te instanceof Exception) {319throw (Exception)te;320}321else {322throw ite;323}324}325}326throw new NoSuchMethodException(toString());327}328329String instanceName(Object instance) {330if (instance == null) {331return "null";332} else if (instance.getClass() == String.class) {333return "\""+(String)instance + "\"";334} else {335// Note: there is a minor problem with using the non-caching336// NameGenerator method. The return value will not have337// specific information about the inner class name. For example,338// In 1.4.2 an inner class would be represented as JList$1 now339// would be named Class.340341return NameGenerator.unqualifiedClassName(instance.getClass());342}343}344345/**346* Prints the value of this statement using a Java-style syntax.347*/348public String toString() {349// Respect a subclass's implementation here.350Object target = getTarget();351String methodName = getMethodName();352Object[] arguments = getArguments();353if (arguments == null) {354arguments = emptyArray;355}356StringBuilder result = new StringBuilder(instanceName(target) + "." + methodName + "(");357int n = arguments.length;358for(int i = 0; i < n; i++) {359result.append(instanceName(arguments[i]));360if (i != n -1) {361result.append(", ");362}363}364result.append(");");365return result.toString();366}367368static Method getMethod(Class<?> type, String name, Class<?>... args) {369try {370return MethodFinder.findMethod(type, name, args);371}372catch (NoSuchMethodException exception) {373return null;374}375}376}377378379