package org.checkerframework.checker.fenum;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.checker.fenum.qual.Fenum;
import org.checkerframework.checker.fenum.qual.FenumBottom;
import org.checkerframework.checker.fenum.qual.FenumTop;
import org.checkerframework.checker.fenum.qual.FenumUnqualified;
import org.checkerframework.checker.fenum.qual.PolyFenum;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.framework.type.AnnotationClassLoader;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.util.GraphQualifierHierarchy;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory;
import org.checkerframework.javacutil.AnnotationUtils;
public class FenumAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
protected AnnotationMirror FENUM_UNQUALIFIED;
protected AnnotationMirror FENUM, FENUM_BOTTOM;
public FenumAnnotatedTypeFactory(BaseTypeChecker checker) {
super(checker);
FENUM_BOTTOM = AnnotationUtils.fromClass(elements, FenumBottom.class);
FENUM = AnnotationUtils.fromClass(elements, Fenum.class);
FENUM_UNQUALIFIED = AnnotationUtils.fromClass(elements, FenumUnqualified.class);
this.postInit();
}
/**
* Copied from SubtypingChecker. Instead of returning an empty set if no "quals" option is
* given, we return Fenum as the only qualifier.
*/
@Override
protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() {
AnnotationClassLoader loader = new AnnotationClassLoader(checker);
Set<Class<? extends Annotation>> qualSet = new LinkedHashSet<Class<? extends Annotation>>();
// Load externally defined quals given in the -Aquals and/or -AqualDirs options
String qualNames = checker.getOption("quals");
String qualDirectories = checker.getOption("qualDirs");
// load individually named qualifiers
if (qualNames != null) {
for (String qualName : qualNames.split(",")) {
qualSet.add(loader.loadExternalAnnotationClass(qualName));
}
}
// load directories of qualifiers
if (qualDirectories != null) {
for (String dirName : qualDirectories.split(":")) {
qualSet.addAll(loader.loadExternalAnnotationClassesFromDirectory(dirName));
}
}
// Load top, bottom, unqualified, and fake enum
qualSet.add(FenumTop.class);
qualSet.add(Fenum.class);
qualSet.add(FenumUnqualified.class);
qualSet.add(FenumBottom.class);
qualSet.add(PolyFenum.class);
// Also call super to load everything in qual directory
qualSet.addAll(getBundledTypeQualifiersWithPolyAll());
// TODO: warn if no qualifiers given?
// Just Fenum("..") is still valid, though...
return qualSet;
}
@Override
public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) {
return new FenumQualifierHierarchy(factory);
}
protected class FenumQualifierHierarchy extends GraphQualifierHierarchy {
/* The user is expected to introduce additional fenum annotations.
* These annotations are declared to be subtypes of FenumTop, using the
* @SubtypeOf annotation.
* However, there is no way to declare that it is a supertype of Bottom.
* Therefore, we use the constructor of GraphQualifierHierarchy that allows
* us to set a dedicated bottom qualifier.
*/
public FenumQualifierHierarchy(MultiGraphFactory factory) {
super(factory, FENUM_BOTTOM);
}
@Override
public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) {
if (AnnotationUtils.areSameIgnoringValues(superAnno, FENUM)
&& AnnotationUtils.areSameIgnoringValues(subAnno, FENUM)) {
return AnnotationUtils.areSame(superAnno, subAnno);
}
// Ignore annotation values to ensure that annotation is in supertype map.
if (AnnotationUtils.areSameIgnoringValues(superAnno, FENUM)) {
superAnno = FENUM;
}
if (AnnotationUtils.areSameIgnoringValues(subAnno, FENUM)) {
subAnno = FENUM;
}
return super.isSubtype(subAnno, superAnno);
}
}
}