TypeInfo based on projections instead of other TypeInfo's

This commit is contained in:
Alex Tkachman
2011-09-05 20:12:12 +02:00
parent 234eafb0c1
commit 93a422ae0a
9 changed files with 208 additions and 50 deletions
+96 -37
View File
@@ -1,6 +1,7 @@
package jet.typeinfo;
import jet.JetObject;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.Arrays;
@@ -8,60 +9,101 @@ import java.util.Arrays;
/**
* @author abreslav
* @author yole
* @author alex.tkachman
*/
public class TypeInfo<T> implements JetObject {
public abstract class TypeInfo<T> implements JetObject {
public static final TypeInfo<Byte> BYTE_TYPE_INFO = getTypeInfo(Byte.class, false);
public static final TypeInfo<Short> SHORT_TYPE_INFO = getTypeInfo(Short.class, false);
public static final TypeInfo<Integer> INT_TYPE_INFO = getTypeInfo(Integer.class, false);
public static final TypeInfo<Long> LONG_TYPE_INFO = getTypeInfo(Long.class, false);
public static final TypeInfo<Character> CHAR_TYPE_INFO = getTypeInfo(Character.class, false);
public static final TypeInfo<Boolean> BOOL_TYPE_INFO = getTypeInfo(Boolean.class, false);
public static final TypeInfo<Float> FLOAT_TYPE_INFO = getTypeInfo(Float.class, false);
public static final TypeInfo<Double> DOUBLE_TYPE_INFO = getTypeInfo(Double.class, false);
private TypeInfo<?> typeInfo;
private final Class<T> theClass;
private final boolean nullable;
private final TypeInfo[] typeParameters;
private final TypeInfoProjection[] projections;
public TypeInfo(Class<T> theClass, boolean nullable) {
private TypeInfo(Class<T> theClass, boolean nullable) {
this.theClass = theClass;
this.nullable = nullable;
this.typeParameters = null;
this.projections = null;
}
public TypeInfo(Class<T> theClass, boolean nullable, TypeInfo[] typeParameters) {
private TypeInfo(Class<T> theClass, boolean nullable, TypeInfoProjection[] projections) {
this.theClass = theClass;
this.nullable = nullable;
this.typeParameters = typeParameters;
this.projections = projections;
}
public Object getClassObject() {
public static <T> TypeInfoProjection invariantProjection(final TypeInfo<T> typeInfo) {
return (TypeInfoImpl) typeInfo;
}
public static <T> TypeInfoProjection inProjection(TypeInfo<T> typeInfo) {
return new TypeInfoProjection.TypeInfoProjectionImpl(typeInfo) {
@NotNull
@Override
public TypeInfoVariance getVariance() {
return TypeInfoVariance.IN_VARIANCE;
}
};
}
public static <T> TypeInfoProjection outProjection(TypeInfo<T> typeInfo) {
return new TypeInfoProjection.TypeInfoProjectionImpl(typeInfo) {
@NotNull
@Override
public TypeInfoVariance getVariance() {
return TypeInfoVariance.OUT_VARIANCE;
}
};
}
public static <T> TypeInfo<T> getTypeInfo(Class<T> klazz, boolean nullable) {
return new TypeInfoImpl<T>(klazz, nullable);
}
public static <T> TypeInfo<T> getTypeInfo(Class<T> klazz, boolean nullable, TypeInfoProjection[] projections) {
return new TypeInfoImpl<T>(klazz, nullable, projections);
}
public final Object getClassObject() {
try {
final Class implClass = theClass.getClassLoader().loadClass(theClass.getCanonicalName() + "$$Impl");
final Field classobj = implClass.getField("$classobj");
return classobj.get(null);
}
catch(Exception e) {
} catch (Exception e) {
return null;
}
}
public boolean isInstance(Object obj) {
public final boolean isInstance(Object obj) {
if (obj instanceof JetObject) {
return ((JetObject) obj).getTypeInfo().isSubtypeOf(this);
}
if(obj == null)
if (obj == null)
return nullable;
return theClass.isAssignableFrom(obj.getClass()); // TODO
}
public boolean isSubtypeOf(TypeInfo<?> superType) {
public final boolean isSubtypeOf(TypeInfo<?> superType) {
if (!superType.theClass.isAssignableFrom(theClass)) {
return false;
}
if (nullable && !superType.nullable) {
return false;
}
if (typeParameters != null) {
if (superType.typeParameters == null || superType.typeParameters.length != typeParameters.length) {
if (projections != null) {
if (superType.projections == null || superType.projections.length != projections.length) {
throw new IllegalArgumentException("inconsistent type infos for the same class");
}
for (int i = 0; i < typeParameters.length; i++) {
for (int i = 0; i < projections.length; i++) {
// TODO handle variance here
if (!typeParameters [i].equals(superType.typeParameters [i])) {
if (!projections[i].equals(superType.projections[i])) {
return false;
}
}
@@ -69,12 +111,16 @@ public class TypeInfo<T> implements JetObject {
return true;
}
public TypeInfo getTypeParameter(int index) {
return typeParameters[index];
public final TypeInfoProjection getProjection(int index) {
return projections[index];
}
public final TypeInfo getArgumentType(int index) {
return projections[index].getType();
}
@Override
public TypeInfo<?> getTypeInfo() {
public final TypeInfo<?> getTypeInfo() {
if (typeInfo == null) {
// TODO: Implementation must be lazy, otherwise the result would be of an infinite size
throw new UnsupportedOperationException(); // TODO
@@ -83,7 +129,7 @@ public class TypeInfo<T> implements JetObject {
}
@Override
public boolean equals(Object o) {
public final boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -91,37 +137,50 @@ public class TypeInfo<T> implements JetObject {
if (!theClass.equals(typeInfo.theClass)) return false;
if (nullable != typeInfo.nullable) return false;
if (!Arrays.equals(typeParameters, typeInfo.typeParameters)) return false;
if (!Arrays.equals(projections, typeInfo.projections)) return false;
return true;
}
@Override
public int hashCode() {
return 31 * theClass.hashCode() + (typeParameters != null ? Arrays.hashCode(typeParameters) : 0);
public final int hashCode() {
return 31 * theClass.hashCode() + (projections != null ? Arrays.hashCode(projections) : 0);
}
@Override
public String toString() {
public final String toString() {
StringBuilder sb = new StringBuilder().append(theClass.getName());
if(typeParameters != null && typeParameters.length != 0) {
if (projections != null && projections.length != 0) {
sb.append("<");
for(int i = 0; i != typeParameters.length-1; ++i) {
sb.append(typeParameters[i].toString()).append(",");
for (int i = 0; i != projections.length - 1; ++i) {
sb.append(projections[i].toString()).append(",");
}
sb.append(typeParameters[typeParameters.length - 1].toString()).append(">");
sb.append(projections[projections.length - 1].toString()).append(">");
}
if(nullable)
if (nullable)
sb.append("?");
return sb.toString();
}
public static final TypeInfo<Byte> BYTE_TYPE_INFO = new TypeInfo<Byte>(Byte.class, false);
public static final TypeInfo<Short> SHORT_TYPE_INFO = new TypeInfo<Short>(Short.class, false);
public static final TypeInfo<Integer> INT_TYPE_INFO = new TypeInfo<Integer>(Integer.class, false);
public static final TypeInfo<Long> LONG_TYPE_INFO = new TypeInfo<Long>(Long.class, false);
public static final TypeInfo<Character> CHAR_TYPE_INFO = new TypeInfo<Character>(Character.class, false);
public static final TypeInfo<Boolean> BOOL_TYPE_INFO = new TypeInfo<Boolean>(Boolean.class, false);
public static final TypeInfo<Float> FLOAT_TYPE_INFO = new TypeInfo<Float>(Float.class, false);
public static final TypeInfo<Double> DOUBLE_TYPE_INFO = new TypeInfo<Double>(Double.class, false);
private static class TypeInfoImpl<T> extends TypeInfo<T> implements TypeInfoProjection {
TypeInfoImpl(Class<T> klazz, boolean nullable) {
super(klazz, nullable);
}
TypeInfoImpl(Class<T> klazz, boolean nullable, TypeInfoProjection[] projections) {
super(klazz, nullable, projections);
}
@NotNull
@Override
public TypeInfoVariance getVariance() {
return TypeInfoVariance.INVARIANT;
}
@NotNull
@Override
public TypeInfo getType() {
return this;
}
}
}
@@ -0,0 +1,50 @@
package jet.typeinfo;
import org.jetbrains.annotations.NotNull;
/**
* @author alex.tkachman
*/
public interface TypeInfoProjection {
@NotNull
TypeInfoVariance getVariance();
@NotNull
TypeInfo getType();
abstract class TypeInfoProjectionImpl implements TypeInfoProjection {
@NotNull private final TypeInfo type;
TypeInfoProjectionImpl(@NotNull TypeInfo typeInfo) {
this.type = typeInfo;
}
@Override
@NotNull
public final TypeInfo getType() {
return type;
}
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TypeInfoProjectionImpl that = (TypeInfoProjectionImpl) o;
// no need to compare variance as we compared classes already
return type.equals(that.type);
}
@Override
public final int hashCode() {
int result = type.hashCode();
result = 31 * result + (getVariance().hashCode());
return result;
}
@Override
public final String toString() {
return (getVariance() == TypeInfoVariance.INVARIANT ? "" : getVariance().toString() + " ") + type;
}
}
}
@@ -0,0 +1,21 @@
package jet.typeinfo;
/**
* @author alex.tkachman
*/
public enum TypeInfoVariance {
INVARIANT("") ,
IN_VARIANCE("in"),
OUT_VARIANCE("out");
private final String label;
TypeInfoVariance(String label) {
this.label = label;
}
@Override
public String toString() {
return label;
}
}