001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.tools.reflect;
017:
018: import java.lang.reflect.*;
019: import java.util.Arrays;
020: import java.io.Serializable;
021: import java.io.IOException;
022: import java.io.ObjectInputStream;
023: import java.io.ObjectOutputStream;
024:
025: /**
026: * A runtime class metaobject.
027: *
028: * <p>A <code>ClassMetaobject</code> is created for every
029: * class of reflective objects. It can be used to hold values
030: * shared among the reflective objects of the same class.
031: *
032: * <p>To obtain a class metaobject, calls <code>_getClass()</code>
033: * on a reflective object. For example,
034: *
035: * <ul><pre>ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
036: * </pre></ul>
037: *
038: * @see javassist.tools.reflect.Metaobject
039: * @see javassist.tools.reflect.Metalevel
040: */
041: public class ClassMetaobject implements Serializable {
042: /**
043: * The base-level methods controlled by a metaobject
044: * are renamed so that they begin with
045: * <code>methodPrefix "_m_"</code>.
046: */
047: static final String methodPrefix = "_m_";
048: static final int methodPrefixLen = 3;
049:
050: private Class javaClass;
051: private Constructor[] constructors;
052: private Method[] methods;
053:
054: /**
055: * Specifies how a <code>java.lang.Class</code> object is loaded.
056: *
057: * <p>If true, it is loaded by:
058: * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul>
059: * <p>If false, it is loaded by <code>Class.forName()</code>.
060: * The default value is false.
061: */
062: public static boolean useContextClassLoader = false;
063:
064: /**
065: * Constructs a <code>ClassMetaobject</code>.
066: *
067: * @param params <code>params[0]</code> is the name of the class
068: * of the reflective objects.
069: */
070: public ClassMetaobject(String[] params) {
071: try {
072: javaClass = getClassObject(params[0]);
073: } catch (ClassNotFoundException e) {
074: javaClass = null;
075: }
076:
077: constructors = javaClass.getConstructors();
078: methods = null;
079: }
080:
081: private void writeObject(ObjectOutputStream out) throws IOException {
082: out.writeUTF(javaClass.getName());
083: }
084:
085: private void readObject(ObjectInputStream in) throws IOException,
086: ClassNotFoundException {
087: javaClass = getClassObject(in.readUTF());
088: constructors = javaClass.getConstructors();
089: methods = null;
090: }
091:
092: private Class getClassObject(String name)
093: throws ClassNotFoundException {
094: if (useContextClassLoader)
095: return Thread.currentThread().getContextClassLoader()
096: .loadClass(name);
097: else
098: return Class.forName(name);
099: }
100:
101: /**
102: * Obtains the <code>java.lang.Class</code> representing this class.
103: */
104: public final Class getJavaClass() {
105: return javaClass;
106: }
107:
108: /**
109: * Obtains the name of this class.
110: */
111: public final String getName() {
112: return javaClass.getName();
113: }
114:
115: /**
116: * Returns true if <code>obj</code> is an instance of this class.
117: */
118: public final boolean isInstance(Object obj) {
119: return javaClass.isInstance(obj);
120: }
121:
122: /**
123: * Creates a new instance of the class.
124: *
125: * @param args the arguments passed to the constructor.
126: */
127: public final Object newInstance(Object[] args)
128: throws CannotCreateException {
129: int n = constructors.length;
130: for (int i = 0; i < n; ++i) {
131: try {
132: return constructors[i].newInstance(args);
133: } catch (IllegalArgumentException e) {
134: // try again
135: } catch (InstantiationException e) {
136: throw new CannotCreateException(e);
137: } catch (IllegalAccessException e) {
138: throw new CannotCreateException(e);
139: } catch (InvocationTargetException e) {
140: throw new CannotCreateException(e);
141: }
142: }
143:
144: throw new CannotCreateException("no constructor matches");
145: }
146:
147: /**
148: * Is invoked when <code>static</code> fields of the base-level
149: * class are read and the runtime system intercepts it.
150: * This method simply returns the value of the field.
151: *
152: * <p>Every subclass of this class should redefine this method.
153: */
154: public Object trapFieldRead(String name) {
155: Class jc = getJavaClass();
156: try {
157: return jc.getField(name).get(null);
158: } catch (NoSuchFieldException e) {
159: throw new RuntimeException(e.toString());
160: } catch (IllegalAccessException e) {
161: throw new RuntimeException(e.toString());
162: }
163: }
164:
165: /**
166: * Is invoked when <code>static</code> fields of the base-level
167: * class are modified and the runtime system intercepts it.
168: * This method simply sets the field to the given value.
169: *
170: * <p>Every subclass of this class should redefine this method.
171: */
172: public void trapFieldWrite(String name, Object value) {
173: Class jc = getJavaClass();
174: try {
175: jc.getField(name).set(null, value);
176: } catch (NoSuchFieldException e) {
177: throw new RuntimeException(e.toString());
178: } catch (IllegalAccessException e) {
179: throw new RuntimeException(e.toString());
180: }
181: }
182:
183: /**
184: * Invokes a method whose name begins with
185: * <code>methodPrefix "_m_"</code> and the identifier.
186: *
187: * @exception CannotInvokeException if the invocation fails.
188: */
189: static public Object invoke(Object target, int identifier,
190: Object[] args) throws Throwable {
191: Method[] allmethods = target.getClass().getMethods();
192: int n = allmethods.length;
193: String head = methodPrefix + identifier;
194: for (int i = 0; i < n; ++i)
195: if (allmethods[i].getName().startsWith(head)) {
196: try {
197: return allmethods[i].invoke(target, args);
198: } catch (java.lang.reflect.InvocationTargetException e) {
199: throw e.getTargetException();
200: } catch (java.lang.IllegalAccessException e) {
201: throw new CannotInvokeException(e);
202: }
203: }
204:
205: throw new CannotInvokeException("cannot find a method");
206: }
207:
208: /**
209: * Is invoked when <code>static</code> methods of the base-level
210: * class are called and the runtime system intercepts it.
211: * This method simply executes the intercepted method invocation
212: * with the original parameters and returns the resulting value.
213: *
214: * <p>Every subclass of this class should redefine this method.
215: */
216: public Object trapMethodcall(int identifier, Object[] args)
217: throws Throwable {
218: try {
219: Method[] m = getReflectiveMethods();
220: return m[identifier].invoke(null, args);
221: } catch (java.lang.reflect.InvocationTargetException e) {
222: throw e.getTargetException();
223: } catch (java.lang.IllegalAccessException e) {
224: throw new CannotInvokeException(e);
225: }
226: }
227:
228: /**
229: * Returns an array of the methods defined on the given reflective
230: * object. This method is for the internal use only.
231: */
232: public final Method[] getReflectiveMethods() {
233: if (methods != null)
234: return methods;
235:
236: Class baseclass = getJavaClass();
237: Method[] allmethods = baseclass.getDeclaredMethods();
238: int n = allmethods.length;
239: int[] index = new int[n];
240: int max = 0;
241: for (int i = 0; i < n; ++i) {
242: Method m = allmethods[i];
243: String mname = m.getName();
244: if (mname.startsWith(methodPrefix)) {
245: int k = 0;
246: for (int j = methodPrefixLen;; ++j) {
247: char c = mname.charAt(j);
248: if ('0' <= c && c <= '9')
249: k = k * 10 + c - '0';
250: else
251: break;
252: }
253:
254: index[i] = ++k;
255: if (k > max)
256: max = k;
257: }
258: }
259:
260: methods = new Method[max];
261: for (int i = 0; i < n; ++i)
262: if (index[i] > 0)
263: methods[index[i] - 1] = allmethods[i];
264:
265: return methods;
266: }
267:
268: /**
269: * Returns the <code>java.lang.reflect.Method</code> object representing
270: * the method specified by <code>identifier</code>.
271: *
272: * <p>Note that the actual method returned will be have an altered,
273: * reflective name i.e. <code>_m_2_..</code>.
274: *
275: * @param identifier the identifier index
276: * given to <code>trapMethodcall()</code> etc.
277: * @see Metaobject#trapMethodcall(int,Object[])
278: * @see #trapMethodcall(int,Object[])
279: */
280: public final Method getMethod(int identifier) {
281: return getReflectiveMethods()[identifier];
282: }
283:
284: /**
285: * Returns the name of the method specified
286: * by <code>identifier</code>.
287: */
288: public final String getMethodName(int identifier) {
289: String mname = getReflectiveMethods()[identifier].getName();
290: int j = ClassMetaobject.methodPrefixLen;
291: for (;;) {
292: char c = mname.charAt(j++);
293: if (c < '0' || '9' < c)
294: break;
295: }
296:
297: return mname.substring(j);
298: }
299:
300: /**
301: * Returns an array of <code>Class</code> objects representing the
302: * formal parameter types of the method specified
303: * by <code>identifier</code>.
304: */
305: public final Class[] getParameterTypes(int identifier) {
306: return getReflectiveMethods()[identifier].getParameterTypes();
307: }
308:
309: /**
310: * Returns a <code>Class</code> objects representing the
311: * return type of the method specified by <code>identifier</code>.
312: */
313: public final Class getReturnType(int identifier) {
314: return getReflectiveMethods()[identifier].getReturnType();
315: }
316:
317: /**
318: * Returns the identifier index of the method, as identified by its
319: * original name.
320: *
321: * <p>This method is useful, in conjuction with
322: * <link>ClassMetaobject#getMethod()</link>, to obtain a quick reference
323: * to the original method in the reflected class (i.e. not the proxy
324: * method), using the original name of the method.
325: *
326: * <p>Written by Brett Randall and Shigeru Chiba.
327: *
328: * @param originalName The original name of the reflected method
329: * @param argTypes array of Class specifying the method signature
330: * @return the identifier index of the original method
331: * @throws NoSuchMethodException if the method does not exist
332: *
333: * @see ClassMetaobject#getMethod(int)
334: */
335: public final int getMethodIndex(String originalName,
336: Class[] argTypes) throws NoSuchMethodException {
337: Method[] mthds = getReflectiveMethods();
338: for (int i = 0; i < mthds.length; i++) {
339: if (mthds[i] == null)
340: continue;
341:
342: // check name and parameter types match
343: if (getMethodName(i).equals(originalName)
344: && Arrays.equals(argTypes, mthds[i]
345: .getParameterTypes()))
346: return i;
347: }
348:
349: throw new NoSuchMethodException("Method " + originalName
350: + " not found");
351: }
352: }
|