package org.checkerframework.framework.type.visitor;
import java.util.Iterator;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedUnionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.javacutil.ErrorReporter;
/**
* A TypeVisitor that takes two AnnotatedTypeMirrors as parameters, and visits them simultaneously.
* Both Annotated Type Mirrors must have the same structure or else the various asserts will fail.
*
* @see AnnotatedTypeScanner
*/
public abstract class AnnotatedTypeComparer<R>
extends AnnotatedTypeScanner<R, AnnotatedTypeMirror> {
/** Compares two annotated type mirrors. */
protected abstract R compare(AnnotatedTypeMirror type, AnnotatedTypeMirror p);
/** Supplies the logic to reduce on how to combine two R objects */
protected abstract R combineRs(R r1, R r2);
protected R scan(
Iterable<? extends AnnotatedTypeMirror> types,
Iterable<? extends AnnotatedTypeMirror> p) {
if (types == null) {
return null;
}
R r = null;
boolean first = true;
Iterator<? extends AnnotatedTypeMirror> tIter = types.iterator(), pIter = p.iterator();
while (tIter.hasNext() && pIter.hasNext()) {
r =
(first
? scan(tIter.next(), pIter.next())
: scanAndReduce(tIter.next(), pIter.next(), r));
first = false;
}
return r;
}
@Override
protected R reduce(R r1, R r2) {
return combineRs(r1, r2);
}
/**
* Run {@link #scan} on types and p, then run {@link #reduce} on the result (plus r) to return a
* single element.
*/
protected R scanAndReduce(
Iterable<? extends AnnotatedTypeMirror> types,
Iterable<? extends AnnotatedTypeMirror> p,
R r) {
return reduce(scan(types, p), r);
}
@Override
public R scanAndReduce(AnnotatedTypeMirror type, AnnotatedTypeMirror p, R r) {
return reduce(scan(type, p), r);
}
@Override
protected R scanAndReduce(
Iterable<? extends AnnotatedTypeMirror> types, AnnotatedTypeMirror p, R r) {
ErrorReporter.errorAbort(
"AnnotatedTypeComparer.scanAndReduce: "
+ p
+ "is not Iterable<? extends AnnotatedTypeMirror>");
return null;
}
@Override
protected R scan(AnnotatedTypeMirror type, AnnotatedTypeMirror p) {
return reduce(super.scan(type, p), compare(type, p));
}
@Override
public final R visitDeclared(AnnotatedDeclaredType type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedDeclaredType : p;
R r = scan(type.getTypeArguments(), ((AnnotatedDeclaredType) p).getTypeArguments());
return r;
}
@Override
public final R visitArray(AnnotatedArrayType type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedArrayType : p;
R r = scan(type.getComponentType(), ((AnnotatedArrayType) p).getComponentType());
return r;
}
@Override
public final R visitExecutable(AnnotatedExecutableType type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedExecutableType : p;
AnnotatedExecutableType ex = (AnnotatedExecutableType) p;
R r = scan(type.getReturnType(), ex.getReturnType());
r = scanAndReduce(type.getReceiverType(), ex.getReceiverType(), r);
r = scanAndReduce(type.getParameterTypes(), ex.getParameterTypes(), r);
r = scanAndReduce(type.getThrownTypes(), ex.getThrownTypes(), r);
r = scanAndReduce(type.getTypeVariables(), ex.getTypeVariables(), r);
return r;
}
@Override
public R visitTypeVariable(AnnotatedTypeVariable type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedTypeVariable : p;
R r;
if (visitedNodes.containsKey(type)) {
return visitedNodes.get(type);
}
visitedNodes.put(type, null);
if (p instanceof AnnotatedTypeVariable) {
AnnotatedTypeVariable tv = (AnnotatedTypeVariable) p;
r = scan(type.getLowerBound(), tv.getLowerBound());
visitedNodes.put(type, r);
r = scanAndReduce(type.getUpperBound(), tv.getUpperBound(), r);
visitedNodes.put(type, r);
} else {
r = scan(type.getLowerBound(), p.getErased());
visitedNodes.put(type, r);
r = scanAndReduce(type.getUpperBound(), p, r);
visitedNodes.put(type, r);
}
return r;
}
@Override
public R visitWildcard(AnnotatedWildcardType type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedWildcardType : p;
AnnotatedWildcardType w = (AnnotatedWildcardType) p;
if (visitedNodes.containsKey(type)) {
return visitedNodes.get(type);
}
visitedNodes.put(type, null);
R r = scan(type.getExtendsBound(), w.getExtendsBound());
visitedNodes.put(type, r);
r = scanAndReduce(type.getSuperBound(), w.getSuperBound(), r);
visitedNodes.put(type, r);
return r;
}
@Override
public R visitIntersection(AnnotatedIntersectionType type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedIntersectionType : p;
if (visitedNodes.containsKey(type)) {
return visitedNodes.get(type);
}
visitedNodes.put(type, null);
R r = scan(type.directSuperTypes(), ((AnnotatedIntersectionType) p).directSuperTypes());
return r;
}
@Override
public R visitUnion(AnnotatedUnionType type, AnnotatedTypeMirror p) {
assert p instanceof AnnotatedUnionType : p;
if (visitedNodes.containsKey(type)) {
return visitedNodes.get(type);
}
visitedNodes.put(type, null);
R r = scan(type.getAlternatives(), ((AnnotatedUnionType) p).getAlternatives());
return r;
}
}