Main.java revision 07a61175c257cf7fb7738d5eede16733440c1b56
110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipackage annotator;
210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.io.*;
410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.*;
510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.regex.*;
6e67b0ef921bf74296a1c601b85e2bdcca1e1a86cMichael Ernstimport plume.*;
710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
8e13e8963268c825e80bb92e310135617d7275656Eric Spishakimport annotator.find.Criteria;
910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.Insertion;
1010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.TreeFinder;
1110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source;
1210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source.CompilerException;
1310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.IndexFileSpecification;
1410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.Specification;
1510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
16f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernstimport com.sun.source.tree.*;
175534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernstimport com.sun.source.util.TreePath;
1810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
1957ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernstimport com.google.common.collect.*;
2057ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst
2110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali/**
2210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * This is the main class for the annotator, which inserts annotations in
23c15d52e959f2b3052978e40552159de77c403428Michael Ernst * Java source code.  You can call it as <tt>java annotator.Main</tt> or by
24c15d52e959f2b3052978e40552159de77c403428Michael Ernst * using the shell script <tt>insert-annotations-to-source</tt>.
25c15d52e959f2b3052978e40552159de77c403428Michael Ernst * <p>
26c15d52e959f2b3052978e40552159de77c403428Michael Ernst *
27c15d52e959f2b3052978e40552159de77c403428Michael Ernst * It takes as input
2810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <ul>
2976be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl *   <li>annotation (index) files, which indicate the annotations to insert</li>
3010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *   <li>Java source files, into which the annotator inserts annotations</li>
3110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * </ul>
3210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Use the --help option for full usage details.
3310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <p>
3410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *
3510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Annotations that are not for the specified Java files are ignored.
3610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */
3710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipublic class Main {
3810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
3910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  /** Directory in which output files are written. */
4010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-d <directory> Directory in which output files are written")
4110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static String outdir = "annotated/";
4210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
43fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst  /**
44fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * If true, overwrite original source files (making a backup first).
45fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * Furthermore, if the backup files already exist, they are used instead
46fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * of the .java files.  This behavior permits a user to tweak the .jaif
47fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * file and re-run the annotator.
48fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * <p>
49fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   *
50fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * Note that if the user runs the annotator with --in-place, makes edits,
51fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * and then re-runs the annotator with this --in-place option, those
52fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * edits are lost.  Similarly, if the user runs the annotator twice in a
53fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * row with --in-place, only the last set of annotations will appear in
54fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * the codebase at the end.
55fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * <p>
56fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   *
57fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * To preserve changes when using the --in-place option, first remove the
58fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * backup files.  Or, use the <tt>-d .</tt> option, which makes (and
59fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * reads) no backup, instead of --in-place.
60fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   */
61f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  @Option("-i Overwrite original source files")
62f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  public static boolean in_place = false;
63f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
6410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-h Print usage information and exit")
6510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean help = false;
6610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
6710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-a Abbreviate annotation names")
6810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean abbreviate = true;
6910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
7010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-c Insert annotations in comments")
7110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean comments = false;
7210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
73bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst  @Option("-o Omit given annotation")
74bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst  public static String omit_annotation;
75bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst
7610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-v Verbose (print progress information)")
7710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean verbose;
7810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
7910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("Debug (print debug information)")
8010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean debug = false;
8110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
8210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  // Implementation details:
8310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  1. The annotator partially compiles source
8410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     files using the compiler API (JSR-199), obtaining an AST.
8510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  2. The annotator reads the specification file, producing a set of
8610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     annotator.find.Insertions.  Insertions completely specify what to
8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     write (as a String, which is ultimately translated according to the
8810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     keyword file) and how to write it (as annotator.find.Criteria).
8910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  3. It then traverses the tree, looking for nodes that satisfy the
9010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     Insertion Criteria, translating the Insertion text against the
9110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     keyword file, and inserting the annotations into the source file.
9210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
9310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  /**
9410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   * Runs the annotator, parsing the source and spec files and applying
9510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   * the annotations.
9610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   */
9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static void main(String[] args) {
9810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
9910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (verbose) {
10067693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst      System.out.printf("insert-annotations-to-source (%s)",
10167693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst                        annotations.io.classfile.ClassFileReader.INDEX_UTILS_VERSION);
10210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
10310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
104f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    Options options = new Options("Main [options] ann-file... java-file...", Main.class);
10576be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl    String[] file_args = options.parse_or_usage (args);
106f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
1071220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst    if (debug) {
1081220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst      TreeFinder.debug = true;
109e13e8963268c825e80bb92e310135617d7275656Eric Spishak      Criteria.debug = true;
1101220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst    }
1111220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst
11210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (help) {
11310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      options.print_usage();
11410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.exit(0);
11510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
11610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
117f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    if (in_place && outdir != "annotated/") { // interned
118f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      options.print_usage("The --outdir and --in-place options are mutually exclusive.");
119f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      System.exit(1);
120f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    }
121f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
12210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (file_args.length < 2) {
123f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length);
12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.exit(1);
12510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
12610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
12710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    // The insertions specified by the annotation files.
12810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    List<Insertion> insertions = new ArrayList<Insertion>();
12910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    // The Java files into which to insert.
13010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    List<String> javafiles = new ArrayList<String>();
13110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
13210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    for (String arg : file_args) {
13310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      if (arg.endsWith(".java")) {
13410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        javafiles.add(arg);
1358ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl      } else if (arg.endsWith(".jaif") ||
1368ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl                 arg.endsWith(".jann")) {
13710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        try {
13810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          Specification spec = new IndexFileSpecification(arg);
13910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          List<Insertion> parsedSpec = spec.parse();
1404735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          if (verbose || debug) {
14110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            System.out.printf("Read %d annotations from %s%n",
14210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali                              parsedSpec.size(), arg);
14310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
144bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst          if (omit_annotation != null) {
145bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            List<Insertion> filtered = new ArrayList<Insertion>(parsedSpec.size());
146bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            for (Insertion insertion : parsedSpec) {
147bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst              if (! omit_annotation.equals(insertion.getText())) {
148bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst                filtered.add(insertion);
149bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst              }
150bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            }
151bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            parsedSpec = filtered;
152bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            if (verbose || debug) {
153bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst              System.out.printf("After filtering: %d annotations from %s%n",
154bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst                                parsedSpec.size(), arg);
155bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            }
156bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst          }
157bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst          insertions.addAll(parsedSpec);
1582c4067b440b23911687e4a353584b088eccf17fbMichael Ernst        } catch (RuntimeException e) {
1592c4067b440b23911687e4a353584b088eccf17fbMichael Ernst          if (e.getCause() != null
1602c4067b440b23911687e4a353584b088eccf17fbMichael Ernst              && e.getCause() instanceof FileNotFoundException) {
1612c4067b440b23911687e4a353584b088eccf17fbMichael Ernst            System.err.println("File not found: " + arg);
1622c4067b440b23911687e4a353584b088eccf17fbMichael Ernst            System.exit(1);
1632c4067b440b23911687e4a353584b088eccf17fbMichael Ernst          } else {
1642c4067b440b23911687e4a353584b088eccf17fbMichael Ernst            throw e;
1652c4067b440b23911687e4a353584b088eccf17fbMichael Ernst          }
16610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        } catch (FileIOException e) {
16710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.err.println("Error while parsing annotation file " + arg);
16810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (e.getMessage() != null) {
16910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            System.err.println(e.getMessage());
17010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
17110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          e.printStackTrace();
17210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.exit(1);
17310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
17410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } else {
17510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        throw new Error("Unrecognized file extension: " + arg);
17610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
17710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
17810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
17910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (debug) {
180b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst      System.out.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size());
18110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
18210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (debug) {
183b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst      System.out.printf("Insertions:%n");
18410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      for (Insertion insertion : insertions) {
185b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst        System.out.printf("  %s%n", insertion);
18610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
18710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
18810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
18910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    for (String javafilename : javafiles) {
19010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
19110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      if (verbose) {
19210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.out.println("Processing " + javafilename);
19310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
19410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
195f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File javafile = new File(javafilename);
196f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
197f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File outfile;
198f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File unannotated = new File(javafilename + ".unannotated");
199f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      if (in_place) {
200f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // It doesn't make sense to check timestamps;
201f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // if the .java.unannotated file exists, then just use it.
202f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // A user can rename that file back to just .java to cause the
203f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // .java file to be read.
204f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (unannotated.exists()) {
205f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (verbose) {
206f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            System.out.printf("Renaming %s to %s%n", unannotated, javafile);
207f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
208f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          boolean success = unannotated.renameTo(javafile);
209f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (! success) {
210f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            throw new Error(String.format("Failed renaming %s to %s",
211f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst                                          unannotated, javafile));
212f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
213f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
214f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        outfile = javafile;
215f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      } else {
216f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        String baseName;
217f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (javafile.isAbsolute()) {
218f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          baseName = javafile.getName();
219f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        } else {
220f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          baseName = javafile.getPath();
221f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
222f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        outfile = new File(outdir, baseName);
223f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      }
224f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
225f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      Set<String> imports = new LinkedHashSet<String>();
226f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
22710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      String fileLineSep = System.getProperty("line.separator");
22810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      Source src;
22910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // Get the source file, and use it to obtain parse trees.
23010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      try {
23110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // fileLineSep is set here so that exceptions can be caught
23210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        fileLineSep = UtilMDE.inferLineSeparator(javafilename);
23310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        src = new Source(javafilename);
234c15d52e959f2b3052978e40552159de77c403428Michael Ernst        if (verbose) {
235c15d52e959f2b3052978e40552159de77c403428Michael Ernst          System.out.printf("Parsed %s%n", javafilename);
236c15d52e959f2b3052978e40552159de77c403428Michael Ernst        }
23710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (CompilerException e) {
23810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
23910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        return;
24010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (IOException e) {
24110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
24210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        return;
24310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
24410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
245c15d52e959f2b3052978e40552159de77c403428Michael Ernst      int num_insertions = 0;
246c15d52e959f2b3052978e40552159de77c403428Michael Ernst
24710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      for (CompilationUnitTree tree : src.parse()) {
24810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
24910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // Create a finder, and use it to get positions.
25010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        TreeFinder finder = new TreeFinder(tree);
2515c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst        if (debug) {
2521d9ddfee01826d63c802f5356c250e3841a68e71wdietl          TreeFinder.debug = true;
2535c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst        }
254ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst        SetMultimap<Integer, Insertion> positions = finder.getPositions(tree, insertions);
25510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
25610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // Apply the positions to the source file.
257c15d52e959f2b3052978e40552159de77c403428Michael Ernst        if (debug || verbose) {
258c15d52e959f2b3052978e40552159de77c403428Michael Ernst          System.err.printf("getPositions returned %d positions in tree for %s%n", positions.size(), javafilename);
25910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
26010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
26157ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        Set<Integer> positionKeysUnsorted = positions.keySet();
26257ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        Set<Integer> positionKeysSorted = new TreeSet<Integer>(new TreeFinder.ReverseIntegerComparator());
26357ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        positionKeysSorted.addAll(positionKeysUnsorted);
26457ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        for (Integer pos : positionKeysSorted) {
265ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst          List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pos));
266ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst          Collections.reverse(toInsertList);
267a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst          if (debug) {
268a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst            System.out.printf("insertion pos: %d%n", pos);
269a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst          }
270d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst          assert pos >= 0
271d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst            : "pos is negative: " + pos + " " + toInsertList.get(0) + " " + javafilename;
272ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst          for (Insertion iToInsert : toInsertList) {
273d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak            String toInsert = iToInsert.getText(comments, abbreviate);
2740f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst            if (! (toInsert.startsWith("@")
27507a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                   || toInsert.startsWith("extends ")
27607a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                   || toInsert.startsWith("((")
27707a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                   || toInsert.equals("))"))) {
27818ff42d676bd77c607f8839e1739beebd43e76d3Eric Spishak              throw new Error("Illegal insertion: " + toInsert);
27900623041550b198d6a389410ea3a34687cddced5Michael Ernst            }
280ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (abbreviate) {
281d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak              String packageName = iToInsert.getPackageName();
282d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak              if (packageName != null) {
283d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak                if (debug && !imports.contains(packageName)) {
28409e02e2c623aae704a60e026404686c147532d80Michael Ernst                  System.out.printf("Need import %s%n  due to insertion %s%n",
285d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak                                    packageName, toInsert);
28609e02e2c623aae704a60e026404686c147532d80Michael Ernst                }
287d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak                imports.add(packageName);
2880f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst              }
28910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            }
29010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
291ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // Possibly add whitespace after the insertion
292ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            boolean gotSeparateLine = false;
293ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (iToInsert.getSeparateLine()) {
294a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst              // System.out.printf("getSeparateLine=true for insertion at pos %d: %s%n", pos, iToInsert);
295ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              int indentation = 0;
296ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              while ((pos - indentation != 0)
297ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                     // horizontal whitespace
298ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                     && (src.charAt(pos-indentation-1) == ' '
299ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                         || src.charAt(pos-indentation-1) == '\t')) {
300a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst                // System.out.printf("src.charAt(pos-indentation-1 == %d-%d-1)='%s'%n",
301a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst                //                   pos, indentation, src.charAt(pos-indentation-1));
302ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                indentation++;
303ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              }
304ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              if ((pos - indentation == 0)
305ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                  // horizontal whitespace
306ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                  || (src.charAt(pos-indentation-1) == '\f'
307ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                      || src.charAt(pos-indentation-1) == '\n'
308ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                      || src.charAt(pos-indentation-1) == '\r')) {
309ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                toInsert = toInsert + fileLineSep + src.substring(pos-indentation, pos);
310ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                gotSeparateLine = true;
311ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              }
312ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            }
313c71d4e21017f8f4412a2d399676e358858999623Michael Ernst
314ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // Possibly add a leading space before the insertion
315ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if ((! gotSeparateLine) && (pos != 0)) {
316ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              char precedingChar = src.charAt(pos-1);
317ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              if (! (Character.isWhitespace(precedingChar)
318ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                     // No space if it's the first formal or generic parameter
31907a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                     // or if it's a CloseParenthesesInsertion
320ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                     || precedingChar == '('
32107a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                     || precedingChar == '<'
32207a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                     || precedingChar == '['
32307a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                     || toInsert.equals("))"))) {
324ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                toInsert = " " + toInsert;
325ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              }
3264735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            }
327ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst
328ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // If it's already there, don't re-insert.  This is a hack!
329ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // Also, I think this is already checked when constructing the
33076be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl            // insertions.
331ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            int precedingTextPos = pos-toInsert.length()-1;
332ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (precedingTextPos >= 0) {
333ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              String precedingTextPlusChar
334ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                = src.getString().substring(precedingTextPos, pos);
335ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'");
336ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length()))
337ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                  || toInsert.equals(precedingTextPlusChar.substring(1))) {
338ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                if (debug) {
339050043fa0fe1286433a9c5985f6e7f1514b3e19bwdietl                    System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'");
3403e5089aea50851069fffb66a96b731c6b06a2666wdietl                    System.out.println("Already present, skipping");
341ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                }
342ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                continue;
3434735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              }
34410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            }
345ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // add trailing whitespace
34609e02e2c623aae704a60e026404686c147532d80Michael Ernst            // (test is not for "extends " because we just added a leading space, above)
34707a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak            if ((! gotSeparateLine) && (! toInsert.startsWith(" extends "))
34807a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                    && (! toInsert.startsWith("((")) && (! toInsert.startsWith(" (("))
34907a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak                    && (! toInsert.equals("))"))) {
350ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              toInsert = toInsert + " ";
351ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            }
352ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            src.insert(pos, toInsert);
353c15d52e959f2b3052978e40552159de77c403428Michael Ernst            if (verbose) {
354c15d52e959f2b3052978e40552159de77c403428Michael Ernst              System.out.print(".");
355c15d52e959f2b3052978e40552159de77c403428Michael Ernst              num_insertions++;
356c15d52e959f2b3052978e40552159de77c403428Michael Ernst              if ((num_insertions % 50) == 0) {
357c15d52e959f2b3052978e40552159de77c403428Michael Ernst                System.out.println();   // terminate the line that contains dots
358c15d52e959f2b3052978e40552159de77c403428Michael Ernst              }
359c15d52e959f2b3052978e40552159de77c403428Michael Ernst            }
360ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (debug) {
361ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              System.out.println("Post-insertion source: " + src.getString());
362ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            }
36310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
36410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
36510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
366c15d52e959f2b3052978e40552159de77c403428Michael Ernst      if (verbose) {
367c15d52e959f2b3052978e40552159de77c403428Michael Ernst        if ((num_insertions % 50) != 0) {
368c15d52e959f2b3052978e40552159de77c403428Michael Ernst          System.out.println();   // terminate the line that contains dots
369c15d52e959f2b3052978e40552159de77c403428Michael Ernst        }
370c15d52e959f2b3052978e40552159de77c403428Michael Ernst      }
37110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
37210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // insert import statements
37310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      {
37410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (debug) {
37510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.out.println(imports.size() + " imports to insert");
37657ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst          for (String classname : imports) {
37757ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst            System.out.println("  " + classname);
37857ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst          }
37910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
38010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Pattern importPattern = Pattern.compile("(?m)^import\\b");
38110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)");
38210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        int importIndex = 0;      // default: beginning of file
38310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        String srcString = src.getString();
38410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Matcher m;
38510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        m = importPattern.matcher(srcString);
38610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (m.find()) {
38710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          importIndex = m.start();
38810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        } else {
38910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // if (debug) {
39010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          //   System.out.println("Didn't find import in " + srcString);
39110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // }
39210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          m = packagePattern.matcher(srcString);
39310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (m.find()) {
39410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            importIndex = m.end();
39510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
39610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
39710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        for (String classname : imports) {
39810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          String toInsert = "import " + classname + ";" + fileLineSep;
39910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          src.insert(importIndex, toInsert);
40010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          importIndex += toInsert.length();
40110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
40210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
40310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
40410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // Write the source file.
40510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      try {
406f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (in_place) {
407f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (verbose) {
408f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            System.out.printf("Renaming %s to %s%n", javafile, unannotated);
409f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
410f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          boolean success = javafile.renameTo(unannotated);
411f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (! success) {
412f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            throw new Error(String.format("Failed renaming %s to %s",
413f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst                                          javafile, unannotated));
414f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
415f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        } else {
416f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          outfile.getParentFile().mkdirs();
417f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
41810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        OutputStream output = new FileOutputStream(outfile);
419f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (verbose) {
420f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          System.out.printf("Writing %s%n", outfile);
421f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
42210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        src.write(output);
423f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        output.close();
42410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (IOException e) {
42510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.err.println("Problem while writing file " + outfile);
42610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
42710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.exit(1);
42810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
42910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
43010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  }
43110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
4325534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  ///
4335534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  /// Utility methods
4345534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  ///
4355534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
4365534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  public static String pathToString(TreePath path) {
4375534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    if (path == null)
4385534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return "null";
439f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst    return treeToString(path.getLeaf());
440f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst  }
441f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst
442f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst  public static String treeToString(Tree node) {
443f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst    String asString = node.toString();
4445534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    String oneLine = firstLine(asString);
4455534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    return "\"" + oneLine + "\"";
4465534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  }
4475534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
4485534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  /**
4495534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   * Return the first non-empty line of the string, adding an ellipsis
4505534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   * (...) if the string was truncated.
4515534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   */
4525534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  public static String firstLine(String s) {
4535534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    while (s.startsWith("\n")) {
4545534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      s = s.substring(1);
4555534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    }
4565534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    int newlineIndex = s.indexOf('\n');
4575534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    if (newlineIndex == -1) {
4585534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return s;
4595534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    } else {
4605534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return s.substring(0, newlineIndex) + "...";
4615534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    }
4625534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  }
4635534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
464aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb  /**
465aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   * Separates the annotation class from its arguments.
466aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   *
467aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   * @return given <code>@foo(bar)</code> it returns the pair <code>{ @foo, (bar) }</code>.
468aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   */
469aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb  public static Pair<String,String> removeArgs(String s) {
470aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb    int pidx = s.indexOf("(");
471aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb    return (pidx == -1) ?
472615ec652dc360e33296fd763a4fa56e59c35b23cMichael Ernst        Pair.of(s, (String)null) :
473aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb        Pair.of(s.substring(0, pidx), s.substring(pidx));
474aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb  }
475aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb
47610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali}
477