blob: 9f3d6354e6f377004c99e8ee84fbccbc7d5249af [file] [log] [blame]
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);
}
}