You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ClassMetaobject.java 12KB

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