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.Method;
19import java.io.Serializable;
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import 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 */
45public 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     */
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     */
70    protected Metaobject() {
71        baseobject = null;
72        classmetaobject = null;
73        methods = null;
74    }
75
76    private void writeObject(ObjectOutputStream out) throws IOException {
77        out.writeObject(baseobject);
78    }
79
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     */
93    public final ClassMetaobject getClassMetaobject() {
94        return classmetaobject;
95    }
96
97    /**
98     * Obtains the object controlled by this metaobject.
99     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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