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