1 package annotations.el; 2 3 import java.util.LinkedHashSet; 4 import java.util.Set; 5 6 import annotations.Annotation; 7 import annotations.field.AnnotationAFT; 8 import annotations.field.AnnotationFieldType; 9 10 /*>>> 11 import org.checkerframework.checker.nullness.qual.*; 12 */ 13 14 /** 15 * A DefCollector supplies a visitor for the annotation definitions in an 16 * AScene. First, call the DefCollector constructor passing the AScene. 17 * Then, call the visit method. 18 * This class exists primarily for the benefit of 19 * {@link annotations.io.IndexFileWriter#write(AScene, Writer)}. 20 */ 21 public abstract class DefCollector { 22 23 // The set of all definitions in the Scene, which the visitor iterates 24 // over. 25 private final Set<AnnotationDef> defs; 26 27 /** 28 * Constructs a new {@link DefCollector}, which immediately collects all 29 * the definitions from annotations the given scene. Next call 30 * {@link #visit} to have the definitions passed back to you in topological 31 * order. If the scene contains two irreconcilable definitions of the 32 * same annotation type, a {@link DefException} is thrown. 33 */ DefCollector(AScene s)34 public DefCollector(AScene s) 35 throws DefException { 36 defs = new LinkedHashSet<AnnotationDef>(); 37 collect(s); 38 } 39 40 // The name "collect" in the methods below means to insert or add to 41 // the the DefCollector. "Insert" or "add" would have been better, but 42 // at least the methods are private. 43 getDef(String name)44 private AnnotationDef getDef(String name) { 45 for (AnnotationDef def : defs) { 46 if (def.name.equals(name)) { 47 return def; 48 } 49 } 50 return null; 51 } 52 53 collect(AScene s)54 private void collect(AScene s) 55 throws DefException { 56 for (AElement p : s.packages.values()) { 57 collect(p); 58 } 59 for (AClass c : s.classes.values()) { 60 collect(c); 61 } 62 } 63 addToDefs(AnnotationDef d)64 private void addToDefs(AnnotationDef d) throws DefException { 65 // TODO: this mimics the condition we have in collect, but 66 // i don't know if we need it 67 if (defs.contains(d)) { 68 return; 69 } 70 AnnotationDef oldD = getDef(d.name); 71 if (oldD == null) { 72 defs.add(d); 73 } else { 74 AnnotationDef ud = AnnotationDef.unify(oldD, d); 75 if (ud == null) { 76 throw new DefException(d.name); 77 } 78 defs.remove(oldD); 79 defs.add(ud); 80 } 81 } 82 collect(AnnotationDef d)83 private void collect(AnnotationDef d) throws DefException { 84 if (defs.contains(d)) { 85 return; 86 } 87 88 // define the fields first 89 for (AnnotationFieldType aft : d.fieldTypes.values()) { 90 if (aft instanceof AnnotationAFT) { 91 collect(((AnnotationAFT) aft).annotationDef); 92 } 93 } 94 95 addToDefs(d); 96 97 // TODO: In the future we want to add the defs of meta-annotations 98 // as well. Enable this option by uncommenting the following line. 99 // 100 // For the time-being, the parser would fail, because of possible 101 // circular references (e.g. Documented and Retention). When it is 102 // fixed, uncomment it 103 // 104 // collect((AElement)d); 105 } 106 collect(AElement e)107 private void collect(AElement e) 108 throws DefException { 109 for (Annotation tla : e.tlAnnotationsHere) { 110 AnnotationDef tld = tla.def; 111 if (defs.contains(tld)) { 112 continue; 113 } 114 115 AnnotationDef d = tld; 116 collect(d); 117 118 addToDefs(d); 119 } 120 if (e.type != null) { 121 collect(e.type); 122 } 123 124 } 125 collect(ATypeElement e)126 private void collect(ATypeElement e) 127 throws DefException { 128 collect((AElement) e); 129 for (AElement it : e.innerTypes.values()) { 130 collect(it); 131 } 132 } 133 collect(ADeclaration d)134 private void collect(ADeclaration d) 135 throws DefException { 136 collect((AElement) d); 137 for (ATypeElement ia : d.insertAnnotations.values()) { 138 collect(ia); 139 } 140 for (ATypeElementWithType ic : d.insertTypecasts.values()) { 141 collect(ic); 142 } 143 } 144 collect(AField f)145 private void collect(AField f) 146 throws DefException { 147 collect((ADeclaration) f); 148 } 149 collect(AMethod m)150 private void collect(AMethod m) 151 throws DefException { 152 for (ATypeElement b : m.bounds.values()) { 153 collect(b); 154 } 155 collect((ADeclaration) m); 156 collect((ATypeElement) m.returnType); 157 collect(m.receiver); 158 for (AElement p : m.parameters.values()) { 159 collect(p); 160 } 161 for (AField l : m.body.locals.values()) { 162 collect(l); 163 } 164 for (ATypeElement tc : m.body.typecasts.values()) { 165 collect(tc); 166 } 167 for (ATypeElement i : m.body.instanceofs.values()) { 168 collect(i); 169 } 170 for (ATypeElement n : m.body.news.values()) { 171 collect(n); 172 } 173 } 174 collect(AClass c)175 private void collect(AClass c) 176 throws DefException { 177 collect((ADeclaration) c); 178 for (ATypeElement b : c.bounds.values()) { 179 collect(b); 180 } 181 for (ATypeElement ei : c.extendsImplements.values()) { 182 collect(ei); 183 } 184 for (AMethod m : c.methods.values()) { 185 collect(m); 186 } 187 for (AField f : c.fields.values()) { 188 collect(f); 189 } 190 } 191 192 /** 193 * Override this method to perform some sort of subclass-specific 194 * processing on the given {@link AnnotationDef}. 195 */ visitAnnotationDef(AnnotationDef d)196 protected abstract void visitAnnotationDef(AnnotationDef d); 197 198 /** 199 * Calls {@link #visitAnnotationDef} on the definitions collected from 200 * the scene that was passed to the constructor. Visiting is done in 201 * topological order: if the definition of <code>A</code> contains a 202 * subannotation of type <code>B</code>, then <code>B</code> is 203 * guaranteed to be visited before <code>A</code>. 204 */ visit()205 public final void visit() { 206 for (AnnotationDef d : defs) { 207 visitAnnotationDef(d); 208 } 209 } 210 } 211