Main.java revision 4735bdd95fe3025e721476ae821d0aca6127f80a
110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipackage annotator;
210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.io.*;
410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.*;
510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.regex.*;
610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport utilMDE.*;
710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.Insertion;
910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.TreeFinder;
1010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source;
1110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source.CompilerException;
1210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.IndexFileSpecification;
1310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.Specification;
1410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
15f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernstimport com.sun.source.tree.*;
165534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernstimport com.sun.source.util.TreePath;
1710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
1810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali/**
1910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * This is the main class for the annotator, which inserts annotations in
2010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Java source code.  It takes as input
2110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <ul>
2210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *   <li>annotation (index) files, which indcate the annotations to insert</li>
2310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *   <li>Java source files, into which the annotator inserts annotations</li>
2410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * </ul>
2510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Use the --help option for full usage details.
2610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <p>
2710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali *
2810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Annotations that are not for the specified Java files are ignored.
2910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */
3010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipublic class Main {
3110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
3210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static final String INDEX_UTILS_VERSION =
331dcec9cfcd5a43838f923a2a1c8c6ee3a269e6aeMichael Ernst    "Annotation file utilities: insert-annotations-to-source v2.3";
3410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
3510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  /** Directory in which output files are written. */
3610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-d <directory> Directory in which output files are written")
3710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static String outdir = "annotated/";
3810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
39775cdcc180e3a3c9bfb9b2be624f177a181f6347Michael Ernst  // It's already possible to emulate this via
40f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  //   -d .
41775cdcc180e3a3c9bfb9b2be624f177a181f6347Michael Ernst  // but the --in-place argument is more convenient and explicit, and it
42775cdcc180e3a3c9bfb9b2be624f177a181f6347Michael Ernst  // makes a backup so it can be run multiple times without restoring the
43775cdcc180e3a3c9bfb9b2be624f177a181f6347Michael Ernst  // original state of the .java files.
44f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  /** Directory in which output files are written. */
45f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  @Option("-i Overwrite original source files")
46f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst  public static boolean in_place = false;
47f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
4810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-h Print usage information and exit")
4910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean help = false;
5010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
5110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-a Abbreviate annotation names")
5210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean abbreviate = true;
5310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
5410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-c Insert annotations in comments")
5510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean comments = false;
5610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
5710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("-v Verbose (print progress information)")
5810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean verbose;
5910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
6010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  @Option("Debug (print debug information)")
6110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static boolean debug = false;
6210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
6310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  // Implementation details:
6410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  1. The annotator partially compiles source
6510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     files using the compiler API (JSR-199), obtaining an AST.
6610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  2. The annotator reads the specification file, producing a set of
6710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     annotator.find.Insertions.  Insertions completely specify what to
6810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     write (as a String, which is ultimately translated according to the
6910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     keyword file) and how to write it (as annotator.find.Criteria).
7010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //  3. It then traverses the tree, looking for nodes that satisfy the
7110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     Insertion Criteria, translating the Insertion text against the
7210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  //     keyword file, and inserting the annotations into the source file.
7310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
7410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  /**
7510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   * Runs the annotator, parsing the source and spec files and applying
7610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   * the annotations.
7710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali   */
7810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  public static void main(String[] args) {
7910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
8010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (verbose) {
8110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.out.println(INDEX_UTILS_VERSION);
8210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
8310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
84f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    Options options = new Options("Main [options] ann-file... java-file...", Main.class);
85f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    String[] file_args = options.parse_and_usage (args);
86f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (help) {
8810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      options.print_usage();
8910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.exit(0);
9010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
9110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
92f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    if (in_place && outdir != "annotated/") { // interned
93f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      options.print_usage("The --outdir and --in-place options are mutually exclusive.");
94f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      System.exit(1);
95f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst    }
96f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (file_args.length < 2) {
98f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length);
9910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.exit(1);
10010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
10110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
10210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    // The insertions specified by the annotation files.
10310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    List<Insertion> insertions = new ArrayList<Insertion>();
10410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    // The Java files into which to insert.
10510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    List<String> javafiles = new ArrayList<String>();
10610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
10710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    for (String arg : file_args) {
10810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      if (arg.endsWith(".java")) {
10910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        javafiles.add(arg);
11010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } else if (arg.endsWith(".jaif")) {
11110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        try {
11210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          Specification spec = new IndexFileSpecification(arg);
11310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          List<Insertion> parsedSpec = spec.parse();
11410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          insertions.addAll(parsedSpec);
1154735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          if (verbose || debug) {
11610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            System.out.printf("Read %d annotations from %s%n",
11710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali                              parsedSpec.size(), arg);
11810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
11910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        } catch (FileIOException e) {
12010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.err.println("Error while parsing annotation file " + arg);
12110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (e.getMessage() != null) {
12210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            System.err.println(e.getMessage());
12310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          e.printStackTrace();
12510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.exit(1);
12610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
12710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } else {
12810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        throw new Error("Unrecognized file extension: " + arg);
12910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
13010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
13110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
13210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (debug) {
13310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.err.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size());
13410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
13510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    if (debug) {
13610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      System.err.printf("Insertions:%n");
13710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      for (Insertion insertion : insertions) {
13810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.err.printf("  %s%n", insertion);
13910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
14010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
14110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
14210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    for (String javafilename : javafiles) {
14310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
14410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      if (verbose) {
14510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.out.println("Processing " + javafilename);
14610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
14710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
148f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File javafile = new File(javafilename);
149f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
150f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File outfile;
151f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      File unannotated = new File(javafilename + ".unannotated");
152f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      if (in_place) {
153f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // It doesn't make sense to check timestamps;
154f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // if the .java.unannotated file exists, then just use it.
155f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // A user can rename that file back to just .java to cause the
156f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        // .java file to be read.
157f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (unannotated.exists()) {
158f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (verbose) {
159f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            System.out.printf("Renaming %s to %s%n", unannotated, javafile);
160f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
161f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          boolean success = unannotated.renameTo(javafile);
162f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (! success) {
163f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            throw new Error(String.format("Failed renaming %s to %s",
164f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst                                          unannotated, javafile));
165f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
166f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
167f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        outfile = javafile;
168f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      } else {
169f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        String baseName;
170f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (javafile.isAbsolute()) {
171f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          baseName = javafile.getName();
172f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        } else {
173f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          baseName = javafile.getPath();
174f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
175f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        outfile = new File(outdir, baseName);
176f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      }
177f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
178f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst      Set<String> imports = new LinkedHashSet<String>();
179f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst
18010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      String fileLineSep = System.getProperty("line.separator");
18110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      Source src;
18210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // Get the source file, and use it to obtain parse trees.
18310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      try {
18410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // fileLineSep is set here so that exceptions can be caught
18510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        fileLineSep = UtilMDE.inferLineSeparator(javafilename);
18610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        src = new Source(javafilename);
18710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (CompilerException e) {
18810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
18910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        return;
19010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (IOException e) {
19110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
19210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        return;
19310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
19410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
19510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      for (CompilationUnitTree tree : src.parse()) {
19610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
19710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // Create a finder, and use it to get positions.
19810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        TreeFinder finder = new TreeFinder(tree);
19910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Map<Integer, String> positions = finder.getPositions(tree, insertions);
20010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
20110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        // Apply the positions to the source file.
20210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (debug) {
20310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.err.printf("%d positions in tree for %s%n", positions.size(), javafilename);
20410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
20510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
20610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        for (Integer pos : positions.keySet()) {
20710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          String toInsert = positions.get(pos).trim();
20810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (! toInsert.startsWith("@")) {
20910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            throw new Error("Insertion doesn't start with '@': " + toInsert);
21010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
21110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (abbreviate) {
21200623041550b198d6a389410ea3a34687cddced5Michael Ernst            int nameEnd = toInsert.indexOf("(");
21300623041550b198d6a389410ea3a34687cddced5Michael Ernst            if (nameEnd == -1) {
21400623041550b198d6a389410ea3a34687cddced5Michael Ernst              nameEnd = toInsert.length();
21500623041550b198d6a389410ea3a34687cddced5Michael Ernst            }
21600623041550b198d6a389410ea3a34687cddced5Michael Ernst            int dotIndex = toInsert.lastIndexOf(".", nameEnd);
21710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            if (dotIndex != -1) {
21800623041550b198d6a389410ea3a34687cddced5Michael Ernst              imports.add(toInsert.substring(1, nameEnd));
21910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali              toInsert = "@" + toInsert.substring(dotIndex + 1);
22010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            }
22110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
22210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (comments) {
22310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            toInsert = "/*" + toInsert + "*/";
22410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
22510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
2264735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          // Possibly add a leading space before the insertion
2274735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          if (pos != 0) {
2284735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            char precedingChar = src.charAt(pos-1);
2294735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            if (! (Character.isWhitespace(precedingChar)
2304735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst                   // No space if it's the first formal or generic parameter
2314735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst                   || precedingChar == '('
2324735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst                   || precedingChar == '<')) {
2334735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              toInsert = " " + toInsert;
2344735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            }
23510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
23610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // If it's already there, don't re-insert.  This is a hack!
2374735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          int precedingTextPos = pos-toInsert.length()-1;
2384735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          if (precedingTextPos >= 0) {
2394735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            String precedingTextPlusChar
2404735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              = src.getString().substring(precedingTextPos, pos);
2414735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'");
2424735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length()))
2434735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst                || toInsert.equals(precedingTextPlusChar.substring(1))) {
2444735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              if (debug) {
2454735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst                System.out.println("Already present, skipping");
2464735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              }
2474735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst              continue;
24810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            }
24910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
2504735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          // add trailing whitespace
2514735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          if (pos==0) {
2524735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            toInsert = toInsert + fileLineSep;
2534735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          } else {
2544735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst            toInsert = toInsert + " ";
2554735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          }
2564735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst          src.insert(pos, toInsert);
25710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (debug) {
25810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            System.out.println("Post-insertion source: " + src.getString());
25910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
26010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
26110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
26210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
26310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // insert import statements
26410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      {
26510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (debug) {
26610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          System.out.println(imports.size() + " imports to insert");
26710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
26810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Pattern importPattern = Pattern.compile("(?m)^import\\b");
26910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)");
27010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        int importIndex = 0;      // default: beginning of file
27110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        String srcString = src.getString();
27210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        Matcher m;
27310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        m = importPattern.matcher(srcString);
27410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        if (m.find()) {
27510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          importIndex = m.start();
27610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        } else {
27710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // if (debug) {
27810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          //   System.out.println("Didn't find import in " + srcString);
27910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          // }
28010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          m = packagePattern.matcher(srcString);
28110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          if (m.find()) {
28210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali            importIndex = m.end();
28310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          }
28410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
28510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        String lineSep = System.getProperty("line.separator");
28610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        for (String classname : imports) {
28710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          String toInsert = "import " + classname + ";" + fileLineSep;
28810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          src.insert(importIndex, toInsert);
28910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali          importIndex += toInsert.length();
29010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        }
29110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
29210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
29310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      // Write the source file.
29410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      try {
295f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (in_place) {
296f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (verbose) {
297f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            System.out.printf("Renaming %s to %s%n", javafile, unannotated);
298f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
299f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          boolean success = javafile.renameTo(unannotated);
300f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          if (! success) {
301f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst            throw new Error(String.format("Failed renaming %s to %s",
302f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst                                          javafile, unannotated));
303f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          }
304f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        } else {
305f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          outfile.getParentFile().mkdirs();
306f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
30710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        OutputStream output = new FileOutputStream(outfile);
308f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        if (verbose) {
309f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst          System.out.printf("Writing %s%n", outfile);
310f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        }
31110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        src.write(output);
312f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst        output.close();
31310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      } catch (IOException e) {
31410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.err.println("Problem while writing file " + outfile);
31510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        e.printStackTrace();
31610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali        System.exit(1);
31710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali      }
31810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali    }
31910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali  }
32010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali
3215534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  ///
3225534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  /// Utility methods
3235534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  ///
3245534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
3255534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  public static String pathToString(TreePath path) {
3265534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    if (path == null)
3275534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return "null";
328f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst    return treeToString(path.getLeaf());
329f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst  }
330f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst
331f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst  public static String treeToString(Tree node) {
332f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst    String asString = node.toString();
3335534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    String oneLine = firstLine(asString);
3345534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    return "\"" + oneLine + "\"";
3355534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  }
3365534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
3375534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  /**
3385534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   * Return the first non-empty line of the string, adding an ellipsis
3395534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   * (...) if the string was truncated.
3405534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst   */
3415534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  public static String firstLine(String s) {
3425534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    while (s.startsWith("\n")) {
3435534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      s = s.substring(1);
3445534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    }
3455534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    int newlineIndex = s.indexOf('\n');
3465534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    if (newlineIndex == -1) {
3475534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return s;
3485534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    } else {
3495534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst      return s.substring(0, newlineIndex) + "...";
3505534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst    }
3515534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst  }
3525534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst
35310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali}
354