All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy