org.jboss.reflect.plugins.javassist.JavassistParameterizedClassInfo Maven / Gradle / Ivy
The newest version!
/*
* 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.reflect.plugins.javassist;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;
import javassist.bytecode.SignatureAttribute.ClassSignature;
import javassist.bytecode.SignatureAttribute.TypeArgument;
import org.jboss.reflect.plugins.ClassInfoImpl;
import org.jboss.reflect.plugins.TypeVariableAware;
import org.jboss.reflect.spi.ClassInfo;
import org.jboss.reflect.spi.ConstructorInfo;
import org.jboss.reflect.spi.DelegateClassInfo;
import org.jboss.reflect.spi.FieldInfo;
import org.jboss.reflect.spi.MethodInfo;
import org.jboss.reflect.spi.TypeInfo;
import org.jboss.reflect.spi.TypeInfoFactory;
import org.jboss.util.JBossStringBuilder;
/**
* Delegate class info to handle generic parameterized types in javassist
*
* @author Kabir Khan
* @version $Revision: 1.1 $
*/
public class JavassistParameterizedClassInfo extends DelegateClassInfo implements TypeVariableAware, JavassistClassInfo
{
/** The serialVersionUID */
private static final long serialVersionUID = 2;
/** The factory */
private final JavassistTypeInfoFactoryImpl factory;
/** The type infos for the type arguments. They get lazily loaded */
private volatile TypeInfo[] typeArgumentInfos = ClassInfoImpl.UNKNOWN_TYPES;
/** The factory to be used to lazily load up the type infos for the type arguments */
private final LazyTypeArgumentFactory lazyTypeArgumentFactory;
private volatile String typeVariable;
/**
* Constructor
*
* @param factory the javassist type info factory
* @param delegate the class info containing the parameterized type's raw type
* @param classLoader the class loader to use when lazily loading up the arguments
* @param typeArguments the javassist generic type arguments
* @param spy used to determine the actual bounds of generic type variables
* @throws IllegalArgumentException if any of the parameters are null
*/
public JavassistParameterizedClassInfo(JavassistTypeInfoFactoryImpl factory, ClassInfo delegate, ClassLoader classLoader, TypeArgument[] typeArguments, JavassistTypeVariableSpy spy)
{
this(factory, delegate, classLoader, typeArguments, spy, null);
}
/**
* Constructor
*
* @param factory the javassist type info factory
* @param delegate the class info containing the parameterized type's raw type
* @param reflectTypeArguments the type infos for the type arguments
* @throws IllegalArgumentException if any of the parameters are null
*/
public JavassistParameterizedClassInfo(JavassistTypeInfoFactoryImpl factory, ClassInfo delegate, Type[] reflectTypeArguments)
{
this(factory, delegate, null, null, null, reflectTypeArguments);
}
private JavassistParameterizedClassInfo(JavassistTypeInfoFactoryImpl factory, ClassInfo delegate, ClassLoader classLoader, TypeArgument[] typeArguments, JavassistTypeVariableSpy spy, Type[] reflectTypeArguments)
{
super(delegate);
if (factory == null)
throw new IllegalArgumentException("null factory");
this.factory = factory;
if (reflectTypeArguments != null)
lazyTypeArgumentFactory = new FromReflectTypeArgumentFactory(reflectTypeArguments);
else
lazyTypeArgumentFactory = new FromClassFileTypeArgumentFactory(typeArguments, spy, classLoader);
}
public void setTypeVariable(String typeVariable)
{
this.typeVariable = typeVariable;
}
public String getTypeVariable()
{
return typeVariable;
}
@Override
public TypeInfoFactory getTypeInfoFactory()
{
return factory;
}
@Override
public ClassLoader getClassLoader()
{
return lazyTypeArgumentFactory.getClassLoader();
}
@Override
public TypeInfo[] getActualTypeArguments()
{
if (typeArgumentInfos == ClassInfoImpl.UNKNOWN_TYPES)
{
typeArgumentInfos = lazyTypeArgumentFactory.createTypeInfos();
}
return typeArgumentInfos;
}
@Override
public TypeInfo getComponentType()
{
return findTypeInfo(JavassistTypeInfo.COLLECTION, 0, CollectionTypeChecker.INSTANCE);
}
@Override
public TypeInfo getKeyType()
{
return findTypeInfo(JavassistTypeInfo.MAP, 0, MapTypeChecker.INSTANCE);
}
@Override
public TypeInfo getValueType()
{
return findTypeInfo(JavassistTypeInfo.MAP, 1, MapTypeChecker.INSTANCE);
}
private TypeInfo findTypeInfo(ClassInfo target, int parameter, TypeChecker checker)
{
ClassSignature sig = getClassSignature();
if (sig == null)
return delegate.getComponentType();
if (!checker.check(this))
return null;
try
{
return JavassistHelper.determineInfoIndex(getActualTypeArguments(), this, target, parameter);
}
catch (Exception e1)
{
throw new RuntimeException(e1);
}
}
@Override
public void toShortString(JBossStringBuilder buffer)
{
appendTypeGenericInfo(this, buffer, null);
}
private void appendTypeGenericInfo(TypeInfo info, JBossStringBuilder buffer, Set doneTypeVariables)
{
boolean first = true;
ClassInfo cinfo = null;
if (info instanceof ClassInfo)
{
cinfo = (ClassInfo)info;
if (cinfo.getTypeVariable() != null)
{
if (doneTypeVariables == null)
doneTypeVariables = new HashSet();
else if (doneTypeVariables.contains(cinfo.getTypeVariable()))
return;
doneTypeVariables.add(cinfo.getTypeVariable());
}
}
buffer.append(info.getName());
if (cinfo != null)
{
if (cinfo.getActualTypeArguments().length > 0)
{
JBossStringBuilder params = new JBossStringBuilder();
params.append("<");
for (TypeInfo arg : cinfo.getActualTypeArguments())
{
if (!first)
params.append(", ");
else
first = false;
appendTypeGenericInfo(arg, params, doneTypeVariables);
}
if (params.length() > 0)
{
params.append(">");
buffer.append(params.toString());
}
}
}
}
@Override
protected void toString(JBossStringBuilder buffer)
{
toShortString(buffer);
}
private static interface TypeChecker
{
boolean check(JavassistParameterizedClassInfo info);
}
private static class MapTypeChecker implements TypeChecker
{
final static MapTypeChecker INSTANCE = new MapTypeChecker();
public boolean check(JavassistParameterizedClassInfo info)
{
return info.isMap();
}
}
private static class CollectionTypeChecker implements TypeChecker
{
final static CollectionTypeChecker INSTANCE = new CollectionTypeChecker();
public boolean check(JavassistParameterizedClassInfo info)
{
return info.isCollection();
}
}
private interface LazyTypeArgumentFactory
{
TypeInfo[] createTypeInfos();
ClassLoader getClassLoader();
}
/**
* Used to lazily create type arguments for the parameterized type if it
* has been accessed with {@link TypeInfoFactory#getTypeInfo(Type)} and the
* parameter is of type {@link ParameterizedType}
*
*/
private class FromReflectTypeArgumentFactory implements LazyTypeArgumentFactory
{
/** The generic type argument type infos */
private final Type[] typeArguments;
FromReflectTypeArgumentFactory(Type[] typeArguments)
{
this.typeArguments = typeArguments;
}
public TypeInfo[] createTypeInfos()
{
TypeInfo[] infos = new TypeInfo[typeArguments.length];
for (int i = 0 ; i < typeArguments.length ; i++)
{
try
{
infos[i] = factory.getTypeInfo(typeArguments[i]);
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
return infos;
}
public ClassLoader getClassLoader()
{
return delegate.getClassLoader();
}
}
/**
* Used to lazily create type arguments for the parameterized type if the parameterized
* type is loaded up as a result of calling
* {@link ClassInfo#getGenericSuperclass()},
* {@link ClassInfo#getGenericInterfaces()},
* {@link MethodInfo#getReturnType()},
* {@link MethodInfo#getParameterTypes()},
* {@link ConstructorInfo#getParameterTypes()} or
* {@link FieldInfo#getType()}
*
* in which case we end up in {@link JavassistTypeInfoFactoryImpl#getParameterizedType(ParameterizedType)}
*
*/
private class FromClassFileTypeArgumentFactory implements LazyTypeArgumentFactory
{
/** The generic type arguments from the javassist class file */
private final TypeArgument[] typeArguments;
/** Utility to determine the actual bounds of generic type variables */
private final JavassistTypeVariableSpy spy;
/** The classloader used to create this parameterized type, it is used to load up the generic info */
private final ClassLoader classLoader;
public FromClassFileTypeArgumentFactory(TypeArgument[] typeArguments, JavassistTypeVariableSpy spy, ClassLoader classLoader)
{
this.typeArguments = typeArguments;
this.spy = spy;
this.classLoader = classLoader;
}
public TypeInfo[] createTypeInfos()
{
if (typeArguments != null && typeArguments.length > 0)
{
if (delegate instanceof JavassistTypeInfo == false)
throw new IllegalStateException("Delegate is not a javassist one");
TypeInfo[] infos = new TypeInfo[typeArguments.length];
for (int i = 0 ; i < typeArguments.length ; i++)
{
infos[i] = factory.createTypeInfoForTypeArgument(typeArguments[i], classLoader, spy);
}
typeArgumentInfos = infos;
}
return typeArgumentInfos;
}
public ClassLoader getClassLoader()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(JavassistTypeInfo.GET_CLASSLOADER_PERMISSION);
return classLoader;
}
}
public ClassSignature getClassSignature()
{
return ((JavassistTypeInfo)delegate).getClassSignature();
}
public ClassLoader getClassLoaderInternal()
{
return ((JavassistClassInfo)delegate).getClassLoaderInternal();
}
public JavassistTypeInfoFactoryImpl getFactory()
{
return factory;
}
}