blob: 1c03064a81588bcd77e6b81290c9014d5c583b6b [file] [log] [blame]
package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext;
import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
/**
* A type S is compatible in a loose invocation context with type T
*
* @author Federico Tomassetti
*/
public class TypeCompatibleWithType extends ConstraintFormula {
private ResolvedType s;
private ResolvedType t;
private TypeSolver typeSolver;
public TypeCompatibleWithType(TypeSolver typeSolver, ResolvedType s, ResolvedType t) {
this.typeSolver = typeSolver;
this.s = s;
this.t = t;
}
@Override
public ReductionResult reduce(BoundSet currentBoundSet) {
// A constraint formula of the form ‹S → T› is reduced as follows:
//
// 1. If S and T are proper types, the constraint reduces to true if S is compatible in a loose invocation context with T (§5.3), and false otherwise.
if (isProperType(s) && isProperType(t)) {
if (isCompatibleInALooseInvocationContext(s, t)) {
return ReductionResult.trueResult();
} else {
return ReductionResult.falseResult();
}
}
// 2. Otherwise, if S is a primitive type, let S' be the result of applying boxing conversion (§5.1.7) to S. Then the constraint reduces to ‹S' → T›.
if (s.isPrimitive()) {
ReflectionTypeSolver typeSolver = new ReflectionTypeSolver();
ResolvedType sFirst = new ReferenceTypeImpl(typeSolver.solveType(s.asPrimitive().getBoxTypeQName()), typeSolver);
return ReductionResult.oneConstraint(new TypeCompatibleWithType(typeSolver, sFirst, t));
}
// 3. Otherwise, if T is a primitive type, let T' be the result of applying boxing conversion (§5.1.7) to T. Then the constraint reduces to ‹S = T'›.
if (t.isPrimitive()) {
ReflectionTypeSolver typeSolver = new ReflectionTypeSolver();
ResolvedType tFirst = new ReferenceTypeImpl(typeSolver.solveType(t.asPrimitive().getBoxTypeQName()), typeSolver);
return ReductionResult.oneConstraint(new TypeSameAsType(s, tFirst));
}
// The fourth and fifth cases are implicit uses of unchecked conversion (§5.1.9). These, along with any use of
// unchecked conversion in the first case, may result in compile-time unchecked warnings, and may influence a
// method's invocation type (§15.12.2.6).
// 4. Otherwise, if T is a parameterized type of the form G<T1, ..., Tn>, and there exists no type of the
// form G<...> that is a supertype of S, but the raw type G is a supertype of S, then the constraint reduces
// to true.
if (t.isReferenceType() && !t.asReferenceType().getTypeDeclaration().getTypeParameters().isEmpty()) {
// FIXME I really cannot understand what the specification means...
// there exists a type of the form G<...> that is a supertype of S?
boolean condition1 = t.isAssignableBy(s);
// the raw type G is a supertype of S
ResolvedType G = t.asReferenceType().toRawType();
boolean condition2 = G.isAssignableBy(s);
if (!condition1 && condition2) {
return ReductionResult.trueResult();
}
//throw new UnsupportedOperationException();
}
// 5. Otherwise, if T is an array type of the form G<T1, ..., Tn>[]k, and there exists no type of the form
// G<...>[]k that is a supertype of S, but the raw type G[]k is a supertype of S, then the constraint
// reduces to true. (The notation []k indicates an array type of k dimensions.)
if (t.isArray()) {
throw new UnsupportedOperationException();
}
// 6. Otherwise, the constraint reduces to ‹S <: T›
return ReductionResult.empty().withConstraint(new TypeSubtypeOfType(typeSolver, s, t));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TypeCompatibleWithType that = (TypeCompatibleWithType) o;
if (!s.equals(that.s)) return false;
return t.equals(that.t);
}
@Override
public int hashCode() {
int result = s.hashCode();
result = 31 * result + t.hashCode();
return result;
}
@Override
public String toString() {
return "TypeCompatibleWithType{" +
"s=" + s +
", t=" + t +
'}';
}
}