Home » openjdk-7 » java » beans » [javadoc | source]

    1   /*
    2    * Copyright (c) 2000, 2010, 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</code> 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)"</code>.
   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;
   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 arguments;
  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 new Character(((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           StringBuffer result = new StringBuffer(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   }

Home » openjdk-7 » java » beans » [javadoc | source]