• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package annotations.el;
2 
3 import java.util.LinkedHashMap;
4 import java.util.LinkedHashSet;
5 import java.util.Map;
6 import java.util.Set;
7 
8 import annotations.Annotation;
9 import annotations.io.IndexFileParser;
10 import annotations.util.coll.VivifyingMap;
11 
12 /*>>>
13 import org.checkerframework.checker.nullness.qual.Nullable;
14 */
15 
16 /**
17  * An <code>AScene</code> (annotated scene) represents the annotations on a
18  * set of Java classes and packages along with the definitions of some or all of
19  * the annotation types used.
20  *
21  * <p>
22  * Each client of the annotation library may wish to use its own representation
23  * for certain kinds of annotations instead of a simple name-value map; thus, a
24  * layer of abstraction in the storage of annotations was introduced.
25  *
26  * <p>
27  * <code>AScene</code>s and many {@link AElement}s can contain other
28  * {@link AElement}s. When these objects are created, their collections of
29  * subelements are empty. In order to associate an annotation with a particular
30  * Java element in an <code>AScene</code>, one must first ensure that an
31  * appropriate {@link AElement} exists in the <code>AScene</code>. To this
32  * end, the maps of subelements have a <code>vivify</code> method. Calling
33  * <code>vivify</code> to access a particular subelement will return the
34  * subelement if it already exists; otherwise it will create and then return the
35  * subelement. (Compare to vivification in Perl.) For example, the following
36  * code will obtain an {@link AMethod} representing <code>Foo.bar</code> in
37  * the <code>AScene</code> <code>s</code>, creating it if it did not
38  * already exist:
39  *
40  * <pre>
41  * AMethod&lt;A&gt; m = s.classes.vivify("Foo").methods.vivify("bar");
42  * </pre>
43  *
44  * <p>
45  * Then one can add an annotation to the method:
46  *
47  * <pre>
48  * m.annotationsHere.add(new Annotation(
49  *     new AnnotationDef(taintedDef, RetentionPolicy.RUNTIME, true),
50  *     new Annotation(taintedDef, Collections.emptyMap())
51  * ));
52  * </pre>
53  */
54 public final class AScene implements Cloneable {
55     private static boolean checkClones = true;
56     public static boolean debugFoundMap = false;
57 
58     /** This scene's annotated packages; map key is package name */
59     public final VivifyingMap<String, AElement> packages =
60             AElement.<String>newVivifyingLHMap_AE();
61 
62     /**
63      * Contains for each annotation type a set of imports to be added to
64      *  the source if the annotation is inserted with the "abbreviate"
65      *  option on.
66      */
67     public final Map<String, Set<String>> imports =
68         new LinkedHashMap<String, Set<String>>();
69 
70     /** This scene's annotated classes; map key is class name */
71     public final VivifyingMap<String, AClass> classes =
72             new VivifyingMap<String, AClass>(
73                     new LinkedHashMap<String, AClass>()) {
74                 @Override
75                 public  AClass createValueFor(
76                  String k) {
77                     return new AClass(k);
78                 }
79 
80                 @Override
81                 public boolean subPrune(AClass v) {
82                     return v.prune();
83                 }
84             };
85 
86     /**
87      * Creates a new {@link AScene} with no classes or packages.
88      */
AScene()89     public AScene() {
90     }
91 
92     /**
93      * Copy constructor for {@link AScene}.
94      */
AScene(AScene scene)95     public AScene(AScene scene) {
96         for (String key : scene.packages.keySet()) {
97             AElement val = scene.packages.get(key);
98             packages.put(key, val.clone());
99         }
100         for (String key : scene.imports.keySet()) {
101             // copy could in principle have different Set implementation
102             Set<String> value = scene.imports.get(key);
103             Set<String> copy = new LinkedHashSet<String>();
104             copy.addAll(value);
105             imports.put(key, copy);
106         }
107         for (String key : scene.classes.keySet()) {
108             AClass clazz = scene.classes.get(key);
109             classes.put(key, clazz.clone());
110         }
111         if (checkClones) {
112             checkClone(this, scene);
113         }
114     }
115 
116     @Override
clone()117     public AScene clone() {
118         return new AScene(this);
119     }
120 
121     /**
122      * Returns whether this {@link AScene} equals <code>o</code>; the
123      * commentary and the cautionary remarks on {@link AElement#equals(Object)}
124      * also apply to {@link AScene#equals(Object)}.
125      */
126     @Override
equals(Object o)127     public boolean equals(Object o) {
128         return o instanceof AScene
129             && ((AScene) o).equals(this);
130     }
131 
132     /**
133      * Returns whether this {@link AScene} equals <code>o</code>; a
134      * slightly faster variant of {@link #equals(Object)} for when the argument
135      * is statically known to be another nonnull {@link AScene}.
136      */
equals(AScene o)137     public boolean equals(AScene o) {
138         return o.classes.equals(classes) && o.packages.equals(packages);
139     }
140 
141     /**
142      * {@inheritDoc}
143      */
144     @Override
hashCode()145     public int hashCode() {
146         return classes.hashCode() + packages.hashCode();
147     }
148 
149     /**
150      * Removes empty subelements of this {@link AScene} depth-first; returns
151      * whether this {@link AScene} is itself empty after pruning.
152      */
prune()153     public boolean prune() {
154         return classes.prune() & packages.prune();
155     }
156 
157     /** Returns a string representation. */
unparse()158     public String unparse() {
159         StringBuilder sb = new StringBuilder();
160         sb.append("packages:\n");
161         for (Map.Entry<String, AElement> entry : packages.entrySet()) {
162             sb.append("  " + entry.getKey() + " => " + entry.getValue() + "\n");
163         }
164         sb.append("classes:\n");
165         for (Map.Entry<String, AClass> entry : classes.entrySet()) {
166             sb.append("  " + entry.getKey() + " => " + "\n");
167             sb.append(entry.getValue().unparse("    "));
168         }
169         return sb.toString();
170     }
171 
172     @Override
toString()173     public String toString() {
174         return unparse();
175     }
176 
177     /**
178      * Throws exception if the arguments 1) are the same reference;
179      * 2) are not equal() in both directions; or 3) contain
180      * corresponding elements that meet either of the preceding two
181      * conditions.
182      */
checkClone(AScene s0, AScene s1)183     public static void checkClone(AScene s0, AScene s1) {
184         if (s0 == null) {
185             if (s1 != null) {
186                 cloneCheckFail();
187             }
188         } else {
189             if (s1 == null) {
190                 cloneCheckFail();
191             }
192             s0.prune();
193             s1.prune();
194             if (s0 == s1) {
195                 cloneCheckFail();
196             }
197             checkElems(s0.packages, s1.packages);
198             checkElems(s0.classes, s1.classes);
199         }
200     }
201 
202     public static <K, V extends AElement> void
checkElems(VivifyingMap<K, V> m0, VivifyingMap<K, V> m1)203     checkElems(VivifyingMap<K, V> m0, VivifyingMap<K, V> m1) {
204         if (m0 == null) {
205             if (m1 != null) {
206                 cloneCheckFail();
207             }
208         } else if (m1 == null) {
209             cloneCheckFail();
210         } else {
211             for (K k : m0.keySet()) {
212                 checkElem(m0.get(k), m1.get(k));
213             }
214         }
215     }
216 
217     /**
218      * Throw exception on visit if e0 == e1 or !e0.equals(e1).
219      * (See {@link #checkClone(AScene, AScene)} for explanation.)
220      */
checkElem(AElement e0, AElement e1)221     public static void checkElem(AElement e0, AElement e1) {
222         checkObject(e0, e1);
223         if (e0 != null) {
224             if (e0 == e1) {
225                 cloneCheckFail();
226             }
227             e0.accept(checkVisitor, e1);
228         }
229     }
230 
231     /**
232      * Throw exception on visit if !el.equals(arg) or !arg.equals(el).
233      * (See {@link #checkClone(AScene, AScene)} for explanation.)
234      */
checkObject(Object o0, Object o1)235     public static void checkObject(Object o0, Object o1) {
236         if (o0 == null ? o1 != null
237                 : !(o0.equals(o1) && o1.equals(o0))) {  // ok if ==
238             throw new RuntimeException("clone check failed");
239         }
240     }
241 
242     /**
243      * Throw exception on visit if el == arg or !el.equals(arg).
244      * (See {@link checkClone(AScene, AScene)} for explanation.)
245      */
246     private static ElementVisitor<Void, AElement> checkVisitor =
247         new ElementVisitor<Void, AElement>() {
248             @Override
249             public Void visitAnnotationDef(AnnotationDef el,
250                     AElement arg) {
251                 return null;
252             }
253 
254             @Override
255             public Void visitBlock(ABlock el, AElement arg) {
256                 ABlock b = (ABlock) arg;
257                 checkElems(el.locals, b.locals);
258                 return null;
259             }
260 
261             @Override
262             public Void visitClass(AClass el, AElement arg) {
263                 AClass c = (AClass) arg;
264                 checkElems(el.bounds, c.bounds);
265                 checkElems(el.extendsImplements, c.extendsImplements);
266                 checkElems(el.fieldInits, c.fieldInits);
267                 checkElems(el.fields, c.fields);
268                 checkElems(el.instanceInits, c.instanceInits);
269                 checkElems(el.methods, c.methods);
270                 checkElems(el.staticInits, c.staticInits);
271                 return visitDeclaration(el, arg);
272             }
273 
274             @Override
275             public Void visitDeclaration(ADeclaration el, AElement arg) {
276                 ADeclaration d = (ADeclaration) arg;
277                 checkElems(el.insertAnnotations, d.insertAnnotations);
278                 checkElems(el.insertTypecasts, d.insertTypecasts);
279                 return visitElement(el, arg);
280             }
281 
282             @Override
283             public Void visitExpression(AExpression el, AElement arg) {
284                 AExpression e = (AExpression) arg;
285                 checkObject(el.id, e.id);
286                 checkElems(el.calls, e.calls);
287                 checkElems(el.funs, e.funs);
288                 checkElems(el.instanceofs, e.instanceofs);
289                 checkElems(el.news, e.news);
290                 checkElems(el.refs, e.refs);
291                 checkElems(el.typecasts, e.typecasts);
292                 return visitElement(el, arg);
293             }
294 
295             @Override
296             public Void visitField(AField el, AElement arg) {
297                 AField f = (AField) arg;
298                 checkElem(el.init, f.init);
299                 return visitDeclaration(el, arg);
300             }
301 
302             @Override
303             public Void visitMethod(AMethod el, AElement arg) {
304                 AMethod m = (AMethod) arg;
305                 checkObject(el.methodName, m.methodName);
306                 checkElem(el.body, m.body);
307                 checkElem(el.returnType, m.returnType);
308                 checkElems(el.bounds, m.bounds);
309                 checkElems(el.parameters, m.parameters);
310                 checkElems(el.throwsException, m.throwsException);
311                 return null;
312             }
313 
314             @Override
315             public Void visitTypeElement(ATypeElement el, AElement arg) {
316                 ATypeElement t = (ATypeElement) arg;
317                 checkObject(el.description, t.description);
318                 checkElems(el.innerTypes, t.innerTypes);
319                 return null;
320             }
321 
322             @Override
323             public Void visitTypeElementWithType(ATypeElementWithType el,
324                     AElement arg) {
325                 ATypeElementWithType t = (ATypeElementWithType) arg;
326                 checkObject(el.getType(), t.getType());
327                 return visitTypeElement(el, arg);
328             }
329 
330             @Override
331             public Void visitElement(AElement el, AElement arg) {
332                 checkObject(el.description, arg.description);
333                 if (el.tlAnnotationsHere.size() !=
334                         arg.tlAnnotationsHere.size()) {
335                     cloneCheckFail();
336                 }
337                 for (Annotation a : el.tlAnnotationsHere) {
338                     if (!arg.tlAnnotationsHere.contains(a)) {
339                         cloneCheckFail();
340                     }
341                 }
342                 checkElem(el.type, arg.type);
343                 return null;
344             }
345         };
346 
cloneCheckFail()347     private static void cloneCheckFail() {
348         throw new RuntimeException("clone check failed");
349     }
350 
351     // temporary main for easy testing on JAIFs
main(String[] args)352     public static void main(String[] args) {
353         int status = 0;
354         checkClones = true;
355 
356         for (int i = 0; i < args.length; i++) {
357             AScene s0 = new AScene();
358             System.out.print(args[i] + ": ");
359             try {
360                 IndexFileParser.parseFile(args[i], s0);
361                 s0.clone();
362                 System.out.println("ok");
363             } catch (Throwable e) {
364                 status = 1;
365                 System.out.println("failed");
366                 e.printStackTrace();
367             }
368         }
369         System.exit(status);
370     }
371 }
372