/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