Home | History | Annotate | Download | only in reflect
      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 
     16 package javassist.tools.reflect;
     17 
     18 import java.lang.reflect.*;
     19 import java.util.Arrays;
     20 import java.io.Serializable;
     21 import java.io.IOException;
     22 import java.io.ObjectInputStream;
     23 import 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  */
     41 public 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