/*
* 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.lang.reflect.*;
import java.util.
Arrays;
import java.io.
Serializable;
import java.io.
IOException;
import java.io.
ObjectInputStream;
import java.io.
ObjectOutputStream;
/**
* 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 {
/**
* 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 <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 <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);
else
return
Class.
forName(
name);
}
/**
* Obtains the <code>java.lang.Class</code> representing this class.
*/
public final
Class getJavaClass() {
return
javaClass;
}
/**
* Obtains the name of this class.
*/
public final
String getName() {
return
javaClass.
getName();
}
/**
* 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.
*
* @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 <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 <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
* <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 <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.
*/
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 <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 <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 <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 <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.
*
* <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");
}
}