package checkers.types; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Name; import checkers.util.AnnotationUtils; /** * Represents a type qualifier hierarchy. * * All method parameter annotations need to be type qualifiers recognized * within this hierarchy. * * This assumes that any particular annotated type in a program is annotated * with at most one qualifier from the hierarchy. */ public abstract class QualifierHierarchy { // ********************************************************************** // Getter methods about this hierarchy // ********************************************************************** /** * @return the root (ultimate super) type qualifier in the hierarchy */ public abstract AnnotationMirror getRootAnnotation(); /** * @return the botton type qualifier in the hierarchy */ public abstract AnnotationMirror getBottomQualifier(); /** * Returns the names of all type qualifiers in this type qualifier * hierarchy * * @return the fully qualified name represented in this hierarchy */ public abstract Set<Name> getTypeQualifiers(); // ********************************************************************** // Qualifier Hierarchy Queries // ********************************************************************** /** * Tests whether anno1 is a super qualifier of anno2, according to the * type qualifier hierarchy. This checks only the qualifiers, not the * Java type. Either argument may be "null", if no type qualifier from * the given hierarchy is present. * * @return true iff anno1 is a sub qualifier of anno2 */ public abstract boolean isSubtype(AnnotationMirror anno1, AnnotationMirror anno2); /** * Tests whether there is any annotation in lhs that is a super qualifier * of some annotation in rhs. * lhs and rhs contain only the annotations, not the Java type. * * @return true iff an annotation in lhs is a super of one in rhs **/ // This method requires more revision // The only case were rhs and lhs have more than one qualifier is in IGJ // where the type of 'this' is '@AssignsFields @I FOO'. Subtyping for // this case, requires subtyping with respect to one qualifier only public boolean isSubtype(Collection<AnnotationMirror> rhs, Collection<AnnotationMirror> lhs) { Collection<AnnotationMirror> lhsAnnos = wrapCollection(lhs); Collection<AnnotationMirror> rhsAnnos = wrapCollection(rhs); for (AnnotationMirror lhsAnno : lhsAnnos) for (AnnotationMirror rhsAnno : rhsAnnos) if (isSubtype(rhsAnno, lhsAnno)) return true; return false; } /** * Returns the least upper bound for a1 and a2 qualifiers. * * Examples: * For NonNull, leastUpperBound('Nullable', 'NonNull') ==> Nullable * For IGJ, leastUpperBound('Immutable', 'Mutable') ==> ReadOnly * * @return the least restrictive qualifiers for both types */ public abstract AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2); /** * Returns the type qualifiers that are the least upper bound of * the qualifiers in annos1 and annos2. * <p> * * This is necessary for determining the type of a conditional * expression (<tt>?:</tt>), where the type of the expression is the * least upper bound of the true and false clauses. * <p> * * The current implementation returns the intersection of annos1 and * annos2 along with the qualifier that is the least upper bound of all * other annotations. * * @return the least upper bound of annos1 and annos2 */ public Set<AnnotationMirror> leastUpperBound(Collection<AnnotationMirror> annos1, Collection<AnnotationMirror> annos2) { Collection<AnnotationMirror> as1 = wrapCollection(annos1); Collection<AnnotationMirror> as2 = wrapCollection(annos2); if (as1.size() == 1 && as2.size() == 1) { AnnotationMirror a1 = as1.iterator().next(); AnnotationMirror a2 = as2.iterator().next(); return Collections.singleton(leastUpperBound(a1, a2)); } // // Let's hope that the difference is simply two elements Set<AnnotationMirror> difference = difference(as1, as2); Set<AnnotationMirror> lub = AnnotationUtils.createAnnotationSet(); lub.addAll(intersect(as1, as2)); if (difference.isEmpty()) return lub; AnnotationMirror lubOfDiff = difference.iterator().next(); for (AnnotationMirror a : difference) lubOfDiff = leastUpperBound(lubOfDiff, a); lub.add(lubOfDiff); return lub; } // ********************************************************************** // Helper methods to extract annotations // ********************************************************************** /** * Returns a subset of the input that contains any qualifiers in this * hierarchy * * <p> * * Annotated types only contain the supported qualifiers by the hierarchy * currently. There is no need to call this method. * * @return the qualifiers in annos within this hierarchy */ @Deprecated public List<AnnotationMirror> validQualifiers(Collection<AnnotationMirror> annos) { List<AnnotationMirror> results = new ArrayList<AnnotationMirror>(); Set<Name> typeQualifiers = getTypeQualifiers(); for (AnnotationMirror anno : annos) { if (typeQualifiers.contains(AnnotationUtils.annotationName(anno))) results.add(anno); } return results; } /** * Finds the first type qualifiers in this hierarchy in the given list of * qualifiers. * * <p> * * Annotated types only contain the supported qualifiers by the hierarchy * currently. There is no need to call this method. * * @return the qualifiers in annos in this hierarchy */ @Deprecated public AnnotationMirror validQualifier(Collection<AnnotationMirror> annos) { List<AnnotationMirror> validQualifiers = validQualifiers(annos); return validQualifiers.isEmpty() ? null : validQualifiers.get(0); } // ********************************************************************** // Helper methods to extract annotations // ********************************************************************** /** * Returns a non-null, non-empty collection of annotations. * If the argument is non-empty, returns the argument. * Otherwise, returns a collection containing only {@code null}, because * a null value of type AnnotationMirror is treated as an unqualified type. * * @return annos if not empty, otherwise a singleton whose element is {@code null} */ protected Collection<AnnotationMirror> wrapCollection(Collection<AnnotationMirror> annos) { if (annos.size() == 0) return Collections.singleton(null); return annos; } /** * @return the intersection set of as1 and as2 */ protected Set<AnnotationMirror> intersect(Collection<AnnotationMirror> as1, Collection<AnnotationMirror> as2) { Set<AnnotationMirror> intersects = AnnotationUtils.createAnnotationSet(); intersects.addAll(as1); intersects.retainAll(as2); return intersects; } /** * @return the elements belonging to exactly one of as1 or as2 */ protected Set<AnnotationMirror> difference(Collection<AnnotationMirror> as1, Collection<AnnotationMirror> as2) { Set<AnnotationMirror> difference = AnnotationUtils.createAnnotationSet(); difference.addAll(as1); difference.addAll(as2); difference.removeAll(intersect(as1, as2)); return difference; } }