1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 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 16package javassist.tools.reflect; 17 18import java.lang.reflect.*; 19import java.util.Arrays; 20import java.io.Serializable; 21import java.io.IOException; 22import java.io.ObjectInputStream; 23import java.io.ObjectOutputStream; 24 25/** 26 * A runtime class metaobject. 27 * 28 * <p>A <code>ClassMetaobject</code> is created for every 29 * class of reflective objects. It can be used to hold values 30 * shared among the reflective objects of the same class. 31 * 32 * <p>To obtain a class metaobject, calls <code>_getClass()</code> 33 * on a reflective object. For example, 34 * 35 * <ul><pre>ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass(); 36 * </pre></ul> 37 * 38 * @see javassist.tools.reflect.Metaobject 39 * @see javassist.tools.reflect.Metalevel 40 */ 41public class ClassMetaobject implements Serializable { 42 /** 43 * The base-level methods controlled by a metaobject 44 * are renamed so that they begin with 45 * <code>methodPrefix "_m_"</code>. 46 */ 47 static final String methodPrefix = "_m_"; 48 static final int methodPrefixLen = 3; 49 50 private Class javaClass; 51 private Constructor[] constructors; 52 private Method[] methods; 53 54 /** 55 * Specifies how a <code>java.lang.Class</code> object is loaded. 56 * 57 * <p>If true, it is loaded by: 58 * <ul><pre>Thread.currentThread().getContextClassLoader().loadClass()</pre></ul> 59 * <p>If false, it is loaded by <code>Class.forName()</code>. 60 * The default value is false. 61 */ 62 public static boolean useContextClassLoader = false; 63 64 /** 65 * Constructs a <code>ClassMetaobject</code>. 66 * 67 * @param params <code>params[0]</code> is the name of the class 68 * of the reflective objects. 69 */ 70 public ClassMetaobject(String[] params) 71 { 72 try { 73 javaClass = getClassObject(params[0]); 74 } 75 catch (ClassNotFoundException e) { 76 throw new RuntimeException("not found: " + params[0] 77 + ", useContextClassLoader: " 78 + Boolean.toString(useContextClassLoader), e); 79 } 80 81 constructors = javaClass.getConstructors(); 82 methods = null; 83 } 84 85 private void writeObject(ObjectOutputStream out) throws IOException { 86 out.writeUTF(javaClass.getName()); 87 } 88 89 private void readObject(ObjectInputStream in) 90 throws IOException, ClassNotFoundException 91 { 92 javaClass = getClassObject(in.readUTF()); 93 constructors = javaClass.getConstructors(); 94 methods = null; 95 } 96 97 private Class getClassObject(String name) throws ClassNotFoundException { 98 if (useContextClassLoader) 99 return Thread.currentThread().getContextClassLoader() 100 .loadClass(name); 101 else 102 return Class.forName(name); 103 } 104 105 /** 106 * Obtains the <code>java.lang.Class</code> representing this class. 107 */ 108 public final Class getJavaClass() { 109 return javaClass; 110 } 111 112 /** 113 * Obtains the name of this class. 114 */ 115 public final String getName() { 116 return javaClass.getName(); 117 } 118 119 /** 120 * Returns true if <code>obj</code> is an instance of this class. 121 */ 122 public final boolean isInstance(Object obj) { 123 return javaClass.isInstance(obj); 124 } 125 126 /** 127 * Creates a new instance of the class. 128 * 129 * @param args the arguments passed to the constructor. 130 */ 131 public final Object newInstance(Object[] args) 132 throws CannotCreateException 133 { 134 int n = constructors.length; 135 for (int i = 0; i < n; ++i) { 136 try { 137 return constructors[i].newInstance(args); 138 } 139 catch (IllegalArgumentException e) { 140 // try again 141 } 142 catch (InstantiationException e) { 143 throw new CannotCreateException(e); 144 } 145 catch (IllegalAccessException e) { 146 throw new CannotCreateException(e); 147 } 148 catch (InvocationTargetException e) { 149 throw new CannotCreateException(e); 150 } 151 } 152 153 throw new CannotCreateException("no constructor matches"); 154 } 155 156 /** 157 * Is invoked when <code>static</code> fields of the base-level 158 * class are read and the runtime system intercepts it. 159 * This method simply returns the value of the field. 160 * 161 * <p>Every subclass of this class should redefine this method. 162 */ 163 public Object trapFieldRead(String name) { 164 Class jc = getJavaClass(); 165 try { 166 return jc.getField(name).get(null); 167 } 168 catch (NoSuchFieldException e) { 169 throw new RuntimeException(e.toString()); 170 } 171 catch (IllegalAccessException e) { 172 throw new RuntimeException(e.toString()); 173 } 174 } 175 176 /** 177 * Is invoked when <code>static</code> fields of the base-level 178 * class are modified and the runtime system intercepts it. 179 * This method simply sets the field to the given value. 180 * 181 * <p>Every subclass of this class should redefine this method. 182 */ 183 public void trapFieldWrite(String name, Object value) { 184 Class jc = getJavaClass(); 185 try { 186 jc.getField(name).set(null, value); 187 } 188 catch (NoSuchFieldException e) { 189 throw new RuntimeException(e.toString()); 190 } 191 catch (IllegalAccessException e) { 192 throw new RuntimeException(e.toString()); 193 } 194 } 195 196 /** 197 * Invokes a method whose name begins with 198 * <code>methodPrefix "_m_"</code> and the identifier. 199 * 200 * @exception CannotInvokeException if the invocation fails. 201 */ 202 static public Object invoke(Object target, int identifier, Object[] args) 203 throws Throwable 204 { 205 Method[] allmethods = target.getClass().getMethods(); 206 int n = allmethods.length; 207 String head = methodPrefix + identifier; 208 for (int i = 0; i < n; ++i) 209 if (allmethods[i].getName().startsWith(head)) { 210 try { 211 return allmethods[i].invoke(target, args); 212 } catch (java.lang.reflect.InvocationTargetException e) { 213 throw e.getTargetException(); 214 } catch (java.lang.IllegalAccessException e) { 215 throw new CannotInvokeException(e); 216 } 217 } 218 219 throw new CannotInvokeException("cannot find a method"); 220 } 221 222 /** 223 * Is invoked when <code>static</code> methods of the base-level 224 * class are called and the runtime system intercepts it. 225 * This method simply executes the intercepted method invocation 226 * with the original parameters and returns the resulting value. 227 * 228 * <p>Every subclass of this class should redefine this method. 229 */ 230 public Object trapMethodcall(int identifier, Object[] args) 231 throws Throwable 232 { 233 try { 234 Method[] m = getReflectiveMethods(); 235 return m[identifier].invoke(null, args); 236 } 237 catch (java.lang.reflect.InvocationTargetException e) { 238 throw e.getTargetException(); 239 } 240 catch (java.lang.IllegalAccessException e) { 241 throw new CannotInvokeException(e); 242 } 243 } 244 245 /** 246 * Returns an array of the methods defined on the given reflective 247 * object. This method is for the internal use only. 248 */ 249 public final Method[] getReflectiveMethods() { 250 if (methods != null) 251 return methods; 252 253 Class baseclass = getJavaClass(); 254 Method[] allmethods = baseclass.getDeclaredMethods(); 255 int n = allmethods.length; 256 int[] index = new int[n]; 257 int max = 0; 258 for (int i = 0; i < n; ++i) { 259 Method m = allmethods[i]; 260 String mname = m.getName(); 261 if (mname.startsWith(methodPrefix)) { 262 int k = 0; 263 for (int j = methodPrefixLen;; ++j) { 264 char c = mname.charAt(j); 265 if ('0' <= c && c <= '9') 266 k = k * 10 + c - '0'; 267 else 268 break; 269 } 270 271 index[i] = ++k; 272 if (k > max) 273 max = k; 274 } 275 } 276 277 methods = new Method[max]; 278 for (int i = 0; i < n; ++i) 279 if (index[i] > 0) 280 methods[index[i] - 1] = allmethods[i]; 281 282 return methods; 283 } 284 285 /** 286 * Returns the <code>java.lang.reflect.Method</code> object representing 287 * the method specified by <code>identifier</code>. 288 * 289 * <p>Note that the actual method returned will be have an altered, 290 * reflective name i.e. <code>_m_2_..</code>. 291 * 292 * @param identifier the identifier index 293 * given to <code>trapMethodcall()</code> etc. 294 * @see Metaobject#trapMethodcall(int,Object[]) 295 * @see #trapMethodcall(int,Object[]) 296 */ 297 public final Method getMethod(int identifier) { 298 return getReflectiveMethods()[identifier]; 299 } 300 301 /** 302 * Returns the name of the method specified 303 * by <code>identifier</code>. 304 */ 305 public final String getMethodName(int identifier) { 306 String mname = getReflectiveMethods()[identifier].getName(); 307 int j = ClassMetaobject.methodPrefixLen; 308 for (;;) { 309 char c = mname.charAt(j++); 310 if (c < '0' || '9' < c) 311 break; 312 } 313 314 return mname.substring(j); 315 } 316 317 /** 318 * Returns an array of <code>Class</code> objects representing the 319 * formal parameter types of the method specified 320 * by <code>identifier</code>. 321 */ 322 public final Class[] getParameterTypes(int identifier) { 323 return getReflectiveMethods()[identifier].getParameterTypes(); 324 } 325 326 /** 327 * Returns a <code>Class</code> objects representing the 328 * return type of the method specified by <code>identifier</code>. 329 */ 330 public final Class getReturnType(int identifier) { 331 return getReflectiveMethods()[identifier].getReturnType(); 332 } 333 334 /** 335 * Returns the identifier index of the method, as identified by its 336 * original name. 337 * 338 * <p>This method is useful, in conjuction with 339 * <link>ClassMetaobject#getMethod()</link>, to obtain a quick reference 340 * to the original method in the reflected class (i.e. not the proxy 341 * method), using the original name of the method. 342 * 343 * <p>Written by Brett Randall and Shigeru Chiba. 344 * 345 * @param originalName The original name of the reflected method 346 * @param argTypes array of Class specifying the method signature 347 * @return the identifier index of the original method 348 * @throws NoSuchMethodException if the method does not exist 349 * 350 * @see ClassMetaobject#getMethod(int) 351 */ 352 public final int getMethodIndex(String originalName, Class[] argTypes) 353 throws NoSuchMethodException 354 { 355 Method[] mthds = getReflectiveMethods(); 356 for (int i = 0; i < mthds.length; i++) { 357 if (mthds[i] == null) 358 continue; 359 360 // check name and parameter types match 361 if (getMethodName(i).equals(originalName) 362 && Arrays.equals(argTypes, mthds[i].getParameterTypes())) 363 return i; 364 } 365 366 throw new NoSuchMethodException("Method " + originalName 367 + " not found"); 368 } 369} 370