import java.lang.*;
import java.math.*;
import java.util.*;
import java.lang.reflect.*;

public class B<T>
{
	public List<T> dummy = new ArrayList<T>(1);

	public T dummy2() { return null; }

	public final Class<T> getClassType2()
	{
		{
			try
			{
				Method method = this.getClass().getMethod("dummy2");

				Class type = method.getReturnType();
				
				Class clz = method.getDeclaringClass();
				System.out.println("F:"+ method);
				System.out.println("T:"+ type);
				System.out.println("C:"+ clz);
				
				Type genericFieldType = method.getGenericReturnType();
				System.out.println("GT:"+ genericFieldType);
				System.out.println("GTC:"+ genericFieldType.getClass());

				if(genericFieldType instanceof ParameterizedType){
					ParameterizedType aType = (ParameterizedType) genericFieldType;
					System.out.println("AT:"+aType);

					Type[] atas = aType.getActualTypeArguments();
					Type   atot = aType.getOwnerType();
					Type   atrt = aType.getRawType();
					System.out.println("ATAS:"+atas);
					System.out.println("ATAS.L:"+atas.length);
					System.out.println("ATAS.0:"+atas[0]);
					System.out.println("ATOT:"+atot);
					System.out.println("ATRT:"+atrt);

					Type[] fieldArgTypes = aType.getActualTypeArguments();
					for(Type fieldArgType : fieldArgTypes){
						Class fieldArgClass = (Class) fieldArgType;
						System.out.println("fieldArgClass = " + fieldArgClass);
					}
				}
				else if(genericFieldType instanceof TypeVariable){
					TypeVariable vType = (TypeVariable) genericFieldType;
					System.out.println("1:" + vType);
					System.out.println("2:" + vType.getName());
					System.out.println("3:" + vType.getBounds());
					System.out.println("3a:" + vType.getBounds()[0]);

					Class  clz0 = (Class)vType.getBounds()[0];
					Object obj0 = clz0.newInstance();
					Class  clz1 = obj0.getClass();
					System.out.println(clz0);
					System.out.println(obj0);
					System.out.println(clz1);
					if (String.class.isAssignableFrom(clz0)) { System.out.println("SA0"); }
					if (String.class.isAssignableFrom(clz1)) { System.out.println("SA1"); }

					if (Integer.class.isAssignableFrom(clz0)) { System.out.println("SI0"); }
					if (Integer.class.isAssignableFrom(clz1)) { System.out.println("SI1"); }

					//BAD:  if (String.class.isAssignableFrom(Object.class)) { System.out.println("S=O"); }
					//GOOD: if (Object.class.isAssignableFrom(String.class)) { System.out.println("O=S"); }


					System.out.println("4:" + vType.getGenericDeclaration());
				}

			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return null;
	}

	public final Class<T> getClassType()
	{
		{
			try
			{
				Field field = this.getClass().getField("dummy");

				Type genericFieldType = field.getGenericType();
				Class type = field.getType();
				Class clz = field.getDeclaringClass();
				System.out.println("F:"+ field);
				System.out.println("T:"+ type);
				System.out.println("C:"+ clz);

				System.out.println("GT:"+ genericFieldType);
				System.out.println("GTC:"+ genericFieldType.getClass());

				if(genericFieldType instanceof ParameterizedType){
					ParameterizedType aType = (ParameterizedType) genericFieldType;

					System.out.println("AT:"+aType);

					Type[] atas = aType.getActualTypeArguments();
					Type   atot = aType.getOwnerType();
					Type   atrt = aType.getRawType();
					System.out.println("ATAS:"+atas);
					System.out.println("ATAS.L:"+atas.length);
					System.out.println("ATAS.0:"+atas[0]);
					System.out.println("ATOT:"+atot);
					System.out.println("ATRT:"+atrt);


					Type[] fieldArgTypes = aType.getActualTypeArguments();
					for(Type fieldArgType : fieldArgTypes){
						/*
						Class fieldArgClass = (Class) fieldArgType;
						System.out.println("fieldArgClass = " + fieldArgClass);
						*/
						System.out.println("fieldArgClass = " + fieldArgType);
						System.out.println("fieldArgClass = " + fieldArgType.getClass());

						TypeVariable vType = (TypeVariable) fieldArgType;
						System.out.println("1:" + vType);
						System.out.println("2:" + vType.getName());
						System.out.println("3:" + vType.getBounds());
						System.out.println("3L:" + vType.getBounds().length);
						System.out.println("3a:" + vType.getBounds()[0]);
						System.out.println("4:" + vType.getGenericDeclaration());
					}
				}
				else if(genericFieldType instanceof TypeVariable){
					TypeVariable vType = (TypeVariable) genericFieldType;
					System.out.println("1:" + vType);
					System.out.println("2:" + vType.getName());
					System.out.println("3:" + vType.getBounds());
					System.out.println("3a:" + vType.getBounds()[0]);
					System.out.println("4:" + vType.getGenericDeclaration());
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return null;
	}
}
