com.feilong.lib.javassist.tools.reflect.ClassMetaobject Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* 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 com.feilong.lib.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 com.feilong.lib.javassist.tools.reflect.Metalevel
*/
public class ClassMetaobject implements Serializable{
/** 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_"
.
*/
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.
*/
public static boolean useContextClassLoader = false;
/**
* Constructs a ClassMetaobject
.
*
* @param params
* params[0]
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.
*/
public final Class> getJavaClass(){
return javaClass;
}
/**
* Obtains the name of this class.
*/
public final String getName(){
return javaClass.getName();
}
/**
* Returns true if obj
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 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.
*/
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.
*/
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.
*
* @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.
*/
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 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_..
.
*
* @param identifier
* the identifier index
* given to trapMethodcall()
etc.
* @see #trapMethodcall(int,Object[])
*/
public final Method getMethod(int identifier){
return getReflectiveMethods()[identifier];
}
/**
* Returns the name of the method specified
* by identifier
.
*/
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
.
*/
public final Class>[] getParameterTypes(int identifier){
return getReflectiveMethods()[identifier].getParameterTypes();
}
/**
* Returns a Class
objects representing the
* return type of the method specified by identifier
.
*/
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
* {@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.
*
*
* 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");
}
}