package org.checkerframework.framework.util; /*>>> import org.checkerframework.checker.interning.qual.*; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.PolyNull; */ import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.framework.qual.PolymorphicQualifier; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.ErrorReporter; /** * Represents the type qualifier hierarchy of a type system that supports multiple separate subtype * hierarchies. * * <p>This class is immutable and can be only created through {@link MultiGraphFactory}. */ public class MultiGraphQualifierHierarchy extends QualifierHierarchy { /** * Factory used to create an instance of {@link GraphQualifierHierarchy}. A factory can be used * to create at most one {@link GraphQualifierHierarchy}. * * <p>To create a hierarchy, a client may do so in three steps: * * <ol> * <li>add qualifiers using {@link #addQualifier(AnnotationMirror)}; * <li>add subtype relations using {@link #addSubtype(AnnotationMirror, AnnotationMirror)} * <li>build the hierarchy and gets using {@link #build()}. * </ol> * * Notice that {@link #addSubtype(AnnotationMirror, AnnotationMirror)} adds the two qualifiers * to the hierarchy if they are not already in. * * <p>Also, once the client builds a hierarchy through {@link #build()}, no further * modifications are allowed nor can it making a new instance. * * <p>Clients build the hierarchy using {@link #addQualifier(AnnotationMirror)} and {@link * #addSubtype(AnnotationMirror, AnnotationMirror)}, then get the instance with calling {@link * #build()} */ public static class MultiGraphFactory { /** * Map from qualifiers to the direct supertypes of the qualifier. Only the subtype relations * given by addSubtype are in this mapping, no transitive relationships. It is immutable * once GraphQualifierHierarchy is built. No polymorphic qualifiers are contained in this * map. */ protected final Map<AnnotationMirror, Set<AnnotationMirror>> supertypes; /** * Map from qualifier hierarchy to the corresponding polymorphic qualifier. The key is: * * the argument to @PolymorphicQualifier (typically the top qualifier in the hierarchy), or * * "PolymorphicQualifier" if @PolymorphicQualifier is used without an argument, or * null, * for the PolyAll qualifier. */ protected final Map<AnnotationMirror, AnnotationMirror> polyQualifiers; protected final AnnotatedTypeFactory atypeFactory; public MultiGraphFactory(AnnotatedTypeFactory atypeFactory) { this.supertypes = AnnotationUtils.createAnnotationMap(); this.polyQualifiers = new HashMap<AnnotationMirror, AnnotationMirror>(); this.atypeFactory = atypeFactory; } /** * Adds the passed qualifier to the hierarchy. Clients need to specify its super qualifiers * in subsequent calls to {@link #addSubtype(AnnotationMirror, AnnotationMirror)}. */ public void addQualifier(AnnotationMirror qual) { assertNotBuilt(); if (AnnotationUtils.containsSame(supertypes.keySet(), qual)) { return; } Class<? extends Annotation> pqtopclass = QualifierPolymorphism.getPolymorphicQualifierTop( atypeFactory.getElementUtils(), qual); if (pqtopclass != null) { AnnotationMirror pqtop = AnnotationUtils.fromClass(atypeFactory.getElementUtils(), pqtopclass); if (QualifierPolymorphism.isPolyAll(qual)) { // Use key null as marker for polyall this.polyQualifiers.put(null, qual); } else { // use given top (which might be PolymorphicQualifier) as key this.polyQualifiers.put(pqtop, qual); } } else { supertypes.put(qual, AnnotationUtils.createAnnotationSet()); } } /** * Adds a subtype relationship between the two type qualifiers. Assumes that both qualifiers * are part of the same qualifier hierarchy; callers should ensure this. * * @param sub the sub type qualifier * @param sup the super type qualifier */ public void addSubtype(AnnotationMirror sub, AnnotationMirror sup) { assertNotBuilt(); addQualifier(sub); addQualifier(sup); supertypes.get(sub).add(sup); } /** * Returns an instance of {@link GraphQualifierHierarchy} that represents the hierarchy * built so far */ public QualifierHierarchy build() { assertNotBuilt(); QualifierHierarchy result = createQualifierHierarchy(); wasBuilt = true; return result; } protected QualifierHierarchy createQualifierHierarchy() { return atypeFactory.createQualifierHierarchy(this); } private boolean wasBuilt = false; protected void assertNotBuilt() { if (wasBuilt) { ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy.Factory was already built. Method build can only be called once."); } } } /** * The declared, direct supertypes for each qualifier, without added transitive relations. * Immutable after construction finishes. No polymorphic qualifiers are contained in this map. * * @see MultiGraphQualifierHierarchy.MultiGraphFactory#supertypes */ protected final Map<AnnotationMirror, Set<AnnotationMirror>> supertypesGraph; /** The transitive closure of the supertypesGraph. Immutable after construction finishes. */ protected final Map<AnnotationMirror, Set<AnnotationMirror>> supertypesMap; /** The top qualifiers of the individual type hierarchies. */ protected final Set<AnnotationMirror> tops; /** The bottom qualifiers of the type hierarchies. TODO: clarify relation to tops. */ protected final Set<AnnotationMirror> bottoms; /** * Reference to the special qualifier org.checkerframework.framework.qual.PolymorphicQualifier. * It is used as a key in polyQualifiers, if the qualifier hierarchy consists of a single top * and no specific qualifier was specified. */ protected final AnnotationMirror polymorphicQualifier; /** @see MultiGraphQualifierHierarchy.MultiGraphFactory#polyQualifiers */ protected final Map<AnnotationMirror, AnnotationMirror> polyQualifiers; /** All qualifiers, including polymorphic qualifiers. */ private final Set<AnnotationMirror> typeQualifiers; public MultiGraphQualifierHierarchy(MultiGraphFactory f) { this(f, (Object[]) null); } // Allow a subclass to provide additional constructor parameters that // are simply passed back via a call to the "finish" method. public MultiGraphQualifierHierarchy(MultiGraphFactory f, Object... args) { super(); // no need for copying as f.supertypes has no mutable references to it // TODO: also make the Set of supertypes immutable? this.supertypesGraph = Collections.unmodifiableMap(f.supertypes); // Calculate the transitive closure Map<AnnotationMirror, Set<AnnotationMirror>> fullMap = buildFullMap(f.supertypes); Set<AnnotationMirror> newtops = findTops(fullMap); Set<AnnotationMirror> newbottoms = findBottoms(fullMap); this.polymorphicQualifier = AnnotationUtils.fromClass( f.atypeFactory.getElementUtils(), PolymorphicQualifier.class); this.polyQualifiers = f.polyQualifiers; addPolyRelations(this, fullMap, this.polyQualifiers, newtops, newbottoms); finish(this, fullMap, this.polyQualifiers, newtops, newbottoms, args); this.tops = Collections.unmodifiableSet(newtops); this.bottoms = Collections.unmodifiableSet(newbottoms); // TODO: make polyQualifiers immutable also? this.supertypesMap = Collections.unmodifiableMap(fullMap); Set<AnnotationMirror> typeQualifiers = AnnotationUtils.createAnnotationSet(); typeQualifiers.addAll(supertypesMap.keySet()); this.typeQualifiers = Collections.unmodifiableSet(typeQualifiers); // System.out.println("MGH: " + this); } /** * Method to finalize the qualifier hierarchy before it becomes unmodifiable. The parameters * pass all fields and allow modification. */ protected void finish( QualifierHierarchy qualHierarchy, Map<AnnotationMirror, Set<AnnotationMirror>> fullMap, Map<AnnotationMirror, AnnotationMirror> polyQualifiers, Set<AnnotationMirror> tops, Set<AnnotationMirror> bottoms, Object... args) {} @SideEffectFree @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Supertypes Graph: "); for (Entry<AnnotationMirror, Set<AnnotationMirror>> qual : supertypesGraph.entrySet()) { sb.append("\n\t"); sb.append(qual.getKey()); sb.append(" = "); sb.append(qual.getValue()); } sb.append("\nSupertypes Map: "); for (Entry<AnnotationMirror, Set<AnnotationMirror>> qual : supertypesMap.entrySet()) { sb.append("\n\t"); sb.append(qual.getKey()); sb.append(" = ["); Set<AnnotationMirror> supertypes = qual.getValue(); if (supertypes.size() == 1) { // if there's only 1 supertype for this qual, then directly display that in the same row sb.append(supertypes.iterator().next()); } else { // otherwise, display each supertype in its own row for (Iterator<AnnotationMirror> iterator = supertypes.iterator(); iterator.hasNext(); ) { // new line and tabbing sb.append("\n\t\t"); // display the supertype sb.append(iterator.next()); // add a comma delimiter if it isn't the last value sb.append(iterator.hasNext() ? ", " : ""); } sb.append("\n\t\t"); // new line and tab indentation for the trailing bracket } sb.append("]"); } sb.append("\nTops: "); sb.append(tops); sb.append("\nBottoms: "); sb.append(bottoms); return sb.toString(); } @Override public Set<? extends AnnotationMirror> getTopAnnotations() { return this.tops; } @Override public AnnotationMirror getTopAnnotation(AnnotationMirror start) { for (AnnotationMirror top : tops) { if (AnnotationUtils.areSame(start, top) || isSubtype(start, top)) { return top; } } ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy: did not find the top corresponding to qualifier " + start + " all tops: " + tops); return null; } @Override public Set<? extends AnnotationMirror> getBottomAnnotations() { return this.bottoms; } @Override public AnnotationMirror getBottomAnnotation(AnnotationMirror start) { for (AnnotationMirror bot : bottoms) { if (AnnotationUtils.areSame(start, bot) || isSubtype(bot, start)) { return bot; } } ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy: did not find the bottom corresponding to qualifier " + start + "; all bottoms: " + bottoms + "; this: " + this); return null; } @Override public AnnotationMirror getPolymorphicAnnotation(AnnotationMirror start) { AnnotationMirror top = getTopAnnotation(start); for (AnnotationMirror key : polyQualifiers.keySet()) { if (AnnotationUtils.areSame(key, top)) { return polyQualifiers.get(key); } } if (AnnotationUtils.containsSame(polyQualifiers.keySet(), polymorphicQualifier)) { return polyQualifiers.get(polymorphicQualifier); } else { // No polymorphic qualifier exists for that hierarchy. ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy: did not find the polymorphic qualifier corresponding to qualifier " + start + "; all polymorphic qualifiers: " + polyQualifiers + "; this: " + this); return null; } } @Override public boolean isSubtype( Collection<? extends AnnotationMirror> rhs, Collection<? extends AnnotationMirror> lhs) { rhs = replacePolyAll(rhs); lhs = replacePolyAll(lhs); if (lhs.isEmpty() || rhs.isEmpty()) { ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy: empty annotations in lhs: " + lhs + " or rhs: " + rhs); } if (lhs.size() != rhs.size()) { ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy: mismatched number of annotations in lhs: " + lhs + " and rhs: " + rhs); } int valid = 0; for (AnnotationMirror lhsAnno : lhs) { for (AnnotationMirror rhsAnno : rhs) { if (AnnotationUtils.areSame(getTopAnnotation(lhsAnno), getTopAnnotation(rhsAnno)) && isSubtype(rhsAnno, lhsAnno)) { ++valid; } } } return lhs.size() == valid; } @Override public boolean isSubtypeTypeVariable( Collection<? extends AnnotationMirror> subAnnos, Collection<? extends AnnotationMirror> superAnnos) { for (AnnotationMirror top : getTopAnnotations()) { AnnotationMirror rhsForTop = findAnnotationInHierarchy(subAnnos, top); AnnotationMirror lhsForTop = findAnnotationInHierarchy(superAnnos, top); if (!isSubtypeTypeVariable(rhsForTop, lhsForTop)) { return false; } } return true; } @Override public Set<? extends AnnotationMirror> getTypeQualifiers() { return typeQualifiers; } // For caching results of lubs private Map<AnnotationPair, AnnotationMirror> lubs = null; @Override public AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { if (!AnnotationUtils.areSameIgnoringValues(getTopAnnotation(a1), getTopAnnotation(a2))) { return null; } else if (isSubtype(a1, a2)) { return a2; } else if (isSubtype(a2, a1)) { return a1; } else if (AnnotationUtils.areSameIgnoringValues(a1, a2)) { return getTopAnnotation(a1); } if (lubs == null) { lubs = calculateLubs(); } AnnotationPair pair = new AnnotationPair(a1, a2); return lubs.get(pair); } @Override public AnnotationMirror leastUpperBoundTypeVariable(AnnotationMirror a1, AnnotationMirror a2) { if (a1 == null || a2 == null) { // [] is a supertype of any qualifier, and [] <: [] return null; } return leastUpperBound(a1, a2); } /** A cache of the results of glb computations. Maps from a pair of annotations to their glb. */ private Map<AnnotationPair, AnnotationMirror> glbs = null; @Override public AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2) { if (AnnotationUtils.areSameIgnoringValues(a1, a2)) { return AnnotationUtils.areSame(a1, a2) ? a1 : getBottomAnnotation(a1); } if (glbs == null) { glbs = calculateGlbs(); } AnnotationPair pair = new AnnotationPair(a1, a2); return glbs.get(pair); } @Override public AnnotationMirror greatestLowerBoundTypeVariable( AnnotationMirror a1, AnnotationMirror a2) { if (a1 == null) { // [] is a supertype of any qualifier, and [] <: [] return a2; } if (a2 == null) { // [] is a supertype of any qualifier, and [] <: [] return a1; } return greatestLowerBound(a1, a2); } /** * {@inheritDoc} * * <p>Most qualifiers have no value fields. However, two annotations with values are subtype of * each other only if they have the same values. i.e. I(m) is a subtype of I(n) iff m = n * * <p>When client specifies an annotation, a1, to be a subtype of annotation with values, a2, * then a1 is a subtype of all instances of a2 regardless of a2 values. * * @param subAnno the sub qualifier * @param superAnno the super qualifier */ @Override public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { checkAnnoInGraph(subAnno); checkAnnoInGraph(superAnno); /* TODO: this optimization leads to recursion for (AnnotationMirror top : tops) { System.out.println("Looking at top: " + tops + " and " + anno1); // We cannot use getRootAnnotation, as that would use subtyping and recurse if (isSubtype(anno1, top) && AnnotationUtils.areSame(top, anno2)) { return true; } }*/ if (AnnotationUtils.areSameIgnoringValues(subAnno, superAnno)) { return AnnotationUtils.areSame(subAnno, superAnno); } Set<AnnotationMirror> supermap1 = this.supertypesMap.get(subAnno); return AnnotationUtils.containsSame(supermap1, superAnno); } @Override public boolean isSubtypeTypeVariable(AnnotationMirror subAnno, AnnotationMirror superAnno) { if (superAnno == null) { // [] is a supertype of any qualifier, and [] <: [] return true; } if (subAnno == null) { // [] is a subtype of no qualifier (only []) return false; } return isSubtype(subAnno, superAnno); } private final void checkAnnoInGraph(AnnotationMirror a) { if (AnnotationUtils.containsSame(supertypesMap.keySet(), a) || AnnotationUtils.containsSame(polyQualifiers.values(), a)) return; if (a == null) { ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy found an unqualified type. Please ensure that " + "your implicit rules cover all cases and/or " + "use a @DefaultQualifierInHierarchy annotation."); } else { // System.out.println("MultiGraphQH: " + this); ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy found the unrecognized qualifier: " + a + ". Please ensure that the qualifier is correctly included in the subtype hierarchy."); } } /** * Infer the tops of the subtype hierarchy. Simple finds the qualifiers that have no supertypes. */ // Not static to allow adaptation in subclasses. Only parameters should be modified. protected Set<AnnotationMirror> findTops( Map<AnnotationMirror, Set<AnnotationMirror>> supertypes) { Set<AnnotationMirror> possibleTops = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror anno : supertypes.keySet()) { if (supertypes.get(anno).isEmpty()) { possibleTops.add(anno); } } return possibleTops; } /** * Infer the bottoms of the subtype hierarchy. Simple finds the qualifiers that are not * supertypes of other qualifiers. */ // Not static to allow adaptation in subclasses. Only parameters should be modified. protected Set<AnnotationMirror> findBottoms( Map<AnnotationMirror, Set<AnnotationMirror>> supertypes) { Set<AnnotationMirror> possibleBottoms = AnnotationUtils.createAnnotationSet(); possibleBottoms.addAll(supertypes.keySet()); for (Set<AnnotationMirror> supers : supertypes.values()) { possibleBottoms.removeAll(supers); } return possibleBottoms; } /** Computes the transitive closure of the given map and returns it. */ /* The method gets all required parameters passed in and could be static. However, * we want to allow subclasses to adapt the behavior and therefore make it an instance method. */ protected Map<AnnotationMirror, Set<AnnotationMirror>> buildFullMap( Map<AnnotationMirror, Set<AnnotationMirror>> supertypes) { Map<AnnotationMirror, Set<AnnotationMirror>> fullMap = AnnotationUtils.createAnnotationMap(); for (AnnotationMirror anno : supertypes.keySet()) { // this method directly modifies fullMap and is // ignoring the returned value findAllSupers(anno, supertypes, fullMap); } return fullMap; } /** * Add the relationships for polymorphic qualifiers. * * <p>A polymorphic qualifier, such as {@code PolyNull}, needs to be: * * <ol> * <li>a subtype of the top qualifier (e.g. {@code Nullable}) * <li>a supertype of all the bottom qualifiers (e.g. {@code NonNull}) * </ol> * * Field supertypesMap is not set yet when this method is called -- use fullMap instead. */ // The method gets all required parameters passed in and could be static. However, // we want to allow subclasses to adapt the behavior and therefore make it an instance method. protected void addPolyRelations( QualifierHierarchy qualHierarchy, Map<AnnotationMirror, Set<AnnotationMirror>> fullMap, Map<AnnotationMirror, AnnotationMirror> polyQualifiers, Set<AnnotationMirror> tops, Set<AnnotationMirror> bottoms) { if (polyQualifiers.isEmpty()) { return; } for (Map.Entry<AnnotationMirror, AnnotationMirror> kv : polyQualifiers.entrySet()) { AnnotationMirror declTop = kv.getKey(); AnnotationMirror polyQualifier = kv.getValue(); if (declTop == null || // PolyAll AnnotationUtils.areSame(declTop, polymorphicQualifier)) { if (declTop == null || // PolyAll tops.size() == 1) { // un-ambigous single top AnnotationUtils.updateMappingToImmutableSet(fullMap, polyQualifier, tops); for (AnnotationMirror bottom : bottoms) { // Add the polyqualifier as a supertype // Need to copy over the set as it is unmodifiable. AnnotationUtils.updateMappingToImmutableSet( fullMap, bottom, Collections.singleton(polyQualifier)); } if (declTop == null) { // PolyAll // Make all other polymorphic qualifiers a subtype of PolyAll for (Map.Entry<AnnotationMirror, AnnotationMirror> otherpolyKV : polyQualifiers.entrySet()) { AnnotationMirror otherTop = otherpolyKV.getKey(); AnnotationMirror otherPoly = otherpolyKV.getValue(); if (otherTop != null) { AnnotationUtils.updateMappingToImmutableSet( fullMap, otherPoly, Collections.singleton(polyQualifier)); } } } } else { ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy.addPolyRelations: " + "incorrect or missing top qualifier given in polymorphic qualifier " + polyQualifier + "; declTop = " + declTop + "; possible top qualifiers: " + tops); } } else { // Ensure that it's really the top of the hierarchy Set<AnnotationMirror> declSupers = fullMap.get(declTop); AnnotationMirror polyTop = null; if (declSupers.isEmpty()) { polyTop = declTop; } else { for (AnnotationMirror ds : declSupers) { if (AnnotationUtils.containsSameIgnoringValues(tops, ds)) { polyTop = ds; } } } boolean found = (polyTop != null); if (found) { AnnotationUtils.updateMappingToImmutableSet( fullMap, polyQualifier, Collections.singleton(polyTop)); } else { ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy.addPolyRelations: " + "incorrect top qualifier given in polymorphic qualifier: " + polyQualifier + " could not find: " + polyTop); } found = false; AnnotationMirror bottom = null; outer: for (AnnotationMirror btm : bottoms) { for (AnnotationMirror btmsuper : fullMap.get(btm)) { if (AnnotationUtils.areSameIgnoringValues(btmsuper, polyTop)) { found = true; bottom = btm; break outer; } } } if (found) { AnnotationUtils.updateMappingToImmutableSet( fullMap, bottom, Collections.singleton(polyQualifier)); } else { // TODO: in a type system with a single qualifier this check will fail. // ErrorReporter.errorAbort("MultiGraphQualifierHierarchy.addPolyRelations: " + // "incorrect top qualifier given in polymorphic qualifier: " + polyQualifier + // " could not find bottom for: " + polyTop); } } } } private Map<AnnotationPair, AnnotationMirror> calculateLubs() { Map<AnnotationPair, AnnotationMirror> newlubs = new HashMap<AnnotationPair, AnnotationMirror>(); for (AnnotationMirror a1 : typeQualifiers) { for (AnnotationMirror a2 : typeQualifiers) { if (AnnotationUtils.areSameIgnoringValues(a1, a2)) { continue; } if (!AnnotationUtils.areSame(getTopAnnotation(a1), getTopAnnotation(a2))) { continue; } AnnotationPair pair = new AnnotationPair(a1, a2); if (newlubs.containsKey(pair)) { continue; } AnnotationMirror lub = findLub(a1, a2); newlubs.put(pair, lub); } } return newlubs; } /** * Finds and returns the Least Upper Bound (LUB) of two annotation mirrors a1 and a2 by * recursively climbing the qualifier hierarchy of a1 until one of them is a subtype of the * other, or returns null if no subtype relationships can be found * * @param a1 first annotation mirror * @param a2 second annotation mirror * @return the LUB of a1 and a2, or null if none can be found */ protected AnnotationMirror findLub(AnnotationMirror a1, AnnotationMirror a2) { if (isSubtype(a1, a2)) { return a2; } if (isSubtype(a2, a1)) { return a1; } assert getTopAnnotation(a1) == getTopAnnotation(a2) : "MultiGraphQualifierHierarchy.findLub: this method may only be called " + "with qualifiers from the same hierarchy. Found a1: " + a1 + " [top: " + getTopAnnotation(a1) + "], a2: " + a2 + " [top: " + getTopAnnotation(a2) + "]"; if (isPolymorphicQualifier(a1)) { return findLubWithPoly(a1, a2); } else if (isPolymorphicQualifier(a2)) { return findLubWithPoly(a2, a1); } Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror a1Super : supertypesGraph.get(a1)) { // TODO: we take the first of the smallest supertypes, maybe we would // get a different LUB if we used a different one? AnnotationMirror a1Lub = findLub(a1Super, a2); if (a1Lub != null) { outset.add(a1Lub); } else { ErrorReporter.errorAbort( "GraphQualifierHierarchy could not determine LUB for " + a1 + " and " + a2 + ". Please ensure that the checker knows about all type qualifiers."); } } if (outset.size() == 1) { return outset.iterator().next(); } if (outset.size() > 1) { // outset is created by climbing the supertypes of the left type, which can go higher in the lattice than needed // findSmallestTypes will remove the unnecessary supertypes of supertypes, retaining only the least upper bound(s) outset = findSmallestTypes(outset); // picks the first qualifier that isn't a polymorphic qualifier // the outset should only have 1 qualifier that isn't polymorphic Iterator<AnnotationMirror> outsetIterator = outset.iterator(); AnnotationMirror anno; do { anno = outsetIterator.next(); } while (isPolymorphicQualifier(anno)); // TODO: more than one, incomparable supertypes. Just pick the first one. // if (outset.size()>1) { System.out.println("Still more than one LUB!"); } return anno; } ErrorReporter.errorAbort( "GraphQualifierHierarchy could not determine LUB for " + a1 + " and " + a2 + ". Please ensure that the checker knows about all type qualifiers."); return null; } private AnnotationMirror findLubWithPoly(AnnotationMirror poly, AnnotationMirror other) { AnnotationMirror bottom = getBottomAnnotation(other); if (AnnotationUtils.areSame(bottom, other)) { return poly; } return getTopAnnotation(poly); } /** Sees if a particular annotation mirror is a polymorphic qualifier. */ private boolean isPolymorphicQualifier(AnnotationMirror qual) { return AnnotationUtils.containsSame(polyQualifiers.values(), qual); } /** Remove all supertypes of elements contained in the set. */ private Set<AnnotationMirror> findSmallestTypes(Set<AnnotationMirror> inset) { Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); outset.addAll(inset); for (AnnotationMirror a1 : inset) { Iterator<AnnotationMirror> outit = outset.iterator(); while (outit.hasNext()) { AnnotationMirror a2 = outit.next(); if (a1 != a2 && isSubtype(a1, a2)) { outit.remove(); } } } return outset; } /** Finds all the super qualifiers for a qualifier. */ private static Set<AnnotationMirror> findAllSupers( AnnotationMirror anno, Map<AnnotationMirror, Set<AnnotationMirror>> supertypes, Map<AnnotationMirror, Set<AnnotationMirror>> allSupersSoFar) { Set<AnnotationMirror> supers = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror superAnno : supertypes.get(anno)) { // add the current super to the superset supers.add(superAnno); // add all of current super's super into superset supers.addAll(findAllSupers(superAnno, supertypes, allSupersSoFar)); } allSupersSoFar.put(anno, Collections.unmodifiableSet(supers)); return supers; } /** Returns a map from each possible pair of annotations to their glb. */ private Map<AnnotationPair, AnnotationMirror> calculateGlbs() { Map<AnnotationPair, AnnotationMirror> newglbs = new HashMap<AnnotationPair, AnnotationMirror>(); for (AnnotationMirror a1 : typeQualifiers) { for (AnnotationMirror a2 : typeQualifiers) { if (AnnotationUtils.areSameIgnoringValues(a1, a2)) { continue; } if (!AnnotationUtils.areSame(getTopAnnotation(a1), getTopAnnotation(a2))) { continue; } AnnotationPair pair = new AnnotationPair(a1, a2); if (newglbs.containsKey(pair)) { continue; } AnnotationMirror glb = findGlb(a1, a2); newglbs.put(pair, glb); } } return newglbs; } private AnnotationMirror findGlb(AnnotationMirror a1, AnnotationMirror a2) { if (isSubtype(a1, a2)) { return a1; } if (isSubtype(a2, a1)) { return a2; } assert getTopAnnotation(a1) == getTopAnnotation(a2) : "MultiGraphQualifierHierarchy.findGlb: this method may only be called " + "with qualifiers from the same hierarchy. Found a1: " + a1 + " [top: " + getTopAnnotation(a1) + "], a2: " + a2 + " [top: " + getTopAnnotation(a2) + "]"; if (isPolymorphicQualifier(a1)) { return findGlbWithPoly(a1, a2); } else if (isPolymorphicQualifier(a2)) { return findGlbWithPoly(a2, a1); } Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror a1Sub : supertypesGraph.keySet()) { if (isSubtype(a1Sub, a1) && !a1Sub.equals(a1)) { AnnotationMirror a1lb = findGlb(a1Sub, a2); if (a1lb != null) { outset.add(a1lb); } } } if (outset.size() == 1) { return outset.iterator().next(); } if (outset.size() > 1) { outset = findGreatestTypes(outset); // More than one, incomparable greatest subtypes. Pick the first one. // TODO: Is that the right approach? // if (outset.size()>1) { System.out.println("Still more than one GLB!"); } return outset.iterator().next(); } ErrorReporter.errorAbort( "MultiGraphQualifierHierarchy could not determine GLB for " + a1 + " and " + a2 + ". Please ensure that the checker knows about all type qualifiers."); return null; } private AnnotationMirror findGlbWithPoly(AnnotationMirror poly, AnnotationMirror other) { AnnotationMirror top = getTopAnnotation(other); if (AnnotationUtils.areSame(top, other)) { return poly; } return getBottomAnnotation(poly); } /** Remove all subtypes of elements contained in the set. */ private Set<AnnotationMirror> findGreatestTypes(Set<AnnotationMirror> inset) { Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); outset.addAll(inset); for (AnnotationMirror a1 : inset) { Iterator<AnnotationMirror> outit = outset.iterator(); while (outit.hasNext()) { AnnotationMirror a2 = outit.next(); if (a1 != a2 && isSubtype(a2, a1)) { outit.remove(); } } } return outset; } private static class AnnotationPair { public final AnnotationMirror a1; public final AnnotationMirror a2; private int hashCode = -1; public AnnotationPair(AnnotationMirror a1, AnnotationMirror a2) { this.a1 = a1; this.a2 = a2; } @Pure @Override public int hashCode() { if (hashCode == -1) { hashCode = 31; if (a1 != null) { hashCode += 17 * AnnotationUtils.annotationName(a1).toString().hashCode(); } if (a2 != null) { hashCode += 17 * AnnotationUtils.annotationName(a2).toString().hashCode(); } } return hashCode; } @Override public boolean equals(Object o) { if (!(o instanceof AnnotationPair)) { return false; } AnnotationPair other = (AnnotationPair) o; if (AnnotationUtils.areSameIgnoringValues(a1, other.a1) && AnnotationUtils.areSameIgnoringValues(a2, other.a2)) { return true; } if (AnnotationUtils.areSameIgnoringValues(a2, other.a1) && AnnotationUtils.areSameIgnoringValues(a1, other.a2)) { return true; } return false; } @SideEffectFree @Override public String toString() { return "AnnotationPair(" + a1 + ", " + a2 + ")"; } } }