/Users/lyon/j4p/src/javassist/reflect/ClassMetaobject.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16 package javassist.reflect;
17
18 import java.lang.reflect.*;
19 import java.io.Serializable;
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23
24 import javassist.CtClass;
25
26 /**
27 * A runtime class metaobject.
28 *
29 * <p>A <code>ClassMetaobject</code> is created for every
30 * class of reflective objects. It can be used to hold values
31 * shared among the reflective objects of the same class.
32 *
33 * @see javassist.reflect.Metaobject
34 */
35 public class ClassMetaobject implements Serializable {
36 /**
37 * The base-level methods controlled by a metaobject
38 * are renamed so that they begin with
39 * <code>methodPrefix "_m_"</code>.
40 */
41 static final String methodPrefix = "_m_";
42 static final int methodPrefixLen = 3;
43
44 private Class javaClass;
45 private Constructor[] constructors;
46 private Method[] methods;
47
48 /**
49 * Specifies how a <code>java.lang.Class</code> object is loaded.
50 *
51 * <p>If true, it is loaded by:
52 * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul>
53 * <p>If false, it is loaded by <code>Class.forName()</code>.
54 * The default value is false.
55 */
56 public static boolean useContextClassLoader = false;
57
58 /**
59 * Constructs a <code>ClassMetaobject</code>.
60 *
61 * @param params <code>params[0]</code> is the name of the class
62 * of the reflective objects.
63 */
64 public ClassMetaobject(String[] params) {
65 try {
66 javaClass = getClassObject(params[0]);
67 } catch (ClassNotFoundException e) {
68 javaClass = null;
69 }
70
71 constructors = javaClass.getConstructors();
72 methods = null;
73 }
74
75 private void writeObject(ObjectOutputStream out) throws IOException {
76 out.writeUTF(javaClass.getName());
77 }
78
79 private void readObject(ObjectInputStream in)
80 throws IOException, ClassNotFoundException {
81 javaClass = getClassObject(in.readUTF());
82 constructors = javaClass.getConstructors();
83 methods = null;
84 }
85
86 private Class getClassObject(String name) throws ClassNotFoundException {
87 if (useContextClassLoader)
88 return Thread.currentThread().getContextClassLoader()
89 .loadClass(name);
90 else
91 return Class.forName(name);
92 }
93
94 /**
95 * Obtains the <code>java.lang.Class</code> representing this class.
96 */
97 public final Class getJavaClass() {
98 return javaClass;
99 }
100
101 /**
102 * Obtains the name of this class.
103 */
104 public final String getName() {
105 return javaClass.getName();
106 }
107
108 /**
109 * Returns true if <code>obj</code> is an instance of this class.
110 */
111 public final boolean isInstance(Object obj) {
112 return javaClass.isInstance(obj);
113 }
114
115 /**
116 * Creates a new instance of the class.
117 *
118 * @param args the arguments passed to the constructor.
119 */
120 public final Object newInstance(Object[] args)
121 throws CannotCreateException {
122 int n = constructors.length;
123 for (int i = 0; i < n; ++i) {
124 try {
125 return constructors[i].newInstance(args);
126 } catch (IllegalArgumentException e) {
127 // try again
128 } catch (InstantiationException e) {
129 throw new CannotCreateException(e);
130 } catch (IllegalAccessException e) {
131 throw new CannotCreateException(e);
132 } catch (InvocationTargetException e) {
133 throw new CannotCreateException(e);
134 }
135 }
136
137 throw new CannotCreateException("no constructor matches");
138 }
139
140 /**
141 * Is invoked when <code>static</code> fields of the base-level
142 * class are read and the runtime system intercepts it.
143 * This method simply returns the value of the field.
144 *
145 * <p>Every subclass of this class should redefine this method.
146 */
147 public Object trapFieldRead(String name) {
148 Class jc = getJavaClass();
149 try {
150 return jc.getField(name).get(null);
151 } catch (NoSuchFieldException e) {
152 throw new RuntimeException(e.toString());
153 } catch (IllegalAccessException e) {
154 throw new RuntimeException(e.toString());
155 }
156 }
157
158 /**
159 * Is invoked when <code>static</code> fields of the base-level
160 * class are modified and the runtime system intercepts it.
161 * This method simply sets the field to the given value.
162 *
163 * <p>Every subclass of this class should redefine this method.
164 */
165 public void trapFieldWrite(String name, Object value) {
166 Class jc = getJavaClass();
167 try {
168 jc.getField(name).set(null, value);
169 } catch (NoSuchFieldException e) {
170 throw new RuntimeException(e.toString());
171 } catch (IllegalAccessException e) {
172 throw new RuntimeException(e.toString());
173 }
174 }
175
176 /**
177 * Invokes a method whose name begins with
178 * <code>methodPrefix "_m_"</code> and the identifier.
179 *
180 * @exception CannotInvokeException if the invocation fails.
181 */
182 static public Object invoke(Object target, int identifier, Object[] args)
183 throws Throwable {
184 Method[] allmethods = target.getClass().getMethods();
185 int n = allmethods.length;
186 String head = methodPrefix + identifier;
187 for (int i = 0; i < n; ++i)
188 if (allmethods[i].getName().startsWith(head)) {
189 try {
190 return allmethods[i].invoke(target, args);
191 } catch (java.lang.reflect.InvocationTargetException e) {
192 throw e.getTargetException();
193 } catch (java.lang.IllegalAccessException e) {
194 throw new CannotInvokeException(e);
195 }
196 }
197
198 throw new CannotInvokeException("cannot find a method");
199 }
200
201 /**
202 * Is invoked when <code>static</code> methods of the base-level
203 * class are called and the runtime system intercepts it.
204 * This method simply executes the intercepted method invocation
205 * with the original parameters and returns the resulting value.
206 *
207 * <p>Every subclass of this class should redefine this method.
208 */
209 public Object trapMethodcall(int identifier, Object[] args)
210 throws Throwable {
211 try {
212 Method[] m = getReflectiveMethods();
213 return m[identifier].invoke(null, args);
214 } catch (java.lang.reflect.InvocationTargetException e) {
215 throw e.getTargetException();
216 } catch (java.lang.IllegalAccessException e) {
217 throw new CannotInvokeException(e);
218 }
219 }
220
221 /**
222 * Returns an array of the methods defined on the given reflective
223 * object. This method is for the internal use only.
224 */
225 public final Method[] getReflectiveMethods() {
226 if (methods != null)
227 return methods;
228
229 Class baseclass = getJavaClass();
230 Method[] allmethods = baseclass.getMethods();
231 int n = allmethods.length;
232 methods = new Method[n];
233 for (int i = 0; i < n; ++i) {
234 Method m = allmethods[i];
235 if (m.getDeclaringClass() == baseclass) {
236 String mname = m.getName();
237 if (mname.startsWith(methodPrefix)) {
238 int k = 0;
239 for (int j = methodPrefixLen; ; ++j) {
240 char c = mname.charAt(j);
241 if ('0' <= c && c <= '9')
242 k = k * 10 + c - '0';
243 else
244 break;
245 }
246
247 methods[k] = m;
248 }
249 }
250 }
251
252 return methods;
253 }
254
255 /**
256 * Returns the name of the method specified
257 * by <code>identifier</code>.
258 */
259 public final String getMethodName(int identifier) {
260 String mname = getReflectiveMethods()[identifier].getName();
261 int j = ClassMetaobject.methodPrefixLen;
262 for (; ;) {
263 char c = mname.charAt(j++);
264 if (c < '0' || '9' < c)
265 break;
266 }
267
268 return mname.substring(j);
269 }
270
271 /**
272 * Returns an array of <code>Class</code> objects representing the
273 * formal parameter types of the method specified
274 * by <code>identifier</code>.
275 */
276 public final Class[] getParameterTypes(int identifier) {
277 return getReflectiveMethods()[identifier].getParameterTypes();
278 }
279
280 /**
281 * Returns a <code>Class</code> objects representing the
282 * return type of the method specified by <code>identifier</code>.
283 */
284 public final Class getReturnType(int identifier) {
285 return getReflectiveMethods()[identifier].getReturnType();
286 }
287 }
288