All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.framework.util.MultiGraphQualifierHierarchy Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
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 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;

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;

/**
 * Represents the type qualifier hierarchy of a type system.
 *
 * This class is immutable and can be only created through {@link MultiGraphFactory}.
 *
 * A QualifierHierarchy that supports multiple separate subtype hierarchies.
 */
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}.
     *
     * To create a hierarchy, a client may do so in three steps:
     * 1. add qualifiers using {@link #addQualifier(AnnotationMirror)};
     * 2. add subtype relations using {@link #addSubtype(AnnotationMirror, AnnotationMirror)}
     * 3. build the hierarchy and gets using {@link #build()}.
     *
     * Notice that {@link #addSubtype(AnnotationMirror, AnnotationMirror)} adds
     * the two qualifiers to the hierarchy if they are not already in.
     *
     * Also, once the client builds a hierarchy through {@link #build()},
     * no further modifications are allowed nor can it making a new instance.
     *
     * 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> 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 polyQualifiers;

        protected final AnnotatedTypeFactory atypeFactory;

        public MultiGraphFactory(AnnotatedTypeFactory atypeFactory) {
            this.supertypes = AnnotationUtils.createAnnotationMap();
            this.polyQualifiers = new HashMap();
            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 (supertypes.containsKey(qual)) {
                return;
            }

            Class 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> supertypesGraph;

    /**
     * The transitive closure of the supertypesGraph.
     * Immutable after construction finishes.
     */
    protected final Map> supertypesMap;

    /**
     * The top qualifiers of the individual type hierarchies.
     */
    protected final Set tops;

    /**
     * The bottom qualifiers of the type hierarchies.
     * TODO: clarify relation to tops.
     */
    protected final Set 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 polyQualifiers;

    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>  fullMap = buildFullMap(f.supertypes);

        Set newtops = findTops(fullMap);
        Set 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);
        // 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> fullMap,
            Map polyQualifiers,
            Set tops, Set bottoms,
            Object... args) { }

    @SideEffectFree
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Supertypes Graph: ");

        for (Entry> qual : supertypesGraph.entrySet()) {
            sb.append("\n\t");
            sb.append(qual.getKey());
            sb.append(" = ");
            sb.append(qual.getValue());
        }

        sb.append("\nSupertypes Map: ");

        for (Entry> qual : supertypesMap.entrySet()) {
            sb.append("\n\t");
            sb.append(qual.getKey());
            sb.append(" = [");

            Set 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 iterator = supertypes.iterator(); iterator.hasNext(); ) {
                    sb.append("\n\t\t");                            // new line and tabbing
                    sb.append(iterator.next());                     // display the supertype
                    sb.append(iterator.hasNext() ? ", " : "");      // add a comma delimiter if it isn't the last value
                }
                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 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 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);
        if (polyQualifiers.containsKey(top)) {
            return polyQualifiers.get(top);
        } else if (polyQualifiers.containsKey(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 rhs, Collection 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 rhs, Collection lhs) {
        for (AnnotationMirror top : getTopAnnotations()) {
            AnnotationMirror rhsForTop = getAnnotationInHierarchy(rhs, top);
            AnnotationMirror lhsForTop = getAnnotationInHierarchy(lhs, top);
            if (!isSubtypeTypeVariable(rhsForTop, lhsForTop)) {
                return false;
            }
        }
        return true;
    }

    protected Set typeQualifiers = null;

    @Override
    public Set getTypeQualifiers() {
        return Collections.unmodifiableSet(supertypesMap.keySet());
    }


    // For caching results of lubs
    private Map 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 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);
    }

    /**
     * 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
     *
     * 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 rhs the right-hand side, i.e. the sub qualifier
     * @param lhs the left-hand side, i.e. the super qualifier
     */
    @Override
    public boolean isSubtype(AnnotationMirror rhs, AnnotationMirror lhs) {
        checkAnnoInGraph(rhs);
        checkAnnoInGraph(lhs);

        /* 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(rhs, lhs)) {
            return AnnotationUtils.areSame(rhs, lhs);
        }
        Set supermap1 = this.supertypesMap.get(rhs);
        return AnnotationUtils.containsSame(supermap1, lhs);
    }

    @Override
    public boolean isSubtypeTypeVariable(AnnotationMirror rhs, AnnotationMirror lhs) {
        if (lhs == null) {
            // [] is a supertype of any qualifier, and [] <: []
            return true;
        }
        if (rhs == null) {
            // [] is a subtype of no qualifier (only [])
            return false;
        }
        return isSubtype(rhs, lhs);
    }

    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
    findTops(Map> supertypes) {
        Set 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
    findBottoms(Map> supertypes) {
        Set possibleBottoms = AnnotationUtils.createAnnotationSet();
        possibleBottoms.addAll(supertypes.keySet());
        for (Set 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>
    buildFullMap(Map> supertypes) {
        Map> 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.
     *
     * A polymorphic qualifier needs to be (take {@code PolyNull} for example):
     * 1. a subtype of the top qualifier (e.g. {@code Nullable})
     * 2. a supertype of all the bottom qualifiers  (e.g. {@code NonNull})
     *
     * 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> fullMap,
            Map polyQualifiers,
            Set tops, Set bottoms) {
        if (polyQualifiers.isEmpty()) {
            return;
        }

        for (Map.Entry 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 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 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  calculateLubs() {
        Map newlubs = new HashMap();
        for (AnnotationMirror a1 : supertypesGraph.keySet()) {
            for (AnnotationMirror a2 : supertypesGraph.keySet()) {
                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) + "]";

        Set 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 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;
    }

    /** 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 findSmallestTypes(Set inset) {
        Set outset = AnnotationUtils.createAnnotationSet();
        outset.addAll(inset);

        for (AnnotationMirror a1 : inset) {
            Iterator 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
    findAllSupers(AnnotationMirror anno,
            Map> supertypes,
            Map> allSupersSoFar) {
        Set 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 calculateGlbs() {
        Map newglbs = new HashMap();
        for (AnnotationMirror a1 : supertypesGraph.keySet()) {
            for (AnnotationMirror a2 : supertypesGraph.keySet()) {
                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) + "]";

        Set 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;
    }

    /** Remove all subtypes of elements contained in the set. */
    private Set findGreatestTypes(Set inset) {
        Set outset = AnnotationUtils.createAnnotationSet();
        outset.addAll(inset);

        for (AnnotationMirror a1 : inset) {
            Iterator 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 + ")";
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy