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

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

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