blob: 25d7e3307310f6780a167fa0e6a8c110a05fd0bb [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package java.beans;
26
27import java.lang.reflect.AccessibleObject;
28import java.lang.reflect.Array;
29import java.lang.reflect.Constructor;
30import java.lang.reflect.InvocationTargetException;
31import java.lang.reflect.Method;
32
33import com.sun.beans.finder.ClassFinder;
34import sun.reflect.misc.MethodUtil;
35
36/**
37 * A <code>Statement</code> object represents a primitive statement
38 * in which a single method is applied to a target and
39 * a set of arguments - as in <code>"a.setFoo(b)"</code>.
40 * Note that where this example uses names
41 * to denote the target and its argument, a statement
42 * object does not require a name space and is constructed with
43 * the values themselves.
44 * The statement object associates the named method
45 * with its environment as a simple set of values:
46 * the target and an array of argument values.
47 *
48 * @since 1.4
49 *
50 * @author Philip Milne
51 */
52public class Statement {
53
54 private static Object[] emptyArray = new Object[]{};
55
56 static ExceptionListener defaultExceptionListener = new ExceptionListener() {
57 public void exceptionThrown(Exception e) {
58 System.err.println(e);
59 // e.printStackTrace();
60 System.err.println("Continuing ...");
61 }
62 };
63
64 Object target;
65 String methodName;
66 Object[] arguments;
67
68 /**
69 * Creates a new <code>Statement</code> object with a <code>target</code>,
70 * <code>methodName</code> and <code>arguments</code> as per the parameters.
71 *
72 * @param target The target of this statement.
73 * @param methodName The methodName of this statement.
74 * @param arguments The arguments of this statement. If <code>null</code> then an empty array will be used.
75 *
76 */
77 @ConstructorProperties({"target", "methodName", "arguments"})
78 public Statement(Object target, String methodName, Object[] arguments) {
79 this.target = target;
80 this.methodName = methodName;
81 this.arguments = (arguments == null) ? emptyArray : arguments;
82 }
83
84 /**
85 * Returns the target of this statement.
86 *
87 * @return The target of this statement.
88 */
89 public Object getTarget() {
90 return target;
91 }
92
93 /**
94 * Returns the name of the method.
95 *
96 * @return The name of the method.
97 */
98 public String getMethodName() {
99 return methodName;
100 }
101
102 /**
103 * Returns the arguments of this statement.
104 *
105 * @return the arguments of this statement.
106 */
107 public Object[] getArguments() {
108 return arguments;
109 }
110
111 /**
112 * The execute method finds a method whose name is the same
113 * as the methodName property, and invokes the method on
114 * the target.
115 *
116 * When the target's class defines many methods with the given name
117 * the implementation should choose the most specific method using
118 * the algorithm specified in the Java Language Specification
119 * (15.11). The dynamic class of the target and arguments are used
120 * in place of the compile-time type information and, like the
121 * <code>java.lang.reflect.Method</code> class itself, conversion between
122 * primitive values and their associated wrapper classes is handled
123 * internally.
124 * <p>
125 * The following method types are handled as special cases:
126 * <ul>
127 * <li>
128 * Static methods may be called by using a class object as the target.
129 * <li>
130 * The reserved method name "new" may be used to call a class's constructor
131 * as if all classes defined static "new" methods. Constructor invocations
132 * are typically considered <code>Expression</code>s rather than <code>Statement</code>s
133 * as they return a value.
134 * <li>
135 * The method names "get" and "set" defined in the <code>java.util.List</code>
136 * interface may also be applied to array instances, mapping to
137 * the static methods of the same name in the <code>Array</code> class.
138 * </ul>
139 */
140 public void execute() throws Exception {
141 invoke();
142 }
143
144 Object invoke() throws Exception {
145 Object target = getTarget();
146 String methodName = getMethodName();
147
148 if (target == null || methodName == null) {
149 throw new NullPointerException((target == null ? "target" :
150 "methodName") + " should not be null");
151 }
152
153 Object[] arguments = getArguments();
154 // Class.forName() won't load classes outside
155 // of core from a class inside core. Special
156 // case this method.
157 if (target == Class.class && methodName.equals("forName")) {
158 return ClassFinder.resolveClass((String)arguments[0]);
159 }
160 Class[] argClasses = new Class[arguments.length];
161 for(int i = 0; i < arguments.length; i++) {
162 argClasses[i] = (arguments[i] == null) ? null : arguments[i].getClass();
163 }
164
165 AccessibleObject m = null;
166 if (target instanceof Class) {
167 /*
168 For class methods, simluate the effect of a meta class
169 by taking the union of the static methods of the
170 actual class, with the instance methods of "Class.class"
171 and the overloaded "newInstance" methods defined by the
172 constructors.
173 This way "System.class", for example, will perform both
174 the static method getProperties() and the instance method
175 getSuperclass() defined in "Class.class".
176 */
177 if (methodName.equals("new")) {
178 methodName = "newInstance";
179 }
180 // Provide a short form for array instantiation by faking an nary-constructor.
181 if (methodName.equals("newInstance") && ((Class)target).isArray()) {
182 Object result = Array.newInstance(((Class)target).getComponentType(), arguments.length);
183 for(int i = 0; i < arguments.length; i++) {
184 Array.set(result, i, arguments[i]);
185 }
186 return result;
187 }
188 if (methodName.equals("newInstance") && arguments.length != 0) {
189 // The Character class, as of 1.4, does not have a constructor
190 // which takes a String. All of the other "wrapper" classes
191 // for Java's primitive types have a String constructor so we
192 // fake such a constructor here so that this special case can be
193 // ignored elsewhere.
194 if (target == Character.class && arguments.length == 1 &&
195 argClasses[0] == String.class) {
196 return new Character(((String)arguments[0]).charAt(0));
197 }
198 m = ReflectionUtils.getConstructor((Class)target, argClasses);
199 }
200 if (m == null && target != Class.class) {
201 m = ReflectionUtils.getMethod((Class)target, methodName, argClasses);
202 }
203 if (m == null) {
204 m = ReflectionUtils.getMethod(Class.class, methodName, argClasses);
205 }
206 }
207 else {
208 /*
209 This special casing of arrays is not necessary, but makes files
210 involving arrays much shorter and simplifies the archiving infrastrcure.
211 The Array.set() method introduces an unusual idea - that of a static method
212 changing the state of an instance. Normally statements with side
213 effects on objects are instance methods of the objects themselves
214 and we reinstate this rule (perhaps temporarily) by special-casing arrays.
215 */
216 if (target.getClass().isArray() &&
217 (methodName.equals("set") || methodName.equals("get"))) {
218 int index = ((Integer)arguments[0]).intValue();
219 if (methodName.equals("get")) {
220 return Array.get(target, index);
221 }
222 else {
223 Array.set(target, index, arguments[1]);
224 return null;
225 }
226 }
227 m = ReflectionUtils.getMethod(target.getClass(), methodName, argClasses);
228 }
229 if (m != null) {
230 try {
231 if (m instanceof Method) {
232 return MethodUtil.invoke((Method)m, target, arguments);
233 }
234 else {
235 return ((Constructor)m).newInstance(arguments);
236 }
237 }
238 catch (IllegalAccessException iae) {
239 throw new Exception("Statement cannot invoke: " +
240 methodName + " on " + target.getClass(),
241 iae);
242 }
243 catch (InvocationTargetException ite) {
244 Throwable te = ite.getTargetException();
245 if (te instanceof Exception) {
246 throw (Exception)te;
247 }
248 else {
249 throw ite;
250 }
251 }
252 }
253 throw new NoSuchMethodException(toString());
254 }
255
256 String instanceName(Object instance) {
257 if (instance == null) {
258 return "null";
259 } else if (instance.getClass() == String.class) {
260 return "\""+(String)instance + "\"";
261 } else {
262 // Note: there is a minor problem with using the non-caching
263 // NameGenerator method. The return value will not have
264 // specific information about the inner class name. For example,
265 // In 1.4.2 an inner class would be represented as JList$1 now
266 // would be named Class.
267
268 return NameGenerator.unqualifiedClassName(instance.getClass());
269 }
270 }
271
272 /**
273 * Prints the value of this statement using a Java-style syntax.
274 */
275 public String toString() {
276 // Respect a subclass's implementation here.
277 Object target = getTarget();
278 String methodName = getMethodName();
279 Object[] arguments = getArguments();
280
281 StringBuffer result = new StringBuffer(instanceName(target) + "." + methodName + "(");
282 int n = arguments.length;
283 for(int i = 0; i < n; i++) {
284 result.append(instanceName(arguments[i]));
285 if (i != n -1) {
286 result.append(", ");
287 }
288 }
289 result.append(");");
290 return result.toString();
291 }
292}