| package org.checkerframework.framework.type; |
| |
| import java.lang.annotation.Annotation; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.util.Elements; |
| import org.checkerframework.checker.initialization.qual.UnderInitialization; |
| import org.checkerframework.checker.nullness.qual.NonNull; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.checker.nullness.qual.RequiresNonNull; |
| import org.checkerframework.framework.qual.AnnotatedFor; |
| import org.checkerframework.framework.util.DefaultQualifierKindHierarchy; |
| import org.checkerframework.framework.util.QualifierKind; |
| import org.checkerframework.framework.util.QualifierKindHierarchy; |
| import org.checkerframework.javacutil.AnnotationBuilder; |
| import org.checkerframework.javacutil.AnnotationUtils; |
| import org.checkerframework.javacutil.BugInCF; |
| import org.checkerframework.javacutil.TypeSystemError; |
| |
| /** |
| * A {@link QualifierHierarchy} where no qualifier has arguments; that is, no qualifier is |
| * represented by an annotation with elements. The meta-annotation {@link |
| * org.checkerframework.framework.qual.SubtypeOf} specifies the subtyping relationships. |
| * |
| * <p>It uses a {@link QualifierKindHierarchy} to model the relationships between qualifiers. |
| * Subclasses can override {@link #createQualifierKindHierarchy(Collection)} to return a subclass of |
| * QualifierKindHierarchy. |
| */ |
| @AnnotatedFor("nullness") |
| public class NoElementQualifierHierarchy implements QualifierHierarchy { |
| |
| /** {@link QualifierKindHierarchy}. */ |
| protected final QualifierKindHierarchy qualifierKindHierarchy; |
| |
| /** Set of top annotation mirrors. */ |
| protected final Set<AnnotationMirror> tops; |
| |
| /** Set of bottom annotation mirrors. */ |
| protected final Set<AnnotationMirror> bottoms; |
| |
| /** Mapping from {@link QualifierKind} to its corresponding {@link AnnotationMirror}. */ |
| protected final Map<QualifierKind, AnnotationMirror> kindToAnnotationMirror; |
| |
| /** Set of all annotations in all the hierarchies. */ |
| protected final Set<? extends AnnotationMirror> qualifiers; |
| |
| /** |
| * Creates a NoElementQualifierHierarchy from the given classes. |
| * |
| * @param qualifierClasses classes of annotations that are the qualifiers |
| * @param elements element utils |
| */ |
| public NoElementQualifierHierarchy( |
| Collection<Class<? extends Annotation>> qualifierClasses, Elements elements) { |
| this.qualifierKindHierarchy = createQualifierKindHierarchy(qualifierClasses); |
| |
| this.kindToAnnotationMirror = createAnnotationMirrors(elements); |
| this.qualifiers = |
| AnnotationUtils.createUnmodifiableAnnotationSet(kindToAnnotationMirror.values()); |
| |
| this.tops = createTops(); |
| this.bottoms = createBottoms(); |
| } |
| |
| /** |
| * Create the {@link QualifierKindHierarchy}. (Subclasses may override to return a subclass of |
| * QualifierKindHierarchy.) |
| * |
| * @param qualifierClasses classes of annotations that are the qualifiers |
| * @return the newly created qualifier kind hierarchy |
| */ |
| protected QualifierKindHierarchy createQualifierKindHierarchy( |
| @UnderInitialization NoElementQualifierHierarchy this, |
| Collection<Class<? extends Annotation>> qualifierClasses) { |
| return new DefaultQualifierKindHierarchy(qualifierClasses); |
| } |
| |
| /** |
| * Creates and returns a mapping from qualifier kind to an annotation mirror created from the |
| * qualifier kind's annotation class. |
| * |
| * @param elements element utils |
| * @return a mapping from qualifier kind to its annotation mirror |
| */ |
| @RequiresNonNull("this.qualifierKindHierarchy") |
| protected Map<QualifierKind, AnnotationMirror> createAnnotationMirrors( |
| @UnderInitialization NoElementQualifierHierarchy this, Elements elements) { |
| Map<QualifierKind, AnnotationMirror> quals = new TreeMap<>(); |
| for (QualifierKind kind : qualifierKindHierarchy.allQualifierKinds()) { |
| if (kind.hasElements()) { |
| throw new TypeSystemError(kind + "has elements"); |
| } |
| quals.put(kind, AnnotationBuilder.fromClass(elements, kind.getAnnotationClass())); |
| } |
| return Collections.unmodifiableMap(quals); |
| } |
| |
| /** |
| * Creates and returns the unmodifiable set of top {@link AnnotationMirror}s. |
| * |
| * @return the unmodifiable set of top {@link AnnotationMirror}s |
| */ |
| @RequiresNonNull({"this.kindToAnnotationMirror", "this.qualifierKindHierarchy"}) |
| protected Set<AnnotationMirror> createTops( |
| @UnderInitialization NoElementQualifierHierarchy this) { |
| Set<AnnotationMirror> tops = AnnotationUtils.createAnnotationSet(); |
| for (QualifierKind top : qualifierKindHierarchy.getTops()) { |
| @SuppressWarnings( |
| "nullness:assignment" // All QualifierKinds are keys in kindToAnnotationMirror |
| ) |
| @NonNull AnnotationMirror topAnno = kindToAnnotationMirror.get(top); |
| tops.add(topAnno); |
| } |
| return Collections.unmodifiableSet(tops); |
| } |
| |
| /** |
| * Creates and returns the unmodifiable set of bottom {@link AnnotationMirror}s. |
| * |
| * @return the unmodifiable set of bottom {@link AnnotationMirror}s |
| */ |
| @RequiresNonNull({"this.kindToAnnotationMirror", "this.qualifierKindHierarchy"}) |
| protected Set<AnnotationMirror> createBottoms( |
| @UnderInitialization NoElementQualifierHierarchy this) { |
| Set<AnnotationMirror> bottoms = AnnotationUtils.createAnnotationSet(); |
| for (QualifierKind bottom : qualifierKindHierarchy.getBottoms()) { |
| @SuppressWarnings( |
| "nullness:assignment" // All QualifierKinds are keys in kindToAnnotationMirror |
| ) |
| @NonNull AnnotationMirror bottomAnno = kindToAnnotationMirror.get(bottom); |
| bottoms.add(bottomAnno); |
| } |
| return Collections.unmodifiableSet(bottoms); |
| } |
| |
| /** |
| * Returns the {@link QualifierKind} for the given annotation. |
| * |
| * @param anno an annotation that is a qualifier in this |
| * @return the {@code QualifierKind} for the given annotation |
| */ |
| protected QualifierKind getQualifierKind(AnnotationMirror anno) { |
| String name = AnnotationUtils.annotationName(anno); |
| QualifierKind kind = qualifierKindHierarchy.getQualifierKind(name); |
| if (kind == null) { |
| throw new BugInCF("Annotation not in hierarchy: %s", anno); |
| } |
| return kind; |
| } |
| |
| @Override |
| public @Nullable AnnotationMirror findAnnotationInSameHierarchy( |
| Collection<? extends AnnotationMirror> annos, AnnotationMirror annotationMirror) { |
| QualifierKind kind = getQualifierKind(annotationMirror); |
| for (AnnotationMirror candidate : annos) { |
| QualifierKind candidateKind = getQualifierKind(candidate); |
| if (candidateKind.isInSameHierarchyAs(kind)) { |
| return candidate; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public @Nullable AnnotationMirror findAnnotationInHierarchy( |
| Collection<? extends AnnotationMirror> annos, AnnotationMirror top) { |
| return findAnnotationInSameHierarchy(annos, top); |
| } |
| |
| @Override |
| public Set<? extends AnnotationMirror> getTopAnnotations() { |
| return tops; |
| } |
| |
| @Override |
| @SuppressWarnings( |
| "nullness:return" // every QualifierKind is a key in its corresponding kindToAnnotationMirror |
| ) |
| public AnnotationMirror getTopAnnotation(AnnotationMirror start) { |
| QualifierKind kind = getQualifierKind(start); |
| return kindToAnnotationMirror.get(kind.getTop()); |
| } |
| |
| @Override |
| public Set<? extends AnnotationMirror> getBottomAnnotations() { |
| return bottoms; |
| } |
| |
| @Override |
| @SuppressWarnings( |
| "nullness:return" // every QualifierKind is a key in its corresponding kindToAnnotationMirror |
| ) |
| public AnnotationMirror getBottomAnnotation(AnnotationMirror start) { |
| QualifierKind kind = getQualifierKind(start); |
| return kindToAnnotationMirror.get(kind.getBottom()); |
| } |
| |
| @Override |
| public @Nullable AnnotationMirror getPolymorphicAnnotation(AnnotationMirror start) { |
| QualifierKind poly = getQualifierKind(start).getPolymorphic(); |
| if (poly == null) { |
| return null; |
| } |
| return kindToAnnotationMirror.get(poly); |
| } |
| |
| @Override |
| public boolean isPolymorphicQualifier(AnnotationMirror qualifier) { |
| return getQualifierKind(qualifier).isPoly(); |
| } |
| |
| @Override |
| public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { |
| QualifierKind subKind = getQualifierKind(subAnno); |
| QualifierKind superKind = getQualifierKind(superAnno); |
| return subKind.isSubtypeOf(superKind); |
| } |
| |
| @Override |
| public @Nullable AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { |
| QualifierKind qual1 = getQualifierKind(a1); |
| QualifierKind qual2 = getQualifierKind(a2); |
| |
| QualifierKind lub = qualifierKindHierarchy.leastUpperBound(qual1, qual2); |
| if (lub == null) { |
| return null; |
| } |
| return kindToAnnotationMirror.get(lub); |
| } |
| |
| @Override |
| public @Nullable AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2) { |
| QualifierKind qual1 = getQualifierKind(a1); |
| QualifierKind qual2 = getQualifierKind(a2); |
| QualifierKind glb = qualifierKindHierarchy.greatestLowerBound(qual1, qual2); |
| if (glb == null) { |
| return null; |
| } |
| return kindToAnnotationMirror.get(glb); |
| } |
| } |