/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.internal.gosu.parser.expressions.TypeVariableDefinition; import gw.internal.gosu.parser.expressions.TypeVariableDefinitionImpl; import gw.lang.parser.Keyword; import gw.lang.parser.TypeVarToTypeMap; import gw.lang.parser.expressions.ITypeVariableDefinition; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.java.IJavaClassType; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.lang.reflect.java.IJavaClassTypeVariable; import gw.lang.reflect.java.JavaTypes; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; /** */ public class GenericTypeVariable implements IGenericTypeVariable { public static final GenericTypeVariable[] EMPTY_TYPEVARS = new GenericTypeVariable[0]; private String _strName; private TypeVariableDefinitionImpl _typeVariableDefinition; private IType _boundingType; public GenericTypeVariable( String strName, IType boundingType ) { _strName = strName; _typeVariableDefinition = null; setBoundingType( boundingType ); if( boundingType == null ) { throw new IllegalArgumentException( "bounding type is null" ); } } public GenericTypeVariable( TypeVariableDefinitionImpl typeVariableDefinition, IType boundingType ) { _strName = typeVariableDefinition.getName(); _typeVariableDefinition = typeVariableDefinition; setBoundingType( boundingType ); if( boundingType == null ) { throw new IllegalArgumentException( "bounding type is null" ); } } public GenericTypeVariable( IType enclosingType, TypeVariable typeVar, TypeVarToTypeMap actualParamByVarName ) { _strName = typeVar.getName(); Type[] fromBounds = typeVar.getBounds(); IType[] boundingTypes = new IType[fromBounds.length]; IType myType = actualParamByVarName.getByString( _strName ); boolean bTemporaryMap = false; if( myType == null ) { // Need to map a Object to this tyepvar's for case where this typevar's // bounds references itself. Behold such an example exists e.g., // Collections: public static <T extends Comparable<? super T>> void sort(List<T> list). // Comparable<? super T> is the bounds of T itself. bTemporaryMap = true; if( actualParamByVarName.isEmpty() ) { actualParamByVarName = new TypeVarToTypeMap(); } actualParamByVarName.putByString( _strName, JavaTypes.OBJECT() ); } for( int j = 0; j < fromBounds.length; j++ ) { boundingTypes[j] = TypeLord.getActualType( fromBounds[j], actualParamByVarName ); if( boundingTypes[j] == null ) { throw new IllegalArgumentException( "bounding type [" + j + "] is null" ); } if( boundingTypes[j].isGenericType() && !boundingTypes[j].isParameterizedType() ) { boundingTypes[j] = TypeSystem.getDefaultParameterizedType( boundingTypes[j] ); } if( boundingTypes[j].isPrimitive() ) { boundingTypes[j] = TypeSystem.getBoxType( boundingTypes[j] ); } } setBoundingType( boundingTypes.length == 1 ? boundingTypes[0] : CompoundType.get( boundingTypes ) ); if( bTemporaryMap ) { actualParamByVarName.removeByString( _strName ); } if( enclosingType != null ) { _typeVariableDefinition = (TypeVariableDefinitionImpl) new TypeVariableDefinition( enclosingType, this ).getTypeVarDef(); } } public GenericTypeVariable( IType enclosingType, IJavaClassTypeVariable typeVar, TypeVarToTypeMap actualParamByVarName ) { _strName = typeVar.getName(); IJavaClassType[] fromBounds = typeVar.getBounds(); IType[] boundingTypes = new IType[fromBounds.length]; IType myType = actualParamByVarName.getByString( _strName ); boolean bTemporaryMap = false; if( myType == null ) { // Need to map a Object to this tyepvar's for case where this typevar's // bounds references itself. Behold such an example exists e.g., // Collections: public static <T extends Comparable<? super T>> void sort(List<T> list). // Comparable<? super T> is the bounds of T itself. bTemporaryMap = true; if( actualParamByVarName.isEmpty() ) { actualParamByVarName = new TypeVarToTypeMap(); } actualParamByVarName.putByString( _strName, JavaTypes.OBJECT() ); } for( int j = 0; j < fromBounds.length; j++ ) { if(fromBounds[j] != null) { boundingTypes[j] = fromBounds[j].getActualType( actualParamByVarName ); } else { boundingTypes[j] = TypeSystem.getErrorType(); } if( boundingTypes[j] == null ) { throw new IllegalArgumentException( "bounding type [" + j + "] is null" ); } if( boundingTypes[j].isGenericType() && !boundingTypes[j].isParameterizedType() ) { boundingTypes[j] = TypeSystem.getDefaultParameterizedType( boundingTypes[j] ); } if( boundingTypes[j].isPrimitive() ) { boundingTypes[j] = TypeSystem.getBoxType( boundingTypes[j] ); } } setBoundingType( boundingTypes.length == 1 ? boundingTypes[0] : CompoundType.get( boundingTypes ) ); if( bTemporaryMap ) { actualParamByVarName.removeByString( _strName ); } if( enclosingType != null ) { _typeVariableDefinition = (TypeVariableDefinitionImpl) new TypeVariableDefinition( enclosingType, this ).getTypeVarDef(); } } // Copy-constructor used for clone operations private GenericTypeVariable(String strName, TypeVariableDefinitionImpl typeVariableDefinition, IType boundingType) { _strName = strName; _typeVariableDefinition = typeVariableDefinition; _boundingType = boundingType; } public String getName() { return _strName; } public void setName( String strName ) { _strName = strName; } public String getNameWithBounds( boolean bRelative ) { return _boundingType == JavaTypes.OBJECT() ? getName() : (getName() + " " + Keyword.KW_extends + " " + (bRelative ? _boundingType.getRelativeName() : _boundingType.getName())); } public ITypeVariableDefinition getTypeVariableDefinition() { return _typeVariableDefinition; } public IType getBoundingType() { return _boundingType; } private void setBoundingType( IType type ) { if( type != null && type.isPrimitive() ) { // Never ever let a type var be bounded by a primitive. // Instead we autobox the type. type = TypeSystem.getBoxType( type ); } _boundingType = type; } public boolean equals( Object o ) { if( this == o ) { return true; } if( o == null || getClass() != o.getClass() ) { return false; } GenericTypeVariable that = (GenericTypeVariable)o; return _boundingType.equals( that._boundingType ) && _strName.equals( that._strName ); } public int hashCode() { return _strName.hashCode(); } public static GenericTypeVariable[] convertTypeVars( IType enclosingType, TypeVariable[] fromVars, TypeVarToTypeMap actualParamByVarName ) { TypeVarToTypeMap paramByVarNameIncludingMethod = new TypeVarToTypeMap( actualParamByVarName ); GenericTypeVariable[] toVars = new GenericTypeVariable[fromVars.length]; for( int i = 0; i < toVars.length; i++ ) { toVars[i] = new GenericTypeVariable( enclosingType, fromVars[i], paramByVarNameIncludingMethod ); paramByVarNameIncludingMethod.put( toVars[i].getTypeVariableDefinition().getType(), toVars[i].getTypeVariableDefinition().getType() ); } return toVars.length == 0 ? EMPTY_TYPEVARS : toVars; } public static GenericTypeVariable[] convertTypeVars( IType enclosingType, IJavaClassTypeVariable[] fromVars, TypeVarToTypeMap actualParamByVarName ) { TypeVarToTypeMap paramByVarNameIncludingMethod = new TypeVarToTypeMap( actualParamByVarName ); GenericTypeVariable[] toVars = new GenericTypeVariable[fromVars.length]; for( int i = 0; i < toVars.length; i++ ) { toVars[i] = new GenericTypeVariable( enclosingType, fromVars[i], paramByVarNameIncludingMethod ); paramByVarNameIncludingMethod.put( toVars[i].getTypeVariableDefinition().getType(), toVars[i].getTypeVariableDefinition().getType() ); } return toVars.length == 0 ? EMPTY_TYPEVARS : toVars; } public IGenericTypeVariable clone() { return new GenericTypeVariable(_strName, _typeVariableDefinition.clone(), _boundingType); } public void createTypeVariableDefinition(IType enclosingType) { _typeVariableDefinition = (TypeVariableDefinitionImpl) new TypeVariableDefinition( enclosingType, this ).getTypeVarDef(); } }