package typing;
import java.util.Set;
import java.util.TreeSet;
/**
* Represents a polymorphic type, which is basicly a
* monomorphic type with a set of type quantified type
* variables.
*
* @author Benedikt Meurer
* @version $Id$
*/
public final class PolyType extends Type {
/**
* Allocates a new polymorphic type with the given
* <code>quantifiedVariables</code> and the monomorphic
* type <code>monoType</code>.
*
* @param quantifiedVariables the set of quantified type variables
* for <code>monoType</code>.
* @param monoType the monomorphic type.
*/
public PolyType(Set<String> quantifiedVariables, MonoType monoType) {
this.quantifiedVariables = quantifiedVariables;
this.monoType = monoType;
}
/**
* {@inheritDoc}
*
* For polymorphic types, this returns <code>true</code>
* if the monomorphic type contains the <code>name</code>
* and <code>name</code> is not bound by this polymorphic
* type.
*
* @see typing.Type#containsFreeTypeVariable(java.lang.String)
*/
@Override
public boolean containsFreeTypeVariable(String name) {
// check if the monomorphic type contains a free
// type variable of the given name
if (this.monoType.containsFreeTypeVariable(name)) {
// check if the type variable is not bound by
// this polymorphic type
return !this.quantifiedVariables.contains(name);
}
else {
return false;
}
}
/**
* {@inheritDoc}
*
* For polymorphic types, this is the set of
* free type variables for the monomorphic
* type, minus the set of quantified names.
*
* @see typing.Type#free()
*/
@Override
public Set<String> free() {
// determine the set of free type variables
// for the monomorphic type
Set<String> freeMono = this.monoType.free();
if (freeMono == EMPTY_SET)
return EMPTY_SET;
// generate the set of free names not bound
// by the quantified names
TreeSet<String> free = new TreeSet<String>();
for (String name : freeMono)
if (!this.quantifiedVariables.contains(name))
free.add(name);
return free;
}
/**
* {@inheritDoc}
*
* Applies the substition <code>s</code> to both the
* monomorphic type and the type variables.
*
* @see typing.Type#substitute(typing.Substitution)
*/
@Override
PolyType substitute(Substitution s) {
// we don't need to do anything if s is empty
if (s == Substitution.EMPTY_SUBSTITUTION)
return this;
// determine the new set of quantified type variables
TreeSet<String> quantifiedVariables = new TreeSet<String>();
for (String name : this.quantifiedVariables) {
TypeVariable tvar = (TypeVariable)s.apply(new TypeVariable(name));
quantifiedVariables.add(tvar.getName());
}
// apply the substitution to the monomorphic type
MonoType monoType = this.monoType.substitute(s);
// check if anything changed
if (this.monoType != monoType || !this.quantifiedVariables.equals(quantifiedVariables))
return new PolyType(quantifiedVariables, monoType);
else
return this;
}
/**
* Instantes this polymorphic type with new type variables allocated
* from the specified <code>typeVariableAllocator</code>, and returns
* the newly generated monomorphic type.
*
* @param typeVariableAllocator the {@link TypeVariableAllocator} to use
* to generate new type variables.
*
* @return the newly instantiated {@link MonoType}.
*/
MonoType instantiate(TypeVariableAllocator typeVariableAllocator) {
// generate a substitution, which replaces all bound type variables
// with new type variables from the type variable allocator
Substitution s = Substitution.EMPTY_SUBSTITUTION;
for (String name : this.quantifiedVariables)
s = new Substitution(name, typeVariableAllocator.allocateTypeVariable(), s);
// instantiate the monomorphic type by applying the substitution
return this.monoType.substitute(s);
}
/**
* Returns the set of quantified type variables.
*
* @return the set of quantified variables.
*/
public Set<String> getQuantifiedVariables() {
return this.quantifiedVariables;
}
/**
* Returns <code>true</code> if <code>obj</code> is a
* polymorphic type (an instanceof {@link PolyType},
* which is equal to this polymorphic type.
*
* @param obj another object.
*
* @return <code>true</code> if equal.
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof PolyType) {
PolyType polyType = (PolyType)obj;
return (this.quantifiedVariables.equals(polyType.quantifiedVariables)
&& this.monoType.equals(polyType.monoType));
}
else {
return false;
}
}
/**
* Returns the string representation of the polymorphic
* type.
*
* @return the string representation of the polymorphic
* type.
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder(128);
if (!this.quantifiedVariables.isEmpty()) {
builder.append('\u2200');
for (String name : this.quantifiedVariables) {
if (builder.length() > 1)
builder.append(',');
builder.append(name);
}
builder.append('.');
}
builder.append(this.monoType);
return builder.toString();
}
private MonoType monoType;
private Set<String> quantifiedVariables;
}