001package daikon.diff;
002
003import daikon.Global;
004import daikon.Ppt;
005import daikon.inv.Invariant;
006import java.io.PrintStream;
007import java.text.DecimalFormat;
008import org.checkerframework.checker.nullness.qual.Nullable;
009
010/** Prints all the invariant pairs, including pairs containing identical invariants. */
011public class PrintAllVisitor extends DepthFirstVisitor {
012
013  // Protected so subclasses can use it.
014  protected static final String lineSep = Global.lineSep;
015
016  protected static boolean HUMAN_OUTPUT = false;
017
018  private static DecimalFormat CONFIDENCE_FORMAT = new DecimalFormat("0.####");
019
020  private PrintStream ps;
021  private boolean verbose;
022  private boolean printEmptyPpts;
023
024  /**
025   * Stores the output generated when visiting invariant nodes. This output cannot be printed
026   * directly to the print stream, because the Ppt output must come before the Invariant output.
027   */
028  private StringBuilder bufOutput = new StringBuilder();
029
030  public PrintAllVisitor(PrintStream ps, boolean verbose, boolean printEmptyPpts) {
031    this.ps = ps;
032    this.verbose = verbose;
033    this.printEmptyPpts = printEmptyPpts;
034  }
035
036  /** Prints the pair of program points, and all the invariants contained within them. */
037  @Override
038  public void visit(PptNode node) {
039    // Empty the string buffer
040    bufOutput.setLength(0);
041
042    super.visit(node);
043
044    if (bufOutput.length() > 0 || printEmptyPpts) {
045      Ppt ppt1 = node.getPpt1();
046      Ppt ppt2 = node.getPpt2();
047
048      ps.print("<");
049      if (ppt1 == null) {
050        ps.print((@Nullable String) null);
051      } else {
052        ps.print(ppt1.name());
053      }
054
055      if (ppt1 == null || ppt2 == null || !ppt1.name().equals(ppt2.name())) {
056        ps.print(", ");
057        if (ppt2 == null) {
058          ps.print((@Nullable String) null);
059        } else {
060          ps.print(ppt2.name());
061        }
062      }
063      ps.println(">");
064      ps.print(bufOutput.toString());
065    }
066  }
067
068  /** Prints a pair of invariants. Includes the type of the invariants and their relationship. */
069  @Override
070  public void visit(InvNode node) {
071
072    if (HUMAN_OUTPUT) {
073      printHumanOutput(node);
074      return;
075    }
076
077    Invariant inv1 = node.getInv1();
078    Invariant inv2 = node.getInv2();
079
080    bufPrint("  <");
081
082    if (inv1 == null) {
083      bufPrint(null);
084    } else {
085      printInvariant(inv1);
086    }
087    bufPrint(", ");
088    if (inv2 == null) {
089      bufPrint((@Nullable String) null);
090    } else {
091      printInvariant(inv2);
092    }
093    bufPrint(">");
094
095    int arity = DetailedStatisticsVisitor.determineArity(inv1, inv2);
096    String arityLabel = DetailedStatisticsVisitor.ARITY_LABELS[arity];
097    int rel = DetailedStatisticsVisitor.determineRelationship(inv1, inv2);
098    String relLabel = DetailedStatisticsVisitor.RELATIONSHIP_LABELS[rel];
099
100    bufPrint(" (" + arityLabel + "," + relLabel + ")");
101
102    bufPrintln();
103  }
104
105  /**
106   * This method is an alternate printing procedure for an InvNode so that the output is more human
107   * readable. The format resembles cvs diff with '+' and '-' signs for the differing invariants.
108   * There is no information on justification or invariant type.
109   */
110  public void printHumanOutput(InvNode node) {
111
112    Invariant inv1 = node.getInv1();
113    Invariant inv2 = node.getInv2();
114
115    //    bufPrint("  <");
116
117    if (inv1 != null && inv2 != null && inv1.format().equals(inv2.format())) {
118      return;
119    }
120
121    if (inv1 == null) {
122      //   bufPrint((String) null);
123    } else {
124      //  printInvariant(inv1);
125      bufPrintln(("- " + inv1.format()).trim());
126    }
127    //    bufPrint(", ");
128    if (inv2 == null) {
129      //      bufPrint((String) null);
130    } else {
131      bufPrintln(("+ " + inv2.format()).trim());
132      //      printInvariant(inv2);
133    }
134    //    bufPrint(">");
135
136    // int arity = DetailedStatisticsVisitor.determineArity(inv1, inv2);
137    // int rel = DetailedStatisticsVisitor.determineRelationship(inv1, inv2);
138    // String arityLabel = DetailedStatisticsVisitor.ARITY_LABELS[arity];
139    // String relLabel = DetailedStatisticsVisitor.RELATIONSHIP_LABELS[rel];
140    //    bufPrint(" (" + arityLabel + "," + relLabel + ")");
141
142    bufPrintln();
143  }
144
145  /**
146   * Prints an invariant, including its printability and possibly its confidence. Example: "argv !=
147   * null {0.9999+}".
148   */
149  protected void printInvariant(Invariant inv) {
150    if (verbose) {
151      bufPrint(inv.repr_prob());
152      bufPrint(" {");
153      printPrintability(inv);
154      bufPrint("}");
155    } else {
156      bufPrint(inv.format());
157      bufPrint(" {");
158      printConfidence(inv);
159      printPrintability(inv);
160      bufPrint("}");
161    }
162  }
163
164  /**
165   * Prints the confidence of the invariant. Confidences between .9999 and 1 are rounded to .9999.
166   */
167  private void printConfidence(Invariant inv) {
168    double conf = inv.getConfidence();
169    if (.9999 < conf && conf < 1) {
170      conf = .9999;
171    }
172    bufPrint(CONFIDENCE_FORMAT.format(conf));
173  }
174
175  /** Prints '+' if the invariant is worth printing, '-' otherwise. */
176  // XXX This routine takes up most of diff's run time on large .inv
177  // files, and is not particularly interesting. There should perhaps
178  // be an option to turn it off. -SMcC
179  private void printPrintability(Invariant inv) {
180    if (inv.isWorthPrinting()) {
181      bufPrint("+");
182    } else {
183      bufPrint("-");
184    }
185  }
186
187  // "prints" by appending to a string buffer
188  protected void bufPrint(@Nullable String s) {
189    bufOutput.append(s);
190  }
191
192  protected void bufPrintln(@Nullable String s) {
193    bufPrint(s);
194    bufPrintln();
195  }
196
197  protected void bufPrintln() {
198    bufOutput.append(Global.lineSep);
199  }
200}