1 package annotations.util;
2 
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.io.Writer;
6 import java.util.Map;
7 import java.util.Set;
8 
9 import com.sun.tools.javac.util.Pair;
10 
11 import annotations.el.ABlock;
12 import annotations.el.AClass;
13 import annotations.el.ADeclaration;
14 import annotations.el.AElement;
15 import annotations.el.AExpression;
16 import annotations.el.AField;
17 import annotations.el.AMethod;
18 import annotations.el.AScene;
19 import annotations.el.ATypeElement;
20 import annotations.el.ATypeElementWithType;
21 import annotations.el.AnnotationDef;
22 import annotations.el.DefException;
23 import annotations.el.ElementVisitor;
24 import annotations.io.IndexFileParser;
25 import annotations.io.IndexFileWriter;
26 import annotations.util.coll.VivifyingMap;
27 
28 /**
29  * Algebraic operations on scenes.
30  *
31  * Also includes a {@link #main(String[])} method that lets these
32  * operations be performed from the command line.
33  *
34  * @author dbro
35  */
36 public class SceneOps {
37   private SceneOps() {}
38 
39   /**
40    * Run an operation on a subcommand-specific number of JAIFs.
41    * Currently the only available subcommand is "diff", which must be
42    * the first of three arguments, followed in order by the "minuend"
43    * and the "subtrahend" (see {@link #diff(AScene, AScene)}).  If
44    * successful, the diff subcommand writes the scene it calculates to
45    * {@link System#out}.
46    *
47    * @throws IOException
48    */
49   public static void main(String[] args) throws IOException {
50     if (args.length != 3 || !"diff".equals(args[0])) {
51       System.err.println(
52           "usage: java annotations.util.SceneOps diff first.jaif second.jaif");
53       System.exit(1);
54     }
55 
56     AScene s1 = new AScene();
57     AScene s2 = new AScene();
58 
59     try {
60       IndexFileParser.parseFile(args[1], s1);
61       IndexFileParser.parseFile(args[2], s2);
62       AScene diff = diff(s1, s2);
63 
64       try (Writer w = new PrintWriter(System.out)) {
65         IndexFileWriter.write(diff, w);
66       } catch (DefException e) {
67         exitWithException(e);
68       }
69     } catch (IOException e) {
70       exitWithException(e);
71     }
72   }
73 
74   /**
75    * Compute the difference of two scenes, that is, a scene containing
76    * all and only those insertion specifications that exist in the first
77    * but not in the second.
78    *
79    * @param s1 the "minuend"
80    * @param s2 the "subtrahend"
81    * @return s1 - s2 ("set difference")
82    */
83   public static AScene diff(AScene s1, AScene s2) {
84     AScene diff = new AScene();
85     new DiffVisitor().visitScene(s1, s2, diff);
86     diff.prune();
87     return diff;
88   }
89 
90   /** Print stack trace (for debugging) and exit with return code 1. */
91   private static void exitWithException(Exception e) {
92     e.printStackTrace();
93     System.exit(1);
94   }
95 
96   // TODO: integrate into scene-lib test suite
97   public static void testDiffEmpties() {
98     assert new AScene().equals(diff(new AScene(), new AScene()));
99   }
100   /** Test that X-X=0, for several scenes X. */
101   public static void testDiffSame() throws IOException {
102     String dirname =
103         "test/annotations/tests/classfile/cases";
104     String[] testcases = { "ClassEmpty", "ClassNonEmpty", "FieldGeneric",
105         "FieldSimple", "LocalVariableGenericArray", "MethodReceiver",
106         "MethodReturnTypeGenericArray", "ObjectCreationGenericArray",
107         "ObjectCreation", "TypecastGenericArray", "Typecast" };
108     AScene emptyScene = new AScene();
109     for (String testcase : testcases) {
110       AScene scene1 = new AScene();
111       AScene scene2 = new AScene();
112       String filename = dirname+"/Test"+testcase+".jaif";
113       IndexFileParser.parseFile(filename, scene1);
114       IndexFileParser.parseFile(filename, scene2);
115       assert emptyScene.equals(diff(scene1, scene1));
116       assert emptyScene.equals(diff(scene1, scene2));
117     }
118   }
119 }
120 
121 /**
122  * Visitor for calculating "set difference" of scenes.
123  * Visitor methods fill in a scene instead of returning one because an
124  * {@link AElement} can be created only inside an {@link AScene}.
125  *
126  * @author dbro
127  */
128 class DiffVisitor
129 implements ElementVisitor<Void, Pair<AElement, AElement>> {
130 
131   /**
132    * Adds all annotations that are in {@code minuend} but not in
133    * {@code subtrahend} to {@code difference}.
134    */
135   public void visitScene(AScene minuend, AScene subtrahend,
136       AScene difference) {
137     visitElements(minuend.packages, subtrahend.packages,
138         difference.packages);
139     diff(minuend.imports, subtrahend.imports, difference.imports);
140     visitElements(minuend.classes, subtrahend.classes,
141         difference.classes);
142   }
143 
144   // Never used, as annotations and definitions don't get duplicated.
145   @Override
146   public Void visitAnnotationDef(AnnotationDef minuend,
147       Pair<AElement, AElement> eltPair) {
148     throw new IllegalStateException(
149         "BUG: DiffVisitor.visitAnnotationDef invoked");
150   }
151 
152   /**
153    * Calculates difference between {@code minuend} and first component
154    * of {@code eltPair}, adding results to second component of {@code eltPair}.
155    */
156   @Override
157   public Void visitBlock(ABlock minuend, Pair<AElement, AElement> eltPair) {
158     ABlock subtrahend = (ABlock) eltPair.fst;
159     ABlock difference = (ABlock) eltPair.snd;
160     visitElements(minuend.locals, subtrahend.locals, difference.locals);
161     return visitExpression(minuend, eltPair);
162   }
163 
164   /**
165    * Calculates difference between {@code minuend} and first component
166    * of {@code eltPair}, adding results to second component of {@code eltPair}.
167    */
168   @Override
169   public Void visitClass(AClass minuend, Pair<AElement, AElement> eltPair) {
170     AClass subtrahend = (AClass) eltPair.fst;
171     AClass difference = (AClass) eltPair.snd;
172     visitElements(minuend.bounds, subtrahend.bounds, difference.bounds);
173     visitElements(minuend.extendsImplements,
174         subtrahend.extendsImplements, difference.extendsImplements);
175     visitElements(minuend.methods, subtrahend.methods,
176         difference.methods);
177     visitElements(minuend.staticInits, subtrahend.staticInits,
178         difference.staticInits);
179     visitElements(minuend.instanceInits, subtrahend.instanceInits,
180         difference.instanceInits);
181     visitElements(minuend.fields, subtrahend.fields, difference.fields);
182     visitElements(minuend.fieldInits, subtrahend.fieldInits,
183         difference.fieldInits);
184     return visitDeclaration(minuend, eltPair);
185   }
186 
187   /**
188    * Calculates difference between {@code minuend} and first component
189    * of {@code eltPair}, adding results to second component of {@code eltPair}.
190    */
191   @Override
192   public Void visitDeclaration(ADeclaration minuend,
193       Pair<AElement, AElement> eltPair) {
194     ADeclaration subtrahend = (ADeclaration) eltPair.fst;
195     ADeclaration difference = (ADeclaration) eltPair.snd;
196     visitElements(minuend.insertAnnotations,
197         subtrahend.insertAnnotations, difference.insertAnnotations);
198     visitElements(minuend.insertTypecasts, subtrahend.insertTypecasts,
199         difference.insertTypecasts);
200     return visitElement(minuend, eltPair);
201   }
202 
203   /**
204    * Calculates difference between {@code minuend} and first component
205    * of {@code eltPair}, adding results to second component of {@code eltPair}.
206    */
207   @Override
208   public Void visitExpression(AExpression minuend,
209       Pair<AElement, AElement> eltPair) {
210     AExpression subtrahend = (AExpression) eltPair.fst;
211     AExpression difference = (AExpression) eltPair.snd;
212     visitElements(minuend.typecasts, subtrahend.typecasts,
213         difference.typecasts);
214     visitElements(minuend.instanceofs, subtrahend.instanceofs,
215         difference.instanceofs);
216     visitElements(minuend.news, subtrahend.news, difference.news);
217     visitElements(minuend.calls, subtrahend.calls, difference.calls);
218     visitElements(minuend.refs, subtrahend.refs, difference.refs);
219     visitElements(minuend.funs, subtrahend.funs, difference.funs);
220     return visitElement(minuend, eltPair);
221   }
222 
223   /**
224    * Calculates difference between {@code minuend} and first component
225    * of {@code eltPair}, adding results to second component of {@code eltPair}.
226    */
227   @Override
228   public Void visitField(AField minuend, Pair<AElement, AElement> eltPair) {
229     return visitDeclaration(minuend, eltPair);
230   }
231 
232   /**
233    * Calculates difference between {@code minuend} and first component
234    * of {@code eltPair}, adding results to second component of {@code eltPair}.
235    */
236   @Override
237   public Void visitMethod(AMethod minuend,
238       Pair<AElement, AElement> eltPair) {
239     AMethod subtrahend = (AMethod) eltPair.fst;
240     AMethod difference = (AMethod) eltPair.snd;
241     visitElements(minuend.bounds, subtrahend.bounds, difference.bounds);
242     visitElements(minuend.parameters, subtrahend.parameters,
243         difference.parameters);
244     visitElements(minuend.throwsException, subtrahend.throwsException,
245         difference.throwsException);
246     visitElements(minuend.parameters, subtrahend.parameters,
247         difference.parameters);
248     visitBlock(minuend.body,
249         elemPair(subtrahend.body, difference.body));
250     if (minuend.returnType != null) {
251       minuend.returnType.accept(this,
252           elemPair(subtrahend.returnType, difference.returnType));
253     }
254     if (minuend.receiver != null) {
255       minuend.receiver.accept(this,
256           elemPair(subtrahend.receiver, difference.receiver));
257     }
258     return visitDeclaration(minuend, eltPair);
259   }
260 
261   /**
262    * Calculates difference between {@code minuend} and first component
263    * of {@code eltPair}, adding results to second component of {@code eltPair}.
264    */
265   @Override
266   public Void visitTypeElement(ATypeElement minuend,
267       Pair<AElement, AElement> eltPair) {
268     ATypeElement subtrahend = (ATypeElement) eltPair.fst;
269     ATypeElement difference = (ATypeElement) eltPair.snd;
270     visitElements(minuend.innerTypes, subtrahend.innerTypes,
271         difference.innerTypes);
272     return visitElement(minuend, eltPair);
273   }
274 
275   /**
276    * Calculates difference between {@code minuend} and first component
277    * of {@code eltPair}, adding results to second component of {@code eltPair}.
278    */
279   @Override
280   public Void visitTypeElementWithType(ATypeElementWithType minuend,
281       Pair<AElement, AElement> eltPair) {
282     return visitTypeElement(minuend, eltPair);
283   }
284 
285   /**
286    * Calculates difference between {@code minuend} and first component
287    * of {@code eltPair}, adding results to second component of {@code eltPair}.
288    */
289   @Override
290   public Void visitElement(AElement minuend,
291       Pair<AElement, AElement> eltPair) {
292     AElement subtrahend = eltPair.fst;
293     AElement difference = eltPair.snd;
294     diff(minuend.tlAnnotationsHere, subtrahend.tlAnnotationsHere,
295         difference.tlAnnotationsHere);
296     if (minuend.type != null) {
297       AElement stype = subtrahend.type;
298       AElement dtype = difference.type;
299       minuend.type.accept(this, elemPair(stype, dtype));
300     }
301     return null;
302   }
303 
304   /**
305    * Calculates difference between {@code minuend} and first component
306    * of {@code eltPair}, adding results to second component of {@code eltPair}.
307    */
308   private <K, V extends AElement>
309   void visitElements(VivifyingMap<K, V> minuend,
310       VivifyingMap<K, V> subtrahend, VivifyingMap<K, V> difference) {
311     if (minuend != null) {
312       for (Map.Entry<K, V> e : minuend.entrySet()) {
313         K key = e.getKey();
314         V mval = e.getValue();
315         V sval = subtrahend.get(key);
316         if (sval == null) {
317           difference.put(key, mval);
318         } else {
319           mval.accept(this, elemPair(sval, difference.vivify(key)));
320         }
321       }
322     }
323   }
324 
325   /**
326    * Calculates difference between {@code minuend} and
327    * {@code subtrahend}, adding the result to {@code difference}.
328    */
329   private static <T> void diff(Set<T> minuend, Set<T> subtrahend,
330       Set<T> difference) {
331     if (minuend != null) {
332       for (T t : minuend) {
333         if (!subtrahend.contains(t)) {
334           difference.add(t);
335         }
336       }
337     }
338   }
339 
340   /**
341    * Calculates difference between {@code minuend} and
342    * {@code subtrahend}, adding the results to {@code difference}.
343    */
344   private static <K, V> void diff(Map<K, Set<V>> minuend,
345       Map<K, Set<V>> subtrahend, Map<K, Set<V>> difference) {
346     if (minuend != null) {
347       for (K key : minuend.keySet()) {
348         Set<V> mval = minuend.get(key);
349         Set<V> sval = subtrahend.get(key);
350         if (sval == null) {
351           difference.put(key, mval);
352         } else if (!sval.equals(mval)) {
353           try {
354             @SuppressWarnings("unchecked")
355             Set<V> set = (Set<V>) sval.getClass().newInstance();
356             diff(mval, sval, set);
357             if (!set.isEmpty()) {
358               difference.put(key, set);
359             }
360           } catch (InstantiationException e) {
361             e.printStackTrace();
362             System.exit(1);
363           } catch (IllegalAccessException e) {
364             e.printStackTrace();
365             System.exit(1);
366           }
367         }
368       }
369     }
370   }
371 
372   /**
373    * Convenience method for ensuring returned {@link Pair} is of the
374    * most general type.
375    */
376   private Pair<AElement, AElement> elemPair(AElement stype,
377       AElement dtype) {
378     return Pair.of(stype, dtype);
379   }
380 }
381