blob: 99b8f81a129c96d8877310755ebd438159b7d296 [file] [log] [blame]
package org.checkerframework.framework.type;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.framework.qual.AnnotatedFor;
import org.checkerframework.framework.util.QualifierKind;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.BugInCF;
/**
* A {@link org.checkerframework.framework.type.QualifierHierarchy} where, when a qualifier has
* arguments, the subtype relation is determined by a superset test on the elements (arguments). The
* elements must be strings.
*
* <p>This assumes that if the lub or glb of two qualifiers has elements, then both of the arguments
* had the same kind as the result does.
*/
@AnnotatedFor("nullness")
public class SubtypeIsSupersetQualifierHierarchy extends MostlyNoElementQualifierHierarchy {
/** The processing environment; used for creating annotations. */
ProcessingEnvironment processingEnv;
/**
* Creates a SubtypeIsSupersetQualifierHierarchy from the given classes.
*
* @param qualifierClasses classes of annotations that are the qualifiers for this hierarchy
* @param processingEnv processing environment
*/
public SubtypeIsSupersetQualifierHierarchy(
Collection<Class<? extends Annotation>> qualifierClasses,
ProcessingEnvironment processingEnv) {
super(qualifierClasses, processingEnv.getElementUtils());
this.processingEnv = processingEnv;
}
@Override
protected boolean isSubtypeWithElements(
AnnotationMirror subAnno,
QualifierKind subKind,
AnnotationMirror superAnno,
QualifierKind superKind) {
if (subKind == superKind) {
List<String> superValues = valuesStringList(superAnno);
List<String> subValues = valuesStringList(subAnno);
return subValues.containsAll(superValues);
}
return subKind.isSubtypeOf(superKind);
}
@Override
protected AnnotationMirror leastUpperBoundWithElements(
AnnotationMirror a1,
QualifierKind qualifierKind1,
AnnotationMirror a2,
QualifierKind qualifierKind2,
QualifierKind lubKind) {
if (qualifierKind1 == qualifierKind2) {
List<String> a1Values = valuesStringList(a1);
List<String> a2Values = valuesStringList(a2);
LinkedHashSet<String> set = new LinkedHashSet<>(a1Values);
set.retainAll(a2Values);
return createAnnotationMirrorWithValue(lubKind, set);
} else if (lubKind == qualifierKind1) {
return a1;
} else if (lubKind == qualifierKind2) {
return a2;
} else {
throw new BugInCF("Unexpected QualifierKinds %s %s", qualifierKind1, qualifierKind2, lubKind);
}
}
@Override
protected AnnotationMirror greatestLowerBoundWithElements(
AnnotationMirror a1,
QualifierKind qualifierKind1,
AnnotationMirror a2,
QualifierKind qualifierKind2,
QualifierKind glbKind) {
if (qualifierKind1 == qualifierKind2) {
List<String> a1Values = valuesStringList(a1);
List<String> a2Values = valuesStringList(a2);
LinkedHashSet<String> set = new LinkedHashSet<>(a1Values);
set.addAll(a2Values);
return createAnnotationMirrorWithValue(glbKind, set);
} else if (glbKind == qualifierKind1) {
return a1;
} else if (glbKind == qualifierKind2) {
return a2;
} else {
throw new BugInCF("Unexpected QualifierKinds %s %s", qualifierKind1, qualifierKind2, glbKind);
}
}
/**
* Returns a mutable list containing the {@code values} element of the given annotation. The
* {@code values} element must be an array of strings.
*
* @param anno an annotation
* @return a mutable list containing the {@code values} element; may be the empty list
*/
private List<String> valuesStringList(AnnotationMirror anno) {
@SuppressWarnings("deprecation") // concrete annotation class is not known
List<String> result = AnnotationUtils.getElementValueArray(anno, "value", String.class, true);
return result;
}
/**
* Returns an AnnotationMirror corresponding to the given kind and values.
*
* @param kind the qualifier kind
* @param values the annotation's {@code values} element/argument
* @return an annotation of the given kind and values
*/
private AnnotationMirror createAnnotationMirrorWithValue(
QualifierKind kind, LinkedHashSet<String> values) {
AnnotationBuilder builder = new AnnotationBuilder(processingEnv, kind.getAnnotationClass());
builder.setValue("value", values.toArray());
return builder.build();
}
}