org.testifyproject.tools.reflect.ClassMetaobject Maven / Gradle / Ivy
/*
* 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 org.testifyproject.testifyprojectpliance 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 org.testifyproject.testifyproject.tools.reflect;
import java.lang.reflect.*;
import java.util.Arrays;
import java.org.testifyproject.testifyproject.Serializable;
import java.org.testifyproject.testifyproject.IOException;
import java.org.testifyproject.testifyproject.ObjectInputStream;
import java.org.testifyproject.testifyproject.ObjectOutputStream;
/**
* 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 org.testifyproject.testifyproject.tools.reflect.Metaobject
* @see org.testifyproject.testifyproject.tools.reflect.Metalevel
*/
public class ClassMetaobject implements Serializable {
/**
* 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 org.testifyproject.testifyprojectfault 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);
else
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 org.testifyproject.testifyprojectfined 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 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
.
*/
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
* ClassMetaobject#getMethod(), 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");
}
}