package org.glassfish.pfl.dynamic.codegen.spi;
import java.util.Collections ;
import java.util.Map ;
import java.util.Set ;
import java.util.HashMap ;
import java.util.WeakHashMap ;
import java.lang.reflect.Modifier ;
import org.glassfish.pfl.dynamic.copyobject.spi.Immutable ;
import org.glassfish.pfl.dynamic.codegen.impl.Identifier ;
import org.glassfish.pfl.dynamic.codegen.impl.ClassInfoReflectiveImpl ;
import org.glassfish.pfl.dynamic.codegen.impl.CurrentClassLoader ;
import org.glassfish.pfl.basic.contain.Pair;
@Immutable
public class Type {
enum Sort { PRIMITIVE, ARRAY, CLASS } ;
private String name ;
private String packageName ;
private String className ;
private String signature ;
private int size ;
private Sort sort ;
private boolean isNumber ;
private int wideningNumber ;
private Type memberType ;
private ClassInfo classInfo ;
private Class<?> typeClass ;
private Type( String name, String signature, int size, boolean isNumber,
Sort sort, int wideningNumber, Type memberType )
{
this.name = name ;
Pair<String,String> parts = Identifier.splitFQN( name ) ;
this.packageName = parts.first() ;
this.className = parts.second() ;
this.signature = signature ;
this.size = size ;
this.isNumber = isNumber ;
this.sort = sort ;
this.wideningNumber = wideningNumber ;
this.memberType = memberType ;
this.classInfo = null ;
}
private Type( String name, String signature, int size, boolean isNumber,
Sort sort, int wideningNumber ) {
this( name, signature, size, isNumber, sort, wideningNumber, null ) ;
}
private static ThreadLocal<Map<Class,Type>> classMap =
new ThreadLocal<Map<Class,Type>>() {
@Override
public Map<Class,Type> initialValue() {
return new WeakHashMap<Class,Type>() ;
}
} ;
private static ThreadLocal<Map<String,Type>> classNameMap =
new ThreadLocal<Map<String,Type>>() {
@Override
public Map<String,Type> initialValue() {
return new WeakHashMap<String,Type>() ;
}
} ;
private static Map<Class,Type> ptcToType =
new HashMap<Class,Type>() ;
public static final void clearCaches() {
classMap.get().clear() ;
classNameMap.get().clear() ;
}
private static final Type myVoid = new Type( "void", "V", 0, false, Sort.PRIMITIVE, -1 ) ;
private static final Type myNull = new Type( "NULL", "N", 1, false, Sort.PRIMITIVE, -1 ) ;
private static final Type myBoolean = new Type( "boolean", "Z", 1, false, Sort.PRIMITIVE, -1 ) ;
private static final Type myByte = new Type( "byte", "B", 1, true, Sort.PRIMITIVE, 1 ) ;
private static final Type myChar = new Type( "char", "C", 1, true, Sort.PRIMITIVE, 2 ) ;
private static final Type myShort = new Type( "short", "S", 1, true, Sort.PRIMITIVE, 2 ) ;
private static final Type myInt = new Type( "int", "I", 1, true, Sort.PRIMITIVE, 3 ) ;
private static final Type myLong = new Type( "long", "J", 2, true, Sort.PRIMITIVE, 4 ) ;
private static final Type myFloat = new Type( "float", "F", 1, true, Sort.PRIMITIVE, 5 ) ;
private static final Type myDouble = new Type( "double", "D", 2, true, Sort.PRIMITIVE, 6 ) ;
static {
ptcToType.put( boolean.class, myBoolean ) ;
ptcToType.put( byte.class, myByte ) ;
ptcToType.put( char.class, myChar ) ;
ptcToType.put( short.class, myShort ) ;
ptcToType.put( int.class, myInt ) ;
ptcToType.put( long.class, myLong ) ;
ptcToType.put( float.class, myFloat ) ;
ptcToType.put( double.class, myDouble ) ;
ptcToType.put( void.class, myVoid ) ;
ptcToType = Collections.unmodifiableMap( ptcToType ) ;
}
private static final Type myObject = _class( "java.lang.Object" ) ;
private static final Type myString = _class( "java.lang.String" ) ;
private static final Type myClass = _class( "java.lang.Class" ) ;
private static final Type myCloneable = _class( "java.lang.Cloneable" ) ;
public static Type _array( Type memberType ) {
String name = memberType.name() + "[]" ;
Type result = classNameMap.get().get( name ) ;
if (result == null) {
result = new Type( name, "[" + memberType.signature,
1, false, Sort.ARRAY, -1, memberType ) ;
classNameMap.get().put( name, result ) ;
}
return result ;
}
public static Type _class( String name ) {
Type result = classNameMap.get().get( name ) ;
if (result == null) {
result = new Type( name, "L" + name.replace( '.', '/' ) + ";",
1, false, Sort.CLASS, -1 ) ;
classNameMap.get().put( name, result ) ;
}
return result ;
}
public static Type _classGenerator( ClassGenerator cg ) {
Type result = _class( cg.name() ) ;
result.classInfo = cg ;
return result ;
}
private static boolean classIsStandard( Class cls ) {
String name = cls.getName() ;
return name.startsWith("java.") ||
name.startsWith("javax.") ;
}
public static synchronized Type type( Class cls ) {
if (cls.isPrimitive()) {
Type type = ptcToType.get( cls ) ;
assert type != null ;
return type ;
}
Type result = classMap.get().get( cls ) ;
if (result == null) {
if (cls.isArray()) {
result = _array(type(cls.getComponentType())) ;
} else {
result = _class(cls.getName()) ;
}
result.typeClass = cls ;
classMap.get().put( cls, result ) ;
if (classIsStandard(cls)) {
classNameMap.get().put( cls.getName(), result ) ;
}
}
return result ;
}
public static Type _void() {
return myVoid ;
}
public static Type _null() {
return myNull ;
}
public static Type _boolean() {
return myBoolean ;
}
public static Type _byte() {
return myByte ;
}
public static Type _char() {
return myChar ;
}
public static Type _short() {
return myShort ;
}
public static Type _int() {
return myInt ;
}
public static Type _long() {
return myLong ;
}
public static Type _float() {
return myFloat ;
}
public static Type _double() {
return myDouble ;
}
public static Type _Object() {
return myObject ;
}
public static Type _String() {
return myString ;
}
public static Type _Class() {
return myClass ;
}
public static Type _Cloneable() {
return myCloneable ;
}
public boolean isPrimitive() {
return sort == Sort.PRIMITIVE ;
}
public boolean isArray() {
return sort == Sort.ARRAY ;
}
public Type memberType() {
if (isArray())
return memberType ;
else
throw new IllegalStateException( "memberType() only valid for Array types" ) ;
}
public int size() {
return this.size ;
}
public String signature() {
return this.signature ;
}
public String name() {
return this.name ;
}
public String packageName() {
return packageName ;
}
public String className() {
return className ;
}
public boolean isNumber() {
return this.isNumber ;
}
public Class<?> getTypeClass() {
if (typeClass == null) {
try {
typeClass = Class.forName( name, true,
CurrentClassLoader.get() ) ;
} catch (ClassNotFoundException cnfe) {
IllegalArgumentException exc =
new IllegalArgumentException(
"Cannot load class for type " + name ) ;
exc.initCause( cnfe ) ;
throw exc ;
}
classMap.get().put( typeClass, this ) ;
}
return typeClass ;
}
public ClassInfo classInfo() {
if (classInfo == null) {
if (isArray())
throw new IllegalStateException(
"Cannot get ClassInfo for array type " + name ) ;
if (isPrimitive())
throw new IllegalStateException(
"Cannot get ClassInfo for primitive type " + name ) ;
classInfo = new ClassInfoReflectiveImpl( this ) ;
}
return classInfo ;
}
@Override
public int hashCode() {
return name.hashCode() ;
}
@Override
public String toString() {
return "Type[" + name + "," + signature + "," + size + "," + sort
+ "]" ;
}
@Override
public boolean equals( Object obj ) {
if (!(obj instanceof Type))
return false ;
if (obj == this)
return true ;
Type other = Type.class.cast( obj ) ;
return signature.equals( other.signature ) ;
}
public boolean hasPrimitiveNarrowingConversionFrom( Type t ) {
if (isPrimitive()) {
if (!t.isPrimitive())
return false ;
if ((wideningNumber < 0) || (t.wideningNumber < 0))
return false ;
if (t == myByte)
return this == myChar ;
if (t == this)
return false ;
return t.wideningNumber >= wideningNumber ;
}
return false ;
}
public boolean hasPrimitiveWideningConversionFrom( Type t ) {
if (isPrimitive()) {
if (!t.isPrimitive())
return false ;
if ((wideningNumber < 0) || (t.wideningNumber < 0))
return false ;
if ((t == myByte) && (this == myChar))
return false ;
return t.wideningNumber < wideningNumber ;
}
return false ;
}
private boolean returnTypeCollision( Set<MethodInfo> set1,
Set<MethodInfo> set2 ) {
for (MethodInfo mi1 : set1) {
for (MethodInfo mi2 : set2) {
if (mi1.signature().equals( mi2.signature() ))
if (!mi1.returnType().equals( mi2.returnType() ))
return true ;
}
}
return false ;
}
private boolean noMethodConflicts( Type t1, Type t2 ) {
ClassInfo c1 = null ;
ClassInfo c2 = null ;
c1 = t1.classInfo() ;
c2 = t2.classInfo() ;
for (String name : c1.methodInfoByName().keySet()) {
if (c2.methodInfoByName().containsKey( name )) {
Set<MethodInfo> set1 = c1.methodInfoByName().get( name ) ;
Set<MethodInfo> set2 = c2.methodInfoByName().get( name ) ;
if (returnTypeCollision( set1, set2 ))
return false ;
}
}
return true ;
}
private boolean isSubclass( Type t ) {
return classInfo().isSubclass( t.classInfo() ) ;
}
private boolean isInterface() {
if (isArray() || isPrimitive())
return false ;
return classInfo().isInterface() ;
}
private int modifiers() {
return classInfo().modifiers() ;
}
public boolean hasReferenceNarrowingConversionFrom( Type t ) {
if (isPrimitive() || t.isPrimitive())
return false ;
if (t.equals( _Object())) {
return true ;
}
if (t.isInterface()) {
if (isArray()) {
return false ;
} else {
if (!isInterface()) {
if (!Modifier.isFinal( modifiers())) {
return true ;
} else if (isSubclass( t )) {
return true ;
}
} else {
if (!t.isSubclass( this )
&& noMethodConflicts( t, this ))
return true ;
}
}
} else if (t.isArray()) {
if (isArray()) {
return memberType().hasReferenceNarrowingConversionFrom(
t.memberType ) ;
} else {
return false ;
}
} else {
if (isArray())
return false ;
if (isSubclass(t)) {
return true ;
}
if (!isInterface() &&
!Modifier.isFinal(t.modifiers()) &&
!t.isSubclass(this)) {
return true ;
}
}
return false ;
}
public boolean hasReferenceWideningConversionFrom( Type t ) {
if (isPrimitive() || t.isPrimitive())
return false ;
if (this.equals( _Object() ))
return true ;
if (t.equals( _null() ))
return true ;
if (t.isArray()) {
if (this.equals( myCloneable ))
return true ;
if (isArray() && !memberType().isPrimitive())
return memberType().isMethodInvocationConvertibleFrom(
t.memberType() ) ;
else
return false ;
}
if (isArray())
return false ;
return t.isSubclass( this ) ;
}
public boolean isAssignmentConvertibleFrom( Type t ) {
if (equals( t ))
return true ;
if (hasPrimitiveWideningConversionFrom( t ))
return true ;
if (hasReferenceWideningConversionFrom( t ))
return true ;
return false ;
}
public boolean isCastingConvertibleFrom( Type t ) {
if (equals( t ))
return true ;
if (hasPrimitiveWideningConversionFrom( t ))
return true ;
if (hasReferenceWideningConversionFrom( t ))
return true ;
if (hasPrimitiveNarrowingConversionFrom( t ))
return true ;
if (hasReferenceNarrowingConversionFrom( t ))
return true ;
return false ;
}
public Type unaryPromotion() {
if (!isNumber())
throw new IllegalArgumentException(
"Only number types have unary promotions" ) ;
if (equals( _byte() ))
return _int() ;
if (equals( _short() ))
return _int() ;
if (equals( _char() ))
return _int() ;
return this ;
}
public Type binaryPromotion( Type t ) {
if (!isNumber() || !t.isNumber())
throw new IllegalArgumentException(
"Only number types have binary promotions" ) ;
if (equals(_double()) || t.equals(_double()))
return _double() ;
if (equals(_float()) || t.equals(_float()))
return _float() ;
if (equals(_long()) || t.equals(_long()))
return _long() ;
return _int() ;
}
public boolean isMethodInvocationConvertibleFrom( Type t ) {
if (t == null)
throw new NullPointerException() ;
if (this.equals(t))
return true ;
if (isPrimitive()) {
return hasPrimitiveWideningConversionFrom( t ) ;
}
return hasReferenceWideningConversionFrom( t ) ;
}
}