| 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 subset 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 SubtypeIsSubsetQualifierHierarchy extends MostlyNoElementQualifierHierarchy { |
| |
| /** The processing environment; used for creating annotations. */ |
| ProcessingEnvironment processingEnv; |
| |
| /** |
| * Creates a SubtypeIsSubsetQualifierHierarchy from the given classes. |
| * |
| * @param qualifierClasses classes of annotations that are the qualifiers for this hierarchy |
| * @param processingEnv processing environment |
| */ |
| public SubtypeIsSubsetQualifierHierarchy( |
| 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 superValues.containsAll(subValues); |
| } |
| 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.addAll(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.retainAll(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(); |
| } |
| } |