| 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); |
| } |
| } |
| } |