/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.lang;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.SignatureAttribute;
import org.jboss.javassist.JavassistUtil;
import org.jboss.lang.reflect.GenericArrayTypeImpl;
import org.jboss.lang.reflect.ParameterizedTypeImpl;
import org.jboss.lang.reflect.Type;
import org.jboss.lang.reflect.TypeVariable;
import org.jboss.lang.reflect.TypeVariableImpl;
import org.jboss.lang.reflect.WildcardTypeImpl;
/**
*
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
* @version $Revision: 1.1 $
*/
public class GenericsHelper
{
final static Type[] NO_TYPES = new Type[0];
final static TypeVariable[] NO_TYPE_VARIABLES = new TypeVariable[0];
public static TypeVariable[] getTypeParameters(Class clazz)
{
SignatureAttribute.ClassSignature sig = getClassSignature(clazz);
if (sig != null)
{
SignatureAttribute.TypeParameter[] params = sig.getParameters();
TypeVariable[] tparams = new TypeVariable[params.length];
for (int i = 0 ; i < params.length ; i++)
{
Object [] bounds = null;
try {
bounds = getBounds(params[i].getClassBound(), params[i].getInterfaceBound());
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
tparams[i] = new TypeVariableImpl(bounds, clazz, params[i].getName());
}
return tparams;
}
return NO_TYPE_VARIABLES;
}
/**
* Convert the Type parameter bounds into appropriate Class objects.
* @param classBound
* @param interfaceBounds
* @return
* @throws ClassNotFoundException
*/
private static Object [] getBounds(SignatureAttribute.ObjectType classBound, SignatureAttribute.ObjectType[] interfaceBounds) throws ClassNotFoundException {
Object [] bounds = null;
// If there is no super type specified, default to object
if (classBound == null && (interfaceBounds == null || interfaceBounds.length == 0))
{
bounds = new Object[]{Object.class};
}
// If there are no interfaces, use only the class bound
else if (interfaceBounds == null || interfaceBounds.length == 0) {
Class superClass = Class.forName(classBound.toString());
bounds = new Object[]{superClass};
}
// If there is no superclass, use the interfaces
else if (classBound == null) {
bounds = new Object[interfaceBounds.length];
for (int i=0; i<interfaceBounds.length; ++i) {
bounds[i] = Class.forName(interfaceBounds[i].toString());
}
}
// Use both the superclass and interfaces
else {
bounds = new Object[interfaceBounds.length + 1];
bounds[0] = Class.forName(classBound.toString());
for (int i=0; i<interfaceBounds.length; ++i) {
bounds[i+1] = Class.forName(interfaceBounds[i].toString());
}
}
return bounds;
}
public static Object[] getGenericInterfaces(Class clazz)
{
try
{
SignatureAttribute.ClassSignature sig = getClassSignature(clazz);
if (sig != null)
{
SignatureAttribute.ClassType[] ifs = sig.getInterfaces();
Object[] inters = new Object[ifs.length];
for (int i = 0 ; i < ifs.length ; i++)
{
inters[i] = createType(Class.forName(ifs[i].getName()), ifs[i]);
}
return inters;
}
return clazz.getInterfaces();
}
catch (ClassNotFoundException e)
{
// AutoGenerated
throw new RuntimeException(e);
}
}
public static Object getGenericSuperclass(Class clazz)
{
if (!clazz.isInterface())
{
SignatureAttribute.ClassSignature sig = getClassSignature(clazz);
if (sig != null)
{
SignatureAttribute.ClassType supa = sig.getSuperClass();
if (supa != null)
{
return createType(clazz.getSuperclass(), supa);
}
}
}
return clazz.getSuperclass();
}
public static TypeVariable[] getTypeParameters(Constructor ctor)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(ctor);
if (methodSig != null)
{
SignatureAttribute.TypeParameter[] params = methodSig.getTypeParameters();
TypeVariable[] tparams = new TypeVariable[params.length];
for (int i = 0 ; i < params.length ; i++)
{
Object [] bounds = null;
try {
bounds = getBounds(params[i].getClassBound(), params[i].getInterfaceBound());
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
tparams[i] = new TypeVariableImpl(bounds, params[i].getClass(), params[i].getName());
}
return tparams;
}
return NO_TYPE_VARIABLES;
}
public static Object[] getGenericParameterTypes(Constructor c)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(c);
return getGenericParameterTypes(c.getDeclaringClass(), c.getParameterTypes(), methodSig);
}
public static Object[] getGenericExceptionTypes(Constructor c)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(c);
return getGenericExceptionTypes(c.getExceptionTypes(), methodSig);
}
public static String toGenericString(Constructor c)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(c);
JBossStringBuilder buf = new JBossStringBuilder();
appendModifiers(buf, c);
appendTypeParameters(buf, getTypeParameters(c));
buf.append(c.getDeclaringClass().getName());
appendGenericParameterTypes(buf, getGenericParameterTypes(c.getDeclaringClass(), c.getParameterTypes(), methodSig));
if (c.getExceptionTypes().length > 0)
{
buf.append(" throws ");
appendGenericExceptionTypes(buf, getGenericExceptionTypes(c.getExceptionTypes(), methodSig));
}
return buf.toString();
}
public static TypeVariable[] getTypeParameters(Method meth)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(meth);
if (methodSig != null)
{
SignatureAttribute.TypeParameter[] params = methodSig.getTypeParameters();
TypeVariable[] tparams = new TypeVariable[params.length];
for (int i = 0 ; i < params.length ; i++)
{
Object [] bounds = null;
try {
bounds = getBounds(params[i].getClassBound(), params[i].getInterfaceBound());
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
tparams[i] = new TypeVariableImpl(bounds, params[i].getClass(), params[i].getName());
}
return tparams;
}
return NO_TYPE_VARIABLES;
}
public static Object getGenericReturnType(Method m)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(m);
return getGenericReturnType(m, methodSig);
}
public static Object[] getGenericParameterTypes(Method m)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(m);
return getGenericParameterTypes(m.getDeclaringClass(), m.getParameterTypes(), methodSig);
}
public static Object[] getGenericExceptionTypes(Method m)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(m);
return getGenericExceptionTypes(m.getExceptionTypes(), methodSig);
}
public static String toGenericString(Method m)
{
SignatureAttribute.MethodSignature methodSig = getMethodSignature(m);
JBossStringBuilder buf = new JBossStringBuilder();
appendModifiers(buf, m);
if (m.getTypeParameters().length > 0) {
appendTypeParameters(buf, getTypeParameters(m));
}
buf.append(formatTypeName(getGenericReturnType(m, methodSig)));
buf.append(" ");
buf.append(m.getDeclaringClass().getName());
buf.append(".");
buf.append(m.getName());
appendGenericParameterTypes(buf, getGenericParameterTypes(m.getDeclaringClass(), m.getParameterTypes(), methodSig));
if (m.getExceptionTypes().length > 0)
{
buf.append(" throws ");
appendGenericExceptionTypes(buf, getGenericExceptionTypes(m.getExceptionTypes(), methodSig));
}
return buf.toString();
}
public static Object getGenericType(Field f)
{
CtField fld = JavassistUtil.getCtField(f);
FieldInfo info = fld.getFieldInfo2();
SignatureAttribute sig = (SignatureAttribute)info.getAttribute(SignatureAttribute.tag);
if (sig != null)
{
try
{
SignatureAttribute.ObjectType type = SignatureAttribute.toFieldSignature(sig.getSignature());
return createType(f.getType(), type);
}
catch (BadBytecode e)
{
throw new RuntimeException(e);
}
}
return f.getType();
}
public static String toGenericString(Field f)
{
JBossStringBuilder buf = new JBossStringBuilder();
appendModifiers(buf, f);
Object type = getGenericType(f);
buf.append(formatTypeName(type));
buf.append(" ");
buf.append(f.getDeclaringClass().getName());
buf.append(".");
buf.append(f.getName());
return buf.toString();
}
public static Object[] getTypeParameters(Object o)
{
if (o instanceof Class)
{
return getTypeParameters((Class)o);
}
else if (o instanceof Method)
{
return getTypeParameters((Method)o);
}
else if (o instanceof Constructor)
{
return getTypeParameters((Constructor)o);
}
if (o == null)
{
throw new IllegalArgumentException("Null object passed in");
}
throw new IllegalArgumentException("Object of type " + o.getClass().getName() + " does not implement GenericDeclaration");
}
private static String formatTypeName(Object type)
{
if (type instanceof Class)
{
return ((Class)type).getName();
}
else
{
return type.toString();
}
}
private static void appendModifiers(JBossStringBuilder buf, Member m)
{
int length = buf.length();
buf.append(Modifier.toString(m.getModifiers()));
if (buf.length() > length)
{
buf.append(" ");
}
}
private static void appendTypeParameters(JBossStringBuilder buf, TypeVariable[] tvs)
{
if (tvs.length == 0)
{
return;
}
buf.append("<");
for (int i = 0 ; i < tvs.length ; i++)
{
if (i > 0)
{
buf.append(",");
}
buf.append(tvs[i]);
}
buf.append("> ");
}
private static void appendGenericParameterTypes(JBossStringBuilder buf, Object[] params)
{
buf.append("(");
for (int i = 0 ; i < params.length ; i++)
{
if (i > 0)
{
buf.append(", ");
}
buf.append(formatTypeName(params[i]));
}
buf.append(")");
}
private static void appendGenericExceptionTypes(JBossStringBuilder buf, Object[] exceptions)
{
for (int i = 0 ; i < exceptions.length ; i++)
{
if (i > 0)
{
buf.append(", ");
}
buf.append(exceptions[i].toString());
}
}
/**
* Create the type impl object.
* @param targetClass
* @param type
* @return an object representing the type
* TODO: this method should probably be re-organized to make it a bit simpler.
*/
private static Object createType(Class targetClass, SignatureAttribute.Type type)
{
try
{
if (type instanceof SignatureAttribute.ClassType)
{
SignatureAttribute.TypeArgument[] args = ((SignatureAttribute.ClassType)type).getTypeArguments();
if (args == null)
{
return targetClass;
}
else
{
Object ownerType = null;
Object[] typeArgs = NO_TYPES;
if (type instanceof SignatureAttribute.NestedClassType)
{
SignatureAttribute.NestedClassType nestedType = (SignatureAttribute.NestedClassType)type;
ownerType = createType(targetClass.getDeclaringClass(), nestedType.getDeclaringClass()) ;
}
if (args.length > 0)
{
typeArgs = new Object[args.length];
for (int i = 0; i < args.length; i++)
{
if (args[i].getType() instanceof SignatureAttribute.NestedClassType)
{
SignatureAttribute.NestedClassType nestedType = (SignatureAttribute.NestedClassType) args[i].getType();
Class nestedClass = Class.forName(nestedType.getDeclaringClass().getName() + "$" + nestedType.getName());
typeArgs[i] = createType(nestedClass, args[i].getType());
}
else if (args[i].isWildcard())
{
Object bound = null;
if (args[i].getType() != null)
{
if (args[i].getType() instanceof SignatureAttribute.ClassType)
{
bound = Class.forName(((SignatureAttribute.ClassType) args[i].getType()).getName());
}
else if (args[i].getType() instanceof SignatureAttribute.TypeVariable)
{
SignatureAttribute.TypeVariable tv = (SignatureAttribute.TypeVariable) args[i].getType();
Object[] bounds = new Object[] {Object.class};
bound = new TypeVariableImpl(bounds, targetClass, tv.getName());
}
}
typeArgs[i] = new WildcardTypeImpl(bound, args[i].getKind());
}
else if (args[i].getType() instanceof SignatureAttribute.ClassType)
{
typeArgs[i] = Class.forName(((SignatureAttribute.ClassType) args[i].getType()).getName());
}
else if (args[i].getType() instanceof SignatureAttribute.TypeVariable)
{
SignatureAttribute.TypeVariable tv = (SignatureAttribute.TypeVariable) args[i].getType();
Object[] bounds = new Object[] {Object.class};
typeArgs[i] = new TypeVariableImpl(bounds, targetClass, tv.getName());
}
else
{
throw new RuntimeException("Invalid argument type " + args[i].getType());
}
}
}
return new ParameterizedTypeImpl(type.toString(), targetClass, ownerType, typeArgs);
}
}
else if (type instanceof SignatureAttribute.TypeVariable)
{
SignatureAttribute.TypeVariable tv = (SignatureAttribute.TypeVariable)type;
Object [] bounds = new Object[]{Object.class};
return new TypeVariableImpl(bounds, targetClass, tv.getName());
}
else if (type instanceof SignatureAttribute.ArrayType)
{
SignatureAttribute.ArrayType arrayType = (SignatureAttribute.ArrayType)type;
return new GenericArrayTypeImpl(arrayType.getComponentType(), arrayType.getDimension(), arrayType.toString());
}
else if (type instanceof SignatureAttribute.BaseType)
{
SignatureAttribute.BaseType baseType = (SignatureAttribute.BaseType)type;
return getPrimitiveType(baseType.getDescriptor());
}
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
throw new RuntimeException("Unknown type " + type.getClass());
}
private static Class getPrimitiveType(char typeDescriptor) {
if (typeDescriptor == 'V')
{
return void.class;
}
else if (typeDescriptor == 'I')
{
return int.class;
}
else if (typeDescriptor == 'B')
{
return byte.class;
}
else if (typeDescriptor == 'J')
{
return long.class;
}
else if (typeDescriptor == 'D')
{
return double.class;
}
else if (typeDescriptor == 'F')
{
return float.class;
}
else if (typeDescriptor == 'C')
{
return char.class;
}
else if (typeDescriptor == 'S')
{
return short.class;
}
else if (typeDescriptor == 'Z')
{
return boolean.class;
}
throw new RuntimeException("bad descriptor: " + typeDescriptor);
}
private static Class getRawClass(SignatureAttribute.ClassType type)
{
try
{
String name = ((SignatureAttribute.ClassType)type).getName();
return Class.forName(name);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
private static SignatureAttribute.ClassSignature getClassSignature(Class clazz)
{
CtClass ctclazz = JavassistUtil.getCtClass(clazz);
ClassFile cf = ctclazz.getClassFile2();
SignatureAttribute sig = (SignatureAttribute)cf.getAttribute(SignatureAttribute.tag);
if (sig != null)
{
try
{
SignatureAttribute.ClassSignature type = SignatureAttribute.toClassSignature(sig.getSignature());
return type;
}
catch (BadBytecode e)
{
throw new RuntimeException(e);
}
}
return null;
}
private static SignatureAttribute.MethodSignature getMethodSignature(Method m)
{
CtMethod mtd = JavassistUtil.getCtMethod(m);
MethodInfo info = mtd.getMethodInfo2();
return getMethodSignature(info);
}
private static SignatureAttribute.MethodSignature getMethodSignature(Constructor c)
{
CtConstructor con = JavassistUtil.getCtConstructor(c);
MethodInfo info = con.getMethodInfo2();
return getMethodSignature(info);
}
private static SignatureAttribute.MethodSignature getMethodSignature(MethodInfo info)
{
SignatureAttribute sig = (SignatureAttribute)info.getAttribute(SignatureAttribute.tag);
if (sig != null)
{
try
{
SignatureAttribute.MethodSignature type = SignatureAttribute.toMethodSignature(sig.getSignature());
return type;
}
catch (BadBytecode e)
{
throw new RuntimeException(e);
}
}
return null;
}
private static Object getGenericReturnType(Method m, SignatureAttribute.MethodSignature methodSig)
{
if (methodSig != null)
{
return createType(m.getReturnType(), methodSig.getReturnType());
}
else
{
return m.getReturnType();
}
}
private static Object[] getGenericParameterTypes(Class targetClass, Class[] parameterTypes, SignatureAttribute.MethodSignature methodSig)
{
if (methodSig != null)
{
SignatureAttribute.Type[] types = methodSig.getParameterTypes();
Object[] paramTypes = new Object[types.length];
for (int i = 0 ; i < types.length ; i++)
{
paramTypes[i] = createType(parameterTypes[i], types[i]);
}
return paramTypes;
}
return parameterTypes;
}
private static Object[] getGenericExceptionTypes(Class[] exceptionTypes, SignatureAttribute.MethodSignature methodSig)
{
if (methodSig != null) {
SignatureAttribute.Type[] types = methodSig.getExceptionTypes();
Object[] exTypes = new Object[types.length];
for (int i = 0 ; i < types.length ; i++)
{
// Here it is assumed that exceptionTypes and types are in the same order
exTypes[i] = createType(exceptionTypes[i], types[i]);
}
return exTypes;
}
return exceptionTypes;
}
}