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.Method;
19 import java.io.Serializable;
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 
24 /**
25  * A runtime metaobject.
26  *
27  * <p>A <code>Metaobject</code> is created for
28  * every object at the base level.  A different reflective object is
29  * associated with a different metaobject.
30  *
31  * <p>The metaobject intercepts method calls
32  * on the reflective object at the base-level.  To change the behavior
33  * of the method calls, a subclass of <code>Metaobject</code>
34  * should be defined.
35  *
36  * <p>To obtain a metaobject, calls <code>_getMetaobject()</code>
37  * on a reflective object.  For example,
38  *
39  * <ul><pre>Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
40  * </pre></ul>
41  *
42  * @see javassist.tools.reflect.ClassMetaobject
43  * @see javassist.tools.reflect.Metalevel
44  */
45 public class Metaobject implements Serializable {
46     protected ClassMetaobject classmetaobject;
47     protected Metalevel baseobject;
48     protected Method[] methods;
49 
50     /**
51      * Constructs a <code>Metaobject</code>.  The metaobject is
52      * constructed before the constructor is called on the base-level
53      * object.
54      *
55      * @param self      the object that this metaobject is associated with.
56      * @param args      the parameters passed to the constructor of
57      *                  <code>self</code>.
58      */
Metaobject(Object self, Object[] args)59     public Metaobject(Object self, Object[] args) {
60         baseobject = (Metalevel)self;
61         classmetaobject = baseobject._getClass();
62         methods = classmetaobject.getReflectiveMethods();
63     }
64 
65     /**
66      * Constructs a <code>Metaobject</code> without initialization.
67      * If calling this constructor, a subclass should be responsible
68      * for initialization.
69      */
Metaobject()70     protected Metaobject() {
71         baseobject = null;
72         classmetaobject = null;
73         methods = null;
74     }
75 
writeObject(ObjectOutputStream out)76     private void writeObject(ObjectOutputStream out) throws IOException {
77         out.writeObject(baseobject);
78     }
79 
readObject(ObjectInputStream in)80     private void readObject(ObjectInputStream in)
81         throws IOException, ClassNotFoundException
82     {
83         baseobject = (Metalevel)in.readObject();
84         classmetaobject = baseobject._getClass();
85         methods = classmetaobject.getReflectiveMethods();
86     }
87 
88     /**
89      * Obtains the class metaobject associated with this metaobject.
90      *
91      * @see javassist.tools.reflect.ClassMetaobject
92      */
getClassMetaobject()93     public final ClassMetaobject getClassMetaobject() {
94         return classmetaobject;
95     }
96 
97     /**
98      * Obtains the object controlled by this metaobject.
99      */
getObject()100     public final Object getObject() {
101         return baseobject;
102     }
103 
104     /**
105      * Changes the object controlled by this metaobject.
106      *
107      * @param self      the object
108      */
setObject(Object self)109     public final void setObject(Object self) {
110         baseobject = (Metalevel)self;
111         classmetaobject = baseobject._getClass();
112         methods = classmetaobject.getReflectiveMethods();
113 
114         // call _setMetaobject() after the metaobject is settled.
115         baseobject._setMetaobject(this);
116     }
117 
118     /**
119      * Returns the name of the method specified
120      * by <code>identifier</code>.
121      */
getMethodName(int identifier)122     public final String getMethodName(int identifier) {
123         String mname = methods[identifier].getName();
124         int j = ClassMetaobject.methodPrefixLen;
125         for (;;) {
126             char c = mname.charAt(j++);
127             if (c < '0' || '9' < c)
128                 break;
129         }
130 
131         return mname.substring(j);
132     }
133 
134     /**
135      * Returns an array of <code>Class</code> objects representing the
136      * formal parameter types of the method specified
137      * by <code>identifier</code>.
138      */
getParameterTypes(int identifier)139     public final Class[] getParameterTypes(int identifier) {
140         return methods[identifier].getParameterTypes();
141     }
142 
143     /**
144      * Returns a <code>Class</code> objects representing the
145      * return type of the method specified by <code>identifier</code>.
146      */
getReturnType(int identifier)147     public final Class getReturnType(int identifier) {
148         return methods[identifier].getReturnType();
149     }
150 
151     /**
152      * Is invoked when public fields of the base-level
153      * class are read and the runtime system intercepts it.
154      * This method simply returns the value of the field.
155      *
156      * <p>Every subclass of this class should redefine this method.
157      */
trapFieldRead(String name)158     public Object trapFieldRead(String name) {
159         Class jc = getClassMetaobject().getJavaClass();
160         try {
161             return jc.getField(name).get(getObject());
162         }
163         catch (NoSuchFieldException e) {
164             throw new RuntimeException(e.toString());
165         }
166         catch (IllegalAccessException e) {
167             throw new RuntimeException(e.toString());
168         }
169     }
170 
171     /**
172      * Is invoked when public fields of the base-level
173      * class are modified and the runtime system intercepts it.
174      * This method simply sets the field to the given value.
175      *
176      * <p>Every subclass of this class should redefine this method.
177      */
trapFieldWrite(String name, Object value)178     public void trapFieldWrite(String name, Object value) {
179         Class jc = getClassMetaobject().getJavaClass();
180         try {
181             jc.getField(name).set(getObject(), value);
182         }
183         catch (NoSuchFieldException e) {
184             throw new RuntimeException(e.toString());
185         }
186         catch (IllegalAccessException e) {
187             throw new RuntimeException(e.toString());
188         }
189     }
190 
191     /**
192      * Is invoked when base-level method invocation is intercepted.
193      * This method simply executes the intercepted method invocation
194      * with the original parameters and returns the resulting value.
195      *
196      * <p>Every subclass of this class should redefine this method.
197      *
198      * <p>Note: this method is not invoked if the base-level method
199      * is invoked by a constructor in the super class.  For example,
200      *
201      * <ul><pre>abstract class A {
202      *   abstract void initialize();
203      *   A() {
204      *       initialize();    // not intercepted
205      *   }
206      * }
207      *
208      * class B extends A {
209      *   void initialize() { System.out.println("initialize()"); }
210      *   B() {
211      *       super();
212      *       initialize();    // intercepted
213      *   }
214      * }</pre></ul>
215      *
216      * <p>if an instance of B is created,
217      * the invocation of initialize() in B is intercepted only once.
218      * The first invocation by the constructor in A is not intercepted.
219      * This is because the link between a base-level object and a
220      * metaobject is not created until the execution of a
221      * constructor of the super class finishes.
222      */
trapMethodcall(int identifier, Object[] args)223     public Object trapMethodcall(int identifier, Object[] args)
224         throws Throwable
225     {
226         try {
227             return methods[identifier].invoke(getObject(), args);
228         }
229         catch (java.lang.reflect.InvocationTargetException e) {
230             throw e.getTargetException();
231         }
232         catch (java.lang.IllegalAccessException e) {
233             throw new CannotInvokeException(e);
234         }
235     }
236 }
237