| package org.checkerframework.framework.type; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.lang.model.element.AnnotationMirror; |
| import org.checkerframework.javacutil.Pair; |
| |
| /** |
| * THIS CLASS IS DESIGNED FOR USE WITH DefaultTypeHierarchy, DefaultRawnessComparer, and |
| * StructuralEqualityComparer ONLY. |
| * |
| * <p>VisitHistory tracks triples of (type1, type2, top), where type1 is a subtype of type2. It does |
| * not track when type1 is not a subtype of type2; such entries are missing from the history. |
| * Clients of this class can check whether or not they have visited an equivalent pair of |
| * AnnotatedTypeMirrors already. This is necessary in order to halt visiting on recursive bounds. |
| * |
| * <p>This class is primarily used to implement isSubtype(ATM, ATM). The pair of types corresponds |
| * to the subtype and the supertype being checked. A single subtype may be visited more than once, |
| * but with a different supertype. For example, if the two types are {@code @A T extends @B |
| * Serializable<T>} and {@code @C Serializable<?>}, then isSubtype is first called one those types |
| * and then on {@code @B Serializable<T>} and {@code @C Serializable<?>}. |
| */ |
| // TODO: do we need to clear the history sometimes? |
| public class SubtypeVisitHistory { |
| |
| /** |
| * The keys are pairs of types; the value is the set of qualifier hierarchy roots for which the |
| * key is in a subtype relationship. |
| */ |
| private final Map<Pair<AnnotatedTypeMirror, AnnotatedTypeMirror>, Set<AnnotationMirror>> visited; |
| |
| public SubtypeVisitHistory() { |
| this.visited = new HashMap<>(); |
| } |
| |
| /** |
| * Put a visit for {@code type1}, {@code type2}, and {@code top} in the history. Has no effect if |
| * isSubtype is false. |
| * |
| * @param type1 the first type |
| * @param type2 the second type |
| * @param currentTop the top of the relevant type hierarchy; only annotations from that hierarchy |
| * are considered |
| * @param isSubtype whether {@code type1} is a subtype of {@code type2}; if false, this method |
| * does nothing |
| */ |
| public void put( |
| final AnnotatedTypeMirror type1, |
| final AnnotatedTypeMirror type2, |
| AnnotationMirror currentTop, |
| boolean isSubtype) { |
| if (!isSubtype) { |
| // Only store information about subtype relations that hold. |
| return; |
| } |
| Pair<AnnotatedTypeMirror, AnnotatedTypeMirror> key = Pair.of(type1, type2); |
| Set<AnnotationMirror> hit = visited.get(key); |
| |
| if (hit != null) { |
| hit.add(currentTop); |
| } else { |
| hit = new HashSet<>(); |
| hit.add(currentTop); |
| this.visited.put(key, hit); |
| } |
| } |
| |
| /** Remove {@code type1} and {@code type2}. */ |
| public void remove( |
| final AnnotatedTypeMirror type1, |
| final AnnotatedTypeMirror type2, |
| AnnotationMirror currentTop) { |
| Pair<AnnotatedTypeMirror, AnnotatedTypeMirror> key = Pair.of(type1, type2); |
| Set<AnnotationMirror> hit = visited.get(key); |
| if (hit != null) { |
| hit.remove(currentTop); |
| if (hit.isEmpty()) { |
| visited.remove(key); |
| } |
| } |
| } |
| |
| /** |
| * Returns true if type1 and type2 (or an equivalent pair) have been passed to the put method |
| * previously. |
| * |
| * @return true if an equivalent pair has already been added to the history |
| */ |
| public boolean contains( |
| final AnnotatedTypeMirror type1, |
| final AnnotatedTypeMirror type2, |
| AnnotationMirror currentTop) { |
| Pair<AnnotatedTypeMirror, AnnotatedTypeMirror> key = Pair.of(type1, type2); |
| Set<AnnotationMirror> hit = visited.get(key); |
| return hit != null && hit.contains(currentTop); |
| } |
| |
| @Override |
| public String toString() { |
| return "VisitHistory( " + visited + " )"; |
| } |
| } |