/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package javassist.tools.reflect;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

A runtime class metaobject.

A ClassMetaobject is created for every class of reflective objects. It can be used to hold values shared among the reflective objects of the same class.

To obtain a class metaobject, calls _getClass() on a reflective object. For example,

ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
See Also:
/** * A runtime class metaobject. * * <p>A <code>ClassMetaobject</code> is created for every * class of reflective objects. It can be used to hold values * shared among the reflective objects of the same class. * * <p>To obtain a class metaobject, calls <code>_getClass()</code> * on a reflective object. For example, * * <pre>ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass(); * </pre> * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.reflect.Metalevel */
public class ClassMetaobject implements Serializable {
default serialVersionUID
/** default serialVersionUID */
private static final long serialVersionUID = 1L;
The base-level methods controlled by a metaobject are renamed so that they begin with methodPrefix "_m_".
/** * The base-level methods controlled by a metaobject * are renamed so that they begin with * <code>methodPrefix "_m_"</code>. */
static final String methodPrefix = "_m_"; static final int methodPrefixLen = 3; private Class<?> javaClass; private Constructor<?>[] constructors; private Method[] methods;
Specifies how a java.lang.Class object is loaded.

If true, it is loaded by:

Thread.currentThread().getContextClassLoader().loadClass()

If false, it is loaded by Class.forName(). The default value is false.

/** * Specifies how a <code>java.lang.Class</code> object is loaded. * * <p>If true, it is loaded by: * <pre>Thread.currentThread().getContextClassLoader().loadClass()</pre> * <p>If false, it is loaded by <code>Class.forName()</code>. * The default value is false. */
public static boolean useContextClassLoader = false;
Constructs a ClassMetaobject.
Params:
  • params – params[0] is the name of the class of the reflective objects.
/** * Constructs a <code>ClassMetaobject</code>. * * @param params <code>params[0]</code> is the name of the class * of the reflective objects. */
public ClassMetaobject(String[] params) { try { javaClass = getClassObject(params[0]); } catch (ClassNotFoundException e) { throw new RuntimeException("not found: " + params[0] + ", useContextClassLoader: " + Boolean.toString(useContextClassLoader), e); } constructors = javaClass.getConstructors(); methods = null; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(javaClass.getName()); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { javaClass = getClassObject(in.readUTF()); constructors = javaClass.getConstructors(); methods = null; } private Class<?> getClassObject(String name) throws ClassNotFoundException { if (useContextClassLoader) return Thread.currentThread().getContextClassLoader() .loadClass(name); return Class.forName(name); }
Obtains the java.lang.Class representing this class.
/** * Obtains the <code>java.lang.Class</code> representing this class. */
public final Class<?> getJavaClass() { return javaClass; }
Obtains the name of this class.
/** * Obtains the name of this class. */
public final String getName() { return javaClass.getName(); }
Returns true if obj is an instance of this class.
/** * Returns true if <code>obj</code> is an instance of this class. */
public final boolean isInstance(Object obj) { return javaClass.isInstance(obj); }
Creates a new instance of the class.
Params:
  • args – the arguments passed to the constructor.
/** * Creates a new instance of the class. * * @param args the arguments passed to the constructor. */
public final Object newInstance(Object[] args) throws CannotCreateException { int n = constructors.length; for (int i = 0; i < n; ++i) { try { return constructors[i].newInstance(args); } catch (IllegalArgumentException e) { // try again } catch (InstantiationException e) { throw new CannotCreateException(e); } catch (IllegalAccessException e) { throw new CannotCreateException(e); } catch (InvocationTargetException e) { throw new CannotCreateException(e); } } throw new CannotCreateException("no constructor matches"); }
Is invoked when static fields of the base-level class are read and the runtime system intercepts it. This method simply returns the value of the field.

Every subclass of this class should redefine this method.

/** * Is invoked when <code>static</code> fields of the base-level * class are read and the runtime system intercepts it. * This method simply returns the value of the field. * * <p>Every subclass of this class should redefine this method. */
public Object trapFieldRead(String name) { Class<?> jc = getJavaClass(); try { return jc.getField(name).get(null); } catch (NoSuchFieldException e) { throw new RuntimeException(e.toString()); } catch (IllegalAccessException e) { throw new RuntimeException(e.toString()); } }
Is invoked when static fields of the base-level class are modified and the runtime system intercepts it. This method simply sets the field to the given value.

Every subclass of this class should redefine this method.

/** * Is invoked when <code>static</code> fields of the base-level * class are modified and the runtime system intercepts it. * This method simply sets the field to the given value. * * <p>Every subclass of this class should redefine this method. */
public void trapFieldWrite(String name, Object value) { Class<?> jc = getJavaClass(); try { jc.getField(name).set(null, value); } catch (NoSuchFieldException e) { throw new RuntimeException(e.toString()); } catch (IllegalAccessException e) { throw new RuntimeException(e.toString()); } }
Invokes a method whose name begins with methodPrefix "_m_" and the identifier.
Throws:
  • CannotInvokeException – if the invocation fails.
/** * Invokes a method whose name begins with * <code>methodPrefix "_m_"</code> and the identifier. * * @exception CannotInvokeException if the invocation fails. */
static public Object invoke(Object target, int identifier, Object[] args) throws Throwable { Method[] allmethods = target.getClass().getMethods(); int n = allmethods.length; String head = methodPrefix + identifier; for (int i = 0; i < n; ++i) if (allmethods[i].getName().startsWith(head)) { try { return allmethods[i].invoke(target, args); } catch (java.lang.reflect.InvocationTargetException e) { throw e.getTargetException(); } catch (java.lang.IllegalAccessException e) { throw new CannotInvokeException(e); } } throw new CannotInvokeException("cannot find a method"); }
Is invoked when static methods of the base-level class are called and the runtime system intercepts it. This method simply executes the intercepted method invocation with the original parameters and returns the resulting value.

Every subclass of this class should redefine this method.

/** * Is invoked when <code>static</code> methods of the base-level * class are called and the runtime system intercepts it. * This method simply executes the intercepted method invocation * with the original parameters and returns the resulting value. * * <p>Every subclass of this class should redefine this method. */
public Object trapMethodcall(int identifier, Object[] args) throws Throwable { try { Method[] m = getReflectiveMethods(); return m[identifier].invoke(null, args); } catch (java.lang.reflect.InvocationTargetException e) { throw e.getTargetException(); } catch (java.lang.IllegalAccessException e) { throw new CannotInvokeException(e); } }
Returns an array of the methods defined on the given reflective object. This method is for the internal use only.
/** * Returns an array of the methods defined on the given reflective * object. This method is for the internal use only. */
public final Method[] getReflectiveMethods() { if (methods != null) return methods; Class<?> baseclass = getJavaClass(); Method[] allmethods = baseclass.getDeclaredMethods(); int n = allmethods.length; int[] index = new int[n]; int max = 0; for (int i = 0; i < n; ++i) { Method m = allmethods[i]; String mname = m.getName(); if (mname.startsWith(methodPrefix)) { int k = 0; for (int j = methodPrefixLen;; ++j) { char c = mname.charAt(j); if ('0' <= c && c <= '9') k = k * 10 + c - '0'; else break; } index[i] = ++k; if (k > max) max = k; } } methods = new Method[max]; for (int i = 0; i < n; ++i) if (index[i] > 0) methods[index[i] - 1] = allmethods[i]; return methods; }
Returns the java.lang.reflect.Method object representing the method specified by identifier.

Note that the actual method returned will be have an altered, reflective name i.e. _m_2_...

Params:
  • identifier – the identifier index given to trapMethodcall() etc.
See Also:
/** * Returns the <code>java.lang.reflect.Method</code> object representing * the method specified by <code>identifier</code>. * * <p>Note that the actual method returned will be have an altered, * reflective name i.e. <code>_m_2_..</code>. * * @param identifier the identifier index * given to <code>trapMethodcall()</code> etc. * @see Metaobject#trapMethodcall(int,Object[]) * @see #trapMethodcall(int,Object[]) */
public final Method getMethod(int identifier) { return getReflectiveMethods()[identifier]; }
Returns the name of the method specified by identifier.
/** * Returns the name of the method specified * by <code>identifier</code>. */
public final String getMethodName(int identifier) { String mname = getReflectiveMethods()[identifier].getName(); int j = ClassMetaobject.methodPrefixLen; for (;;) { char c = mname.charAt(j++); if (c < '0' || '9' < c) break; } return mname.substring(j); }
Returns an array of Class objects representing the formal parameter types of the method specified by identifier.
/** * Returns an array of <code>Class</code> objects representing the * formal parameter types of the method specified * by <code>identifier</code>. */
public final Class<?>[] getParameterTypes(int identifier) { return getReflectiveMethods()[identifier].getParameterTypes(); }
Returns a Class objects representing the return type of the method specified by identifier.
/** * Returns a <code>Class</code> objects representing the * return type of the method specified by <code>identifier</code>. */
public final Class<?> getReturnType(int identifier) { return getReflectiveMethods()[identifier].getReturnType(); }
Returns the identifier index of the method, as identified by its original name.

This method is useful, in conjuction with getMethod(int), to obtain a quick reference to the original method in the reflected class (i.e. not the proxy method), using the original name of the method.

Written by Brett Randall and Shigeru Chiba.

Params:
  • originalName – The original name of the reflected method
  • argTypes – array of Class specifying the method signature
Throws:
See Also:
Returns: the identifier index of the original method
/** * Returns the identifier index of the method, as identified by its * original name. * * <p>This method is useful, in conjuction with * {@link ClassMetaobject#getMethod(int)}, to obtain a quick reference * to the original method in the reflected class (i.e. not the proxy * method), using the original name of the method. * * <p>Written by Brett Randall and Shigeru Chiba. * * @param originalName The original name of the reflected method * @param argTypes array of Class specifying the method signature * @return the identifier index of the original method * @throws NoSuchMethodException if the method does not exist * * @see ClassMetaobject#getMethod(int) */
public final int getMethodIndex(String originalName, Class<?>[] argTypes) throws NoSuchMethodException { Method[] mthds = getReflectiveMethods(); for (int i = 0; i < mthds.length; i++) { if (mthds[i] == null) continue; // check name and parameter types match if (getMethodName(i).equals(originalName) && Arrays.equals(argTypes, mthds[i].getParameterTypes())) return i; } throw new NoSuchMethodException("Method " + originalName + " not found"); } }