001// MultiDiffVisitor.java
002
003package daikon.diff;
004
005import daikon.PptMap;
006import daikon.inv.Invariant;
007import daikon.inv.OutputFormat;
008import java.io.OutputStream;
009import java.io.PrintStream;
010import java.util.ArrayList;
011import java.util.HashMap;
012import java.util.HashSet;
013import java.util.Map;
014import java.util.StringTokenizer;
015import org.checkerframework.checker.nullness.qual.KeyFor;
016import org.checkerframework.checker.nullness.qual.NonNull;
017import org.checkerframework.checker.nullness.qual.Nullable;
018import org.plumelib.util.CollectionsPlume;
019
020/**
021 * <B>MultiDiffVisitor</B> is a state-storing NodeVisitor that works across multiple files
022 * regardless of the current two-file infrastructure. This allows the selection of very unique
023 * invariants that occur once over an entire set of trace files.
024 */
025public class MultiDiffVisitor extends PrintNullDiffVisitor {
026
027  protected PptMap currMap;
028  private HashSet<String> programPointsList;
029  private HashMap<String, Integer> freqList;
030  private HashSet<String> justifiedList;
031  private int total = 0;
032  private static boolean spinfoMode = false;
033  private static PrintStream out = System.out;
034
035  public MultiDiffVisitor(PptMap firstMap) {
036    // I'll always want System.out, and never verbose!
037    super(System.out, false);
038    currMap = firstMap;
039    programPointsList = new HashSet<String>();
040    freqList = new HashMap<>();
041    justifiedList = new HashSet<String>();
042  }
043
044  public static void setForSpinfoOut(OutputStream out_os) {
045    MultiDiffVisitor.out = new PrintStream(out_os, true);
046    spinfoMode = true;
047  }
048
049  @Override
050  public void visit(RootNode node) {
051
052    total++;
053    super.visit(node);
054  }
055
056  @Override
057  public void visit(InvNode node) {
058    Invariant inv1 = node.getInv1();
059    Invariant inv2 = node.getInv2();
060
061    // Use the histogram map
062    if (inv1 != null && shouldPrint(inv1, inv2)) {
063      String tmpStr = inv1.ppt.name();
064      // example:
065      // tmpStr == FeedTheCat.measure(III)I:::ENTER(b, this.bCap
066      String thisPptName = tmpStr.substring(0, tmpStr.lastIndexOf('('));
067
068      programPointsList.add(thisPptName);
069      String key = thisPptName + "$" + inv1.format_using(OutputFormat.JAVA);
070      Integer val = freqList.get(key);
071      if (val == null) {
072        // Use one as default, obviously
073        freqList.put(key, 1);
074      } else {
075        // increment if it's already there
076        freqList.put(key, val.intValue() + 1);
077      }
078
079      // add to justified list if this was justified once
080      //    if (inv1.justified()) {
081      justifiedList.add(key);
082      // }
083    }
084  }
085
086  /** Prints everything in the goodList. */
087  public void printAll() {
088
089    if (spinfoMode) {
090      printAllSpinfo();
091      return;
092    }
093
094    // keeps track of suppressed invariants due to appearing in
095    // every sample of the MultiDiff
096    int kill = 0;
097    int unjustifiedKill = 0;
098    ArrayList<String> bigList = new ArrayList<>();
099
100    // New historgram stuff
101    System.out.println("Histogram**************");
102
103    // This gets all of the output in the format:
104    // inv.ppt.name() + "$" + inv1.format_java() + " Count = " + freq
105    for (String str : freqList.keySet()) {
106      int freq = freqList.get(str).intValue();
107      if (freq < total && justifiedList.contains(str)) {
108        bigList.add(str + " Count =  " + freq);
109        // System.out.println (str + " Count =  " + freq);
110
111      }
112      // don't print something true in EVERY set
113      else if (freq == total) {
114        kill++;
115      } else {
116        // don't print something that was never justified
117        unjustifiedKill++;
118      }
119    }
120    System.out.println("Invariants appearing in all: " + kill);
121    System.out.println("Invariants never justified: " + unjustifiedKill);
122
123    // Now build the final HashMap that will have the following
124    // mapping:  program point names ->
125    //                      ArrayList of inv.format_java() with frequency
126
127    HashMap<String, ArrayList<String>> lastMap = new HashMap<>();
128    // One pass to fill each mapping with an empty ArrayList
129    for (String key : programPointsList) {
130      lastMap.put(key, new ArrayList<String>());
131    }
132
133    // Now to populate those ArrayLists
134    for (String str : bigList) {
135      StringTokenizer st = new StringTokenizer(str, "$");
136      String key = st.nextToken(); // a Ppt name
137      String data = st.nextToken();
138      try {
139        @SuppressWarnings("nullness") // map
140        @NonNull ArrayList<String> formatAndFrequencyList = lastMap.get(key);
141        formatAndFrequencyList.add(data);
142      } catch (Exception e) {
143        System.out.println(key + " error in MultiDiffVisitor");
144      }
145    }
146
147    // print it all
148    for (Map.Entry<@KeyFor("lastMap") String, ArrayList<String>> entry : lastMap.entrySet()) {
149      String key = entry.getKey();
150      ArrayList<String> al = entry.getValue();
151      // don't print anything if there are no selective invariants
152      if (al.size() == 0) {
153        continue;
154      }
155      System.out.println();
156      System.out.println(key + "*****************");
157      System.out.println();
158      for (Object toPrint : al) {
159        System.out.println(toPrint);
160      }
161    }
162    System.out.println();
163    System.out.println();
164  }
165
166  /** Prints everything in the goodList, outputs as spinfo. */
167  public void printAllSpinfo() {
168
169    // keeps track of suppressed invariants due to appearing in
170    // every sample of the MultiDiff
171    ArrayList<String> bigList = new ArrayList<>();
172
173    // This gets all of the output in the format:
174    // inv.ppt.name() + "$" + inv1.format_java()
175    for (String str : freqList.keySet()) {
176      int freq = freqList.get(str).intValue();
177      if (freq < total && justifiedList.contains(str)) {
178        // just want the String on its own line
179        bigList.add(str);
180      }
181    }
182
183    // Now build the final HashMap that will have the following
184    // mapping:  program point names ->
185    //                      ArrayList of inv.format_java() with frequency
186
187    HashMap<String, ArrayList<String>> lastMap = new HashMap<>();
188    // One pass to fill each mapping with an empty ArrayList
189    for (String key : programPointsList) {
190      lastMap.put(key, new ArrayList<String>());
191    }
192
193    // Now to populate those ArrayLists
194    for (String str : bigList) {
195      StringTokenizer st = new StringTokenizer(str, "$");
196      String key = st.nextToken();
197      String data = st.nextToken();
198      try {
199        @SuppressWarnings("nullness") // map
200        @NonNull ArrayList<String> formatAndFrequencyList = lastMap.get(key);
201        formatAndFrequencyList.add(data);
202      } catch (Exception e) {
203        out.println(key + " error in MultiDiffVisitor");
204      }
205    }
206
207    // print it all
208    String lastPpt = "";
209    // sort them so that multiple exits will end up being adjacent
210    // to each other when they are from the same method
211    for (@KeyFor("lastMap") String key : CollectionsPlume.sortedKeySet(lastMap)) {
212      ArrayList<String> al = lastMap.get(key);
213      // don't print anything if there are no selective invariants
214
215      if (al.size() == 0) {
216        continue;
217      }
218
219      // Get rid of the extra stuff like (III)I:::ENTER
220      // at the end of each of the program points
221
222      // use the fact that we only want the stuff before the first '('
223      StringTokenizer pToke = new StringTokenizer(key, "(");
224
225      // sadly we only want EXIT values, so throw out any ENTERs
226      // because the spinfo won't deal well with them anyway
227
228      //            if (key.indexOf ("ENTER") != -1) continue;
229
230      // Now we don't want to reprint the program point name
231      // again in the spinfo file if it has been printed from
232      // the previous Ppt that exited at a different point -LL
233
234      String thisPpt = pToke.nextToken();
235
236      if (!lastPpt.equals(thisPpt)) {
237        out.println();
238        out.println("PPT_NAME " + thisPpt);
239
240        lastPpt = thisPpt;
241      }
242      for (Object toPrint : al) {
243        out.println(toPrint);
244      }
245    }
246  }
247
248  @Override
249  protected boolean shouldPrint(@Nullable Invariant inv1, @Nullable Invariant inv2) {
250    return true; // super.shouldPrint (inv1, inv2) &&
251    //    inv1.format().toString().indexOf(">") == -1 &&
252    // inv1.format().toString().indexOf("orig") == -1;
253  }
254}