package annotations.el; import java.util.LinkedHashSet; import java.util.Set; import annotations.Annotation; import annotations.field.AnnotationAFT; import annotations.field.AnnotationFieldType; /*>>> import org.checkerframework.checker.nullness.qual.*; */ /** * A DefCollector supplies a visitor for the annotation definitions in an * AScene. First, call the DefCollector constructor passing the AScene. * Then, call the visit method. * This class exists primarily for the benefit of * {@link annotations.io.IndexFileWriter#write(AScene, Writer)}. */ public abstract class DefCollector { // The set of all definitions in the Scene, which the visitor iterates // over. private final Set<AnnotationDef> defs; /** * Constructs a new {@link DefCollector}, which immediately collects all * the definitions from annotations the given scene. Next call * {@link #visit} to have the definitions passed back to you in topological * order. If the scene contains two irreconcilable definitions of the * same annotation type, a {@link DefException} is thrown. */ public DefCollector(AScene s) throws DefException { defs = new LinkedHashSet<AnnotationDef>(); collect(s); } // The name "collect" in the methods below means to insert or add to // the the DefCollector. "Insert" or "add" would have been better, but // at least the methods are private. private AnnotationDef getDef(String name) { for (AnnotationDef def : defs) { if (def.name.equals(name)) { return def; } } return null; } private void collect(AScene s) throws DefException { for (AElement p : s.packages.values()) { collect(p); } for (AClass c : s.classes.values()) { collect(c); } } private void addToDefs(AnnotationDef d) throws DefException { // TODO: this mimics the condition we have in collect, but // i don't know if we need it if (defs.contains(d)) { return; } AnnotationDef oldD = getDef(d.name); if (oldD == null) { defs.add(d); } else { AnnotationDef ud = AnnotationDef.unify(oldD, d); if (ud == null) { throw new DefException(d.name); } defs.remove(oldD); defs.add(ud); } } private void collect(AnnotationDef d) throws DefException { if (defs.contains(d)) { return; } // define the fields first for (AnnotationFieldType aft : d.fieldTypes.values()) { if (aft instanceof AnnotationAFT) { collect(((AnnotationAFT) aft).annotationDef); } } addToDefs(d); // TODO: In the future we want to add the defs of meta-annotations // as well. Enable this option by uncommenting the following line. // // For the time-being, the parser would fail, because of possible // circular references (e.g. Documented and Retention). When it is // fixed, uncomment it // // collect((AElement)d); } private void collect(AElement e) throws DefException { for (Annotation tla : e.tlAnnotationsHere) { AnnotationDef tld = tla.def; if (defs.contains(tld)) { continue; } AnnotationDef d = tld; collect(d); addToDefs(d); } if (e.type != null) { collect(e.type); } } private void collect(ATypeElement e) throws DefException { collect((AElement) e); for (AElement it : e.innerTypes.values()) { collect(it); } } private void collect(ADeclaration d) throws DefException { collect((AElement) d); for (ATypeElement ia : d.insertAnnotations.values()) { collect(ia); } for (ATypeElementWithType ic : d.insertTypecasts.values()) { collect(ic); } } private void collect(AField f) throws DefException { collect((ADeclaration) f); } private void collect(AMethod m) throws DefException { for (ATypeElement b : m.bounds.values()) { collect(b); } collect((ADeclaration) m); collect((ATypeElement) m.returnType); collect(m.receiver); for (AElement p : m.parameters.values()) { collect(p); } for (AField l : m.body.locals.values()) { collect(l); } for (ATypeElement tc : m.body.typecasts.values()) { collect(tc); } for (ATypeElement i : m.body.instanceofs.values()) { collect(i); } for (ATypeElement n : m.body.news.values()) { collect(n); } } private void collect(AClass c) throws DefException { collect((ADeclaration) c); for (ATypeElement b : c.bounds.values()) { collect(b); } for (ATypeElement ei : c.extendsImplements.values()) { collect(ei); } for (AMethod m : c.methods.values()) { collect(m); } for (AField f : c.fields.values()) { collect(f); } } /** * Override this method to perform some sort of subclass-specific * processing on the given {@link AnnotationDef}. */ protected abstract void visitAnnotationDef(AnnotationDef d); /** * Calls {@link #visitAnnotationDef} on the definitions collected from * the scene that was passed to the constructor. Visiting is done in * topological order: if the definition of <code>A</code> contains a * subannotation of type <code>B</code>, then <code>B</code> is * guaranteed to be visited before <code>A</code>. */ public final void visit() { for (AnnotationDef d : defs) { visitAnnotationDef(d); } } }