Main.java revision 290791bf3ace51eca8cb637b47e1e1d01d92111d
110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipackage annotator;
210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
3f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.io.File;
4f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.io.FileNotFoundException;
5f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.io.FileOutputStream;
6f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.io.IOException;
7f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.io.OutputStream;
8f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.ArrayList;
9f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.Collections;
10adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brownimport java.util.Comparator;
11f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.LinkedHashSet;
12f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.List;
13f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.Set;
14f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.TreeSet;
15f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.regex.Matcher;
16f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport java.util.regex.Pattern;
1710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
18f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport plume.FileIOException;
19f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport plume.Option;
20f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport plume.Options;
21f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport plume.Pair;
22f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport plume.UtilMDE;
23f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport annotator.Source.CompilerException;
24e13e8963268c825e80bb92e310135617d7275656Eric Spishakimport annotator.find.Criteria;
2510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.Insertion;
26290791bf3ace51eca8cb637b47e1e1d01d92111dDan Brownimport annotator.find.Insertions;
27c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brownimport annotator.find.ReceiverInsertion;
2810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.TreeFinder;
2910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.IndexFileSpecification;
3010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.Specification;
3110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
32f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport com.google.common.collect.SetMultimap;
33f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport com.sun.source.tree.CompilationUnitTree;
34f41c057e252477de867dd3e5bd9d7bd33d6af4ebEric Spishakimport com.sun.source.tree.Tree;
355534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernstimport com.sun.source.util.TreePath;
36de7d64fbe7b6eb28b7ec1a2393b91b8e9c6afea6Dan Brownimport com.sun.tools.javac.main.CommandLine;
37b7158c74813fa9eed49afea2189aee0cf51cfb73Dan Brownimport com.sun.tools.javac.tree.JCTree;
3810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
3910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali/**
4010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * This is the main class for the annotator, which inserts annotations in
41c15d52e959f2b3052978e40552159de77c403428Michael Ernst * Java source code.  You can call it as <tt>java annotator.Main</tt> or by
42c15d52e959f2b3052978e40552159de77c403428Michael Ernst * using the shell script <tt>insert-annotations-to-source</tt>.
43c15d52e959f2b3052978e40552159de77c403428Michael Ernst * <p>
44c15d52e959f2b3052978e40552159de77c403428Michael Ernst *
45c15d52e959f2b3052978e40552159de77c403428Michael Ernst * It takes as input
4610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <ul>
4776be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl *   <li>annotation (index) files, which indicate the annotations to insert</li>
4810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *   <li>Java source files, into which the annotator inserts annotations</li>
4910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * </ul>
5010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Use the --help option for full usage details.
5110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <p>
5210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *
5310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Annotations that are not for the specified Java files are ignored.
5410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */
5510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipublic class Main {
5610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
5710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  /** Directory in which output files are written. */
5810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-d <directory> Directory in which output files are written")
5910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static String outdir = "annotated/";
6010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
61fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst  /**
62fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * If true, overwrite original source files (making a backup first).
63fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * Furthermore, if the backup files already exist, they are used instead
64fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * of the .java files.  This behavior permits a user to tweak the .jaif
65fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * file and re-run the annotator.
66fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * <p>
67fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   *
68fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * Note that if the user runs the annotator with --in-place, makes edits,
69fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * and then re-runs the annotator with this --in-place option, those
70fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * edits are lost.  Similarly, if the user runs the annotator twice in a
71fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * row with --in-place, only the last set of annotations will appear in
72fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * the codebase at the end.
73fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * <p>
74fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   *
75fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * To preserve changes when using the --in-place option, first remove the
76fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * backup files.  Or, use the <tt>-d .</tt> option, which makes (and
77fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   * reads) no backup, instead of --in-place.
78fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst   */
79f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  @Option("-i Overwrite original source files")
80f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  public static boolean in_place = false;
81f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
8210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-a Abbreviate annotation names")
8310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean abbreviate = true;
8410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
8510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-c Insert annotations in comments")
8610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean comments = false;
8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
88bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst  @Option("-o Omit given annotation")
89bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst  public static String omit_annotation;
90bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst
9138326fd68e58af05a0368708acf0099d4c3d0ac1Michael Ernst  @Option("-h Print usage information and exit")
9238326fd68e58af05a0368708acf0099d4c3d0ac1Michael Ernst  public static boolean help = false;
9338326fd68e58af05a0368708acf0099d4c3d0ac1Michael Ernst
9410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-v Verbose (print progress information)")
9510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean verbose;
9610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("Debug (print debug information)")
9810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean debug = false;
9910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
1008b8c76e4860528031809204af427c71f6ee81fc2Eric Spishak  @Option("Print error stack")
1018b8c76e4860528031809204af427c71f6ee81fc2Eric Spishak  public static boolean print_error_stack = false;
1028b8c76e4860528031809204af427c71f6ee81fc2Eric Spishak
10310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  // Implementation details:
10410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  1. The annotator partially compiles source
10510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     files using the compiler API (JSR-199), obtaining an AST.
10610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  2. The annotator reads the specification file, producing a set of
10710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     annotator.find.Insertions.  Insertions completely specify what to
10810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     write (as a String, which is ultimately translated according to the
10910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     keyword file) and how to write it (as annotator.find.Criteria).
11010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  3. It then traverses the tree, looking for nodes that satisfy the
11110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     Insertion Criteria, translating the Insertion text against the
11210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     keyword file, and inserting the annotations into the source file.
11310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
11410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  /**
11510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   * Runs the annotator, parsing the source and spec files and applying
11610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   * the annotations.
11710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   */
118de7d64fbe7b6eb28b7ec1a2393b91b8e9c6afea6Dan Brown  public static void main(String[] args) throws IOException {
11910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
12010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (verbose) {
12167693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst      System.out.printf("insert-annotations-to-source (%s)",
12267693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst                        annotations.io.classfile.ClassFileReader.INDEX_UTILS_VERSION);
12310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
125f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    Options options = new Options("Main [options] ann-file... java-file...", Main.class);
126de7d64fbe7b6eb28b7ec1a2393b91b8e9c6afea6Dan Brown    String[] file_args = options.parse_or_usage(CommandLine.parse(args));
127f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
1281220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst    if (debug) {
1291220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst      TreeFinder.debug = true;
130e13e8963268c825e80bb92e310135617d7275656Eric Spishak      Criteria.debug = true;
1311220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst    }
1321220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst
13310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (help) {
13410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      options.print_usage();
13510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.exit(0);
13610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
13710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
138f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    if (in_place && outdir != "annotated/") { // interned
139f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      options.print_usage("The --outdir and --in-place options are mutually exclusive.");
140f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      System.exit(1);
141f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    }
142f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
14310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (file_args.length < 2) {
144f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length);
14510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.exit(1);
14610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
14710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
14810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    // The insertions specified by the annotation files.
149290791bf3ace51eca8cb637b47e1e1d01d92111dDan Brown    Insertions insertions = new Insertions();
15010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    // The Java files into which to insert.
15110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    List<String> javafiles = new ArrayList<String>();
15210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
15310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    for (String arg : file_args) {
15410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      if (arg.endsWith(".java")) {
15510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        javafiles.add(arg);
1568ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl      } else if (arg.endsWith(".jaif") ||
1578ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl                 arg.endsWith(".jann")) {
15810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        try {
15910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          Specification spec = new IndexFileSpecification(arg);
16010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          List<Insertion> parsedSpec = spec.parse();
1614735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          if (verbose || debug) {
16210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            System.out.printf("Read %d annotations from %s%n",
16310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali                              parsedSpec.size(), arg);
16410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
165bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst          if (omit_annotation != null) {
166bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            List<Insertion> filtered = new ArrayList<Insertion>(parsedSpec.size());
167bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            for (Insertion insertion : parsedSpec) {
1682441c4b66f26bf96ea7a8749645a933aa09a2d62Eric Spishak              // TODO: this won't omit annotations if the insertion is more than
1692441c4b66f26bf96ea7a8749645a933aa09a2d62Eric Spishak              // just the annotation (such as if the insertion is a cast
1702441c4b66f26bf96ea7a8749645a933aa09a2d62Eric Spishak              // insertion or a 'this' parameter in a method declaration).
171bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst              if (! omit_annotation.equals(insertion.getText())) {
172bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst                filtered.add(insertion);
173bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst              }
174bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            }
175bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            parsedSpec = filtered;
176bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            if (verbose || debug) {
177bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst              System.out.printf("After filtering: %d annotations from %s%n",
178bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst                                parsedSpec.size(), arg);
179bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst            }
180bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst          }
181bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst          insertions.addAll(parsedSpec);
1822c4067b440b23911687e4a353584b088eccf17fbMichael Ernst        } catch (RuntimeException e) {
1832c4067b440b23911687e4a353584b088eccf17fbMichael Ernst          if (e.getCause() != null
1842c4067b440b23911687e4a353584b088eccf17fbMichael Ernst              && e.getCause() instanceof FileNotFoundException) {
1852c4067b440b23911687e4a353584b088eccf17fbMichael Ernst            System.err.println("File not found: " + arg);
1862c4067b440b23911687e4a353584b088eccf17fbMichael Ernst            System.exit(1);
1872c4067b440b23911687e4a353584b088eccf17fbMichael Ernst          } else {
1882c4067b440b23911687e4a353584b088eccf17fbMichael Ernst            throw e;
1892c4067b440b23911687e4a353584b088eccf17fbMichael Ernst          }
19010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        } catch (FileIOException e) {
191bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak          // Add 1 to the line number since line numbers in text editors are usually one-based.
192bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak          System.err.println("Error while parsing annotation file " + arg + " at line "
193bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak              + (e.lineNumber + 1) + ":");
19410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (e.getMessage() != null) {
195bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak            System.err.println('\t' + e.getMessage());
196bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak          }
197bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak          if (e.getCause() != null && e.getCause().getMessage() != null) {
198bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak            System.err.println('\t' + e.getCause().getMessage());
199bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak          }
200bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak          if (debug) {
201bcf0e58623d43433c1c05a2fc954f69411b86759Eric Spishak            e.printStackTrace();
20210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
20310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.exit(1);
20410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
20510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } else {
20610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        throw new Error("Unrecognized file extension: " + arg);
20710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
20810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
20910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
21010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (debug) {
211b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst      System.out.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size());
21210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
21310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (debug) {
214b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst      System.out.printf("Insertions:%n");
21510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      for (Insertion insertion : insertions) {
216b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst        System.out.printf("  %s%n", insertion);
21710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
21810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
21910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
22010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    for (String javafilename : javafiles) {
22110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
22210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      if (verbose) {
22310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.out.println("Processing " + javafilename);
22410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
22510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
226f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File javafile = new File(javafilename);
227f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
228f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File outfile;
229f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File unannotated = new File(javafilename + ".unannotated");
230f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      if (in_place) {
231f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // It doesn't make sense to check timestamps;
232f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // if the .java.unannotated file exists, then just use it.
233f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // A user can rename that file back to just .java to cause the
234f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // .java file to be read.
235f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (unannotated.exists()) {
236f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (verbose) {
237f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            System.out.printf("Renaming %s to %s%n", unannotated, javafile);
238f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
239f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          boolean success = unannotated.renameTo(javafile);
240f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (! success) {
241f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            throw new Error(String.format("Failed renaming %s to %s",
242f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst                                          unannotated, javafile));
243f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
244f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
245f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        outfile = javafile;
246f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      } else {
247f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        String baseName;
248f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (javafile.isAbsolute()) {
249f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          baseName = javafile.getName();
250f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        } else {
251f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          baseName = javafile.getPath();
252f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
253f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        outfile = new File(outdir, baseName);
254f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      }
255f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
256f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      Set<String> imports = new LinkedHashSet<String>();
257f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
25810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      String fileLineSep = System.getProperty("line.separator");
25910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      Source src;
26010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // Get the source file, and use it to obtain parse trees.
26110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      try {
26210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // fileLineSep is set here so that exceptions can be caught
26310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        fileLineSep = UtilMDE.inferLineSeparator(javafilename);
26410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        src = new Source(javafilename);
265c15d52e959f2b3052978e40552159de77c403428Michael Ernst        if (verbose) {
266c15d52e959f2b3052978e40552159de77c403428Michael Ernst          System.out.printf("Parsed %s%n", javafilename);
267c15d52e959f2b3052978e40552159de77c403428Michael Ernst        }
26810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (CompilerException e) {
26910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
27010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        return;
27110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (IOException e) {
27210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
27310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        return;
27410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
27510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
276c15d52e959f2b3052978e40552159de77c403428Michael Ernst      int num_insertions = 0;
277c15d52e959f2b3052978e40552159de77c403428Michael Ernst
278b7158c74813fa9eed49afea2189aee0cf51cfb73Dan Brown      for (CompilationUnitTree cut : src.parse()) {
279b7158c74813fa9eed49afea2189aee0cf51cfb73Dan Brown        JCTree.JCCompilationUnit tree = (JCTree.JCCompilationUnit) cut;
28010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
28110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // Create a finder, and use it to get positions.
28210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        TreeFinder finder = new TreeFinder(tree);
2835c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst        if (debug) {
2841d9ddfee01826d63c802f5356c250e3841a68e71wdietl          TreeFinder.debug = true;
2855c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst        }
286ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst        SetMultimap<Integer, Insertion> positions = finder.getPositions(tree, insertions);
28710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
28810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // Apply the positions to the source file.
289c15d52e959f2b3052978e40552159de77c403428Michael Ernst        if (debug || verbose) {
290c15d52e959f2b3052978e40552159de77c403428Michael Ernst          System.err.printf("getPositions returned %d positions in tree for %s%n", positions.size(), javafilename);
29110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
29210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
29357ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        Set<Integer> positionKeysUnsorted = positions.keySet();
294adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown        Set<Integer> positionKeysSorted =
295adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown          new TreeSet<Integer>(new Comparator<Integer>() {
296adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown            @Override
297adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown            public int compare(Integer o1, Integer o2) {
298adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown              return o1.compareTo(o2) * -1;
299adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown            }
300adc47d693f66c43acddf4956515c0c94db3e6cb4Dan Brown          });
30157ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        positionKeysSorted.addAll(positionKeysUnsorted);
30257ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst        for (Integer pos : positionKeysSorted) {
303c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown          boolean receiverInserted = false;
304ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst          List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pos));
305ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst          Collections.reverse(toInsertList);
306a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst          if (debug) {
307a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst            System.out.printf("insertion pos: %d%n", pos);
308a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst          }
309d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst          assert pos >= 0
310d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst            : "pos is negative: " + pos + " " + toInsertList.get(0) + " " + javafilename;
311ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst          for (Insertion iToInsert : toInsertList) {
312ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // Possibly add whitespace after the insertion
3133d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            String trailingWhitespace = "";
314ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            boolean gotSeparateLine = false;
315ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (iToInsert.getSeparateLine()) {
316a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst              // System.out.printf("getSeparateLine=true for insertion at pos %d: %s%n", pos, iToInsert);
317ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              int indentation = 0;
318ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              while ((pos - indentation != 0)
319ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                     // horizontal whitespace
320ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                     && (src.charAt(pos-indentation-1) == ' '
321ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                         || src.charAt(pos-indentation-1) == '\t')) {
322a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst                // System.out.printf("src.charAt(pos-indentation-1 == %d-%d-1)='%s'%n",
323a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst                //                   pos, indentation, src.charAt(pos-indentation-1));
324ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                indentation++;
325ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              }
326ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              if ((pos - indentation == 0)
327ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                  // horizontal whitespace
328ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                  || (src.charAt(pos-indentation-1) == '\f'
329ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                      || src.charAt(pos-indentation-1) == '\n'
330ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                      || src.charAt(pos-indentation-1) == '\r')) {
3313d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak                trailingWhitespace = fileLineSep + src.substring(pos-indentation, pos);
332ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                gotSeparateLine = true;
333ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              }
334ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            }
335c71d4e21017f8f4412a2d399676e358858999623Michael Ernst
3363d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            char precedingChar;
3373d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            if (pos != 0) {
3383d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak              precedingChar = src.charAt(pos - 1);
3393d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            } else {
3403d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak              precedingChar = '\0';
3413d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            }
3423d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak
343c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown            if (iToInsert.getKind() == Insertion.Kind.RECEIVER) {
344c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown              ReceiverInsertion ri = (ReceiverInsertion) iToInsert;
345c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown              ri.setAnnotationsOnly(receiverInserted);
346c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown              receiverInserted = true;
347c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown            }
348c448ebefd28e4a67c0c4957ee387f02ab9020502Dan Brown
3493d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            String toInsert = iToInsert.getText(comments, abbreviate,
3503d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak                    gotSeparateLine, pos, precedingChar) + trailingWhitespace;
3513d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak            if (abbreviate) {
3523d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak              Set<String> packageNames = iToInsert.getPackageNames();
3533d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak              if (debug) {
3543d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak                System.out.printf("Need import %s%n  due to insertion %s%n",
3553d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak                                  packageNames, toInsert);
356ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              }
3573d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak              imports.addAll(packageNames);
3584735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            }
359ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst
360ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // If it's already there, don't re-insert.  This is a hack!
361ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            // Also, I think this is already checked when constructing the
36276be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl            // insertions.
363ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            int precedingTextPos = pos-toInsert.length()-1;
364ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (precedingTextPos >= 0) {
365ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              String precedingTextPlusChar
366ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                = src.getString().substring(precedingTextPos, pos);
367ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'");
368ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length()))
369ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                  || toInsert.equals(precedingTextPlusChar.substring(1))) {
370ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                if (debug) {
371050043fa0fe1286433a9c5985f6e7f1514b3e19bwdietl                    System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'");
3723e5089aea50851069fffb66a96b731c6b06a2666wdietl                    System.out.println("Already present, skipping");
373ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                }
374ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst                continue;
3754735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              }
37610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            }
3773d8c27ef3f539aea5f49643fed2b38e49e99eda4Eric Spishak
378ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            src.insert(pos, toInsert);
379c15d52e959f2b3052978e40552159de77c403428Michael Ernst            if (verbose) {
380c15d52e959f2b3052978e40552159de77c403428Michael Ernst              System.out.print(".");
381c15d52e959f2b3052978e40552159de77c403428Michael Ernst              num_insertions++;
382c15d52e959f2b3052978e40552159de77c403428Michael Ernst              if ((num_insertions % 50) == 0) {
383c15d52e959f2b3052978e40552159de77c403428Michael Ernst                System.out.println();   // terminate the line that contains dots
384c15d52e959f2b3052978e40552159de77c403428Michael Ernst              }
385c15d52e959f2b3052978e40552159de77c403428Michael Ernst            }
386ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            if (debug) {
387ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst              System.out.println("Post-insertion source: " + src.getString());
388ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst            }
38910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
39010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
39110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
392c15d52e959f2b3052978e40552159de77c403428Michael Ernst      if (verbose) {
393c15d52e959f2b3052978e40552159de77c403428Michael Ernst        if ((num_insertions % 50) != 0) {
394c15d52e959f2b3052978e40552159de77c403428Michael Ernst          System.out.println();   // terminate the line that contains dots
395c15d52e959f2b3052978e40552159de77c403428Michael Ernst        }
396c15d52e959f2b3052978e40552159de77c403428Michael Ernst      }
39710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
39810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // insert import statements
39910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      {
40010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (debug) {
40110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.out.println(imports.size() + " imports to insert");
40257ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst          for (String classname : imports) {
40357ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst            System.out.println("  " + classname);
40457ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst          }
40510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
40610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Pattern importPattern = Pattern.compile("(?m)^import\\b");
40710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)");
40810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        int importIndex = 0;      // default: beginning of file
40910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        String srcString = src.getString();
41010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Matcher m;
41110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        m = importPattern.matcher(srcString);
41210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (m.find()) {
41310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          importIndex = m.start();
41410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        } else {
41510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // if (debug) {
41610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          //   System.out.println("Didn't find import in " + srcString);
41710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // }
41810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          m = packagePattern.matcher(srcString);
41910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (m.find()) {
42010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            importIndex = m.end();
42110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
42210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
42310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        for (String classname : imports) {
42410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          String toInsert = "import " + classname + ";" + fileLineSep;
42510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          src.insert(importIndex, toInsert);
42610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          importIndex += toInsert.length();
42710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
42810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
42910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
43010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // Write the source file.
43110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      try {
432f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (in_place) {
433f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (verbose) {
434f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            System.out.printf("Renaming %s to %s%n", javafile, unannotated);
435f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
436f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          boolean success = javafile.renameTo(unannotated);
437f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (! success) {
438f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            throw new Error(String.format("Failed renaming %s to %s",
439f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst                                          javafile, unannotated));
440f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
441f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        } else {
442f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          outfile.getParentFile().mkdirs();
443f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
44410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        OutputStream output = new FileOutputStream(outfile);
445f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (verbose) {
446f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          System.out.printf("Writing %s%n", outfile);
447f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
44810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        src.write(output);
449f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        output.close();
45010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (IOException e) {
45110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.err.println("Problem while writing file " + outfile);
45210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
45310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.exit(1);
45410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
45510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
45610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  }
45710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
4585534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  ///
4595534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  /// Utility methods
4605534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  ///
4615534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
4625534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  public static String pathToString(TreePath path) {
4635534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    if (path == null)
4645534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return "null";
465f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst    return treeToString(path.getLeaf());
466f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst  }
467f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst
468f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst  public static String treeToString(Tree node) {
469f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst    String asString = node.toString();
4705534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    String oneLine = firstLine(asString);
4715534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    return "\"" + oneLine + "\"";
4725534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  }
4735534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
4745534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  /**
4755534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   * Return the first non-empty line of the string, adding an ellipsis
4765534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   * (...) if the string was truncated.
4775534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   */
4785534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  public static String firstLine(String s) {
4795534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    while (s.startsWith("\n")) {
4805534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      s = s.substring(1);
4815534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    }
4825534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    int newlineIndex = s.indexOf('\n');
4835534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    if (newlineIndex == -1) {
4845534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return s;
4855534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    } else {
4865534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return s.substring(0, newlineIndex) + "...";
4875534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    }
4885534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  }
4895534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
490aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb  /**
491aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   * Separates the annotation class from its arguments.
492aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   *
493aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   * @return given <code>@foo(bar)</code> it returns the pair <code>{ @foo, (bar) }</code>.
494aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb   */
495aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb  public static Pair<String,String> removeArgs(String s) {
496aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb    int pidx = s.indexOf("(");
497aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb    return (pidx == -1) ?
498615ec652dc360e33296fd763a4fa56e59c35b23cMichael Ernst        Pair.of(s, (String)null) :
499aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb        Pair.of(s.substring(0, pidx), s.substring(pidx));
500aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb  }
501aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb
50210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali}
503