/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.internal.gosu.parser.expressions.TypeVariableDefinition; import gw.lang.reflect.AbstractType; import gw.lang.reflect.FunctionType; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.ITypeVariableType; import gw.lang.parser.expressions.ITypeVariableDefinition; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.ITypeLoader; import gw.lang.reflect.Modifier; import java.io.ObjectStreamException; import java.util.Collections; import java.util.Set; /** */ public class TypeVariableType extends AbstractType implements ITypeVariableType { private ITypeVariableDefinition _typeVarDef; transient private TypeVariableArrayType _arrayType; private boolean _bFunctionStatement; public TypeVariableType( ITypeVariableDefinition typeVarDef, boolean forFunction ) { _typeVarDef = typeVarDef; _bFunctionStatement = forFunction; } public TypeVariableType( IType ownersType, IGenericTypeVariable typeVar ) { _typeVarDef = new TypeVariableDefinition( ownersType, typeVar ); } public ITypeVariableDefinition getTypeVarDef() { return _typeVarDef; } public String getName() { return _typeVarDef.getName(); } public String getDisplayName() { return getName(); } public String getRelativeName() { return getName(); } public String getNameWithEnclosingType() { String strEnclosingType = getEnclosingType().getName(); if( getEnclosingType() instanceof FunctionType ) { // Add id to distinguish between overloaded methods. We can't use the signature // because we parse the type vars before the signature. strEnclosingType += "." + System.identityHashCode( this ); } return strEnclosingType + '.' + getRelativeName(); } public String getNamespace() { return null; } public ITypeLoader getTypeLoader() { return null; } public IType getBoundingType() { return _typeVarDef.getTypeVar().getBoundingType(); } public boolean isInterface() { return getBoundingType().isInterface(); } public boolean isEnum() { return false; } public IType[] getInterfaces() { if( getBoundingType().isInterface() ) { return new IType[] { getBoundingType() }; } return EMPTY_TYPE_ARRAY; } public IType getSupertype() { if( !getBoundingType().isInterface() ) { return getBoundingType(); } return null; } public IType getEnclosingType() { return _typeVarDef.getEnclosingType(); } public IType getGenericType() { return null; } public boolean isFinal() { return true; } public boolean isParameterizedType() { return false; } public boolean isGenericType() { return false; } public GenericTypeVariable[] getGenericTypeVariables() { return null; } public IType getParameterizedType( IType... ofType ) { return null; } public IType[] getTypeParameters() { return null; } public Set<IType> getAllTypesInHierarchy() { //noinspection unchecked return (Set<IType>)(getBoundingType() == null ? Collections.emptySet() : getBoundingType().getAllTypesInHierarchy()); } public boolean isArray() { return false; } public boolean isPrimitive() { return false; } public IType getArrayType() { if( _arrayType == null ) { synchronized( this ) { if( _arrayType == null ) { // Note: we do the first assignment, then the second, so that the assignment to the instance variable // doesn't happen prior to the constructor completing, as the initial assignment might happen // before the constructor is done //noinspection UnnecessaryLocalVariable TypeVariableArrayType arrayType = new TypeVariableArrayType( this, null, getTypeLoader() ); _arrayType = arrayType; } } } return _arrayType; } public Object makeArrayInstance( int iLength ) { return getBoundingType().makeArrayInstance( iLength ); } public Object getArrayComponent( Object array, int iIndex ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException { throw new UnsupportedOperationException(); } public void setArrayComponent( Object array, int iIndex, Object value ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException { throw new UnsupportedOperationException(); } public int getArrayLength( Object array ) throws IllegalArgumentException { throw new UnsupportedOperationException(); } public IType getComponentType() { throw new UnsupportedOperationException(); } /** * A type variable type is assignable only to/from itself or a reference thereof. * <p> * For example: <pre> * function foo<T,S>( t: T, s: S ) * { * t = s // illegal, T and S may not have the same type at runtime * t = "hello" // illegal, T may not be a String * var x: T = t // ok, x and t are both of type T * } */ public boolean isAssignableFrom( IType type ) { if( type == this ) { return true; } if( !(type instanceof TypeVariableType) ) { return false; } return getRelativeName().equals( type.getRelativeName() ) && type.getEnclosingType() == getEnclosingType(); } /** * Ok, here's the deal. By all rights type variable types should have identity * equality. That's not the case right now for at least these two reasons: * <ul> * <li> We compile in multiple phases (header, decl, def). Each phase recompiles * the header, including type vars. Each time a new type var type is * created for the type var definition. It's not too hard to re-use the * prior type var type, but then there's this problem... * <li> If a subclass is generic and parameterizes its super with its type vars * the super parameterizd type retains whatever the type var type from the * first phase of the subclass' compilation. And, in studio, we don't * refresh the type system, just the type we're editing. So the super class * parameterized with the subclass' type vars and whatever else stemming * from there that refs the type vars remains. So the subclass can be edited * and refreshed a zillion time, but the super parameterized class will still * have the type var from way back. * </ul> * Probably the best way to handle this is to make TypeVariableType a loadable * type i.e., there will only ever be a single ref for a particular type var. * The challege is not so much with class type vars, but with function type * vars, since 1) we parse them before we parse the function's args we don't * quite have a fq name (yay! for overloading), and 2) function types * themselves are non-loadable and must remain that way as long as other types * are permitted to be non-loadable. Specifically, since a param type in a * function can be of any type, the fq name of the function may not be unique * i.e., the reason a type is non-loadable is usually because it can't have a * unique name. (yay! again for overloading) */ public boolean equals( Object obj ) { return obj instanceof IType && isAssignableFrom( (IType)obj ); } public int hashCode() { //!! This is here to prevent anyone from doing anything other than identity equality for TypeVariableType return super.hashCode(); } public boolean isMutable() { return false; } public ITypeInfo getTypeInfo() { return getBoundingType().getTypeInfo(); } public void unloadTypeInfo() { } public Object readResolve() throws ObjectStreamException { return this; } public boolean isValid() { return true; } public int getModifiers() { return Modifier.PUBLIC; } public boolean isAbstract() { return false; } public boolean isFunctionStatement() { return _bFunctionStatement; } public boolean isDiscarded() { return false; } public void setDiscarded( boolean bDiscarded ) { } public boolean isCompoundType() { return false; } public Set<IType> getCompoundTypeComponents() { return null; } @Override public String toString() { return "<" + getName() + ">"; } }