Main.java revision e67b0ef921bf74296a1c601b85e2bdcca1e1a86c
110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipackage annotator; 210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.io.*; 410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.*; 510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.regex.*; 6e67b0ef921bf74296a1c601b85e2bdcca1e1a86cMichael Ernstimport plume.*; 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 1857ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernstimport com.google.common.collect.*; 1957ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst 2010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali/** 2110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * This is the main class for the annotator, which inserts annotations in 2210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Java source code. It takes as input 2310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <ul> 2410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <li>annotation (index) files, which indcate the annotations to insert</li> 2510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <li>Java source files, into which the annotator inserts annotations</li> 2610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * </ul> 2710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Use the --help option for full usage details. 2810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <p> 2910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * 3010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Annotations that are not for the specified Java files are ignored. 3110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 3210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipublic class Main { 3310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 3410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static final String INDEX_UTILS_VERSION = 355f7cd572aafcf0cc8181bb099e80ef5cc2874628Michael Ernst "Annotation file utilities: insert-annotations-to-source v3.0"; 3610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 3710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** Directory in which output files are written. */ 3810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-d <directory> Directory in which output files are written") 3910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static String outdir = "annotated/"; 4010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 41fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst /** 42fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * If true, overwrite original source files (making a backup first). 43fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * Furthermore, if the backup files already exist, they are used instead 44fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * of the .java files. This behavior permits a user to tweak the .jaif 45fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * file and re-run the annotator. 46fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * <p> 47fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * 48fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * Note that if the user runs the annotator with --in-place, makes edits, 49fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * and then re-runs the annotator with this --in-place option, those 50fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * edits are lost. Similarly, if the user runs the annotator twice in a 51fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * row with --in-place, only the last set of annotations will appear in 52fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * the codebase at the end. 53fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * <p> 54fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * 55fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * To preserve changes when using the --in-place option, first remove the 56fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * backup files. Or, use the <tt>-d .</tt> option, which makes (and 57fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * reads) no backup, instead of --in-place. 58fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst */ 59f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst @Option("-i Overwrite original source files") 60f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst public static boolean in_place = false; 61f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 6210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-h Print usage information and exit") 6310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean help = false; 6410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 6510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-a Abbreviate annotation names") 6610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean abbreviate = true; 6710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 6810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-c Insert annotations in comments") 6910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean comments = false; 7010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-v Verbose (print progress information)") 7210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean verbose; 7310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("Debug (print debug information)") 7510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean debug = false; 7610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Implementation details: 7810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 1. The annotator partially compiles source 7910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // files using the compiler API (JSR-199), obtaining an AST. 8010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 2. The annotator reads the specification file, producing a set of 8110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // annotator.find.Insertions. Insertions completely specify what to 8210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // write (as a String, which is ultimately translated according to the 8310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file) and how to write it (as annotator.find.Criteria). 8410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 3. It then traverses the tree, looking for nodes that satisfy the 8510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Insertion Criteria, translating the Insertion text against the 8610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file, and inserting the annotations into the source file. 8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 8810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** 8910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Runs the annotator, parsing the source and spec files and applying 9010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * the annotations. 9110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 9210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static void main(String[] args) { 9310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 9410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 9510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println(INDEX_UTILS_VERSION); 9610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 98f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Options options = new Options("Main [options] ann-file... java-file...", Main.class); 99f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst String[] file_args = options.parse_and_usage (args); 100f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 1011220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst if (debug) { 1021220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst TreeFinder.debug = true; 1031220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst } 1041220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst 10510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (help) { 10610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali options.print_usage(); 10710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(0); 10810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 10910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 110f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place && outdir != "annotated/") { // interned 111f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("The --outdir and --in-place options are mutually exclusive."); 112f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.exit(1); 113f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 114f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 11510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (file_args.length < 2) { 116f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length); 11710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 11810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 11910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 12010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The insertions specified by the annotation files. 12110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> insertions = new ArrayList<Insertion>(); 12210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The Java files into which to insert. 12310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<String> javafiles = new ArrayList<String>(); 12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 12510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String arg : file_args) { 12610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (arg.endsWith(".java")) { 12710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali javafiles.add(arg); 12810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else if (arg.endsWith(".jaif")) { 12910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 13010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Specification spec = new IndexFileSpecification(arg); 13110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> parsedSpec = spec.parse(); 13210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali insertions.addAll(parsedSpec); 1334735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst if (verbose || debug) { 13410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.printf("Read %d annotations from %s%n", 13510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali parsedSpec.size(), arg); 13610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 1372c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } catch (RuntimeException e) { 1382c4067b440b23911687e4a353584b088eccf17fbMichael Ernst if (e.getCause() != null 1392c4067b440b23911687e4a353584b088eccf17fbMichael Ernst && e.getCause() instanceof FileNotFoundException) { 1402c4067b440b23911687e4a353584b088eccf17fbMichael Ernst System.err.println("File not found: " + arg); 1412c4067b440b23911687e4a353584b088eccf17fbMichael Ernst System.exit(1); 1422c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } else { 1432c4067b440b23911687e4a353584b088eccf17fbMichael Ernst throw e; 1442c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } 14510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (FileIOException e) { 14610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Error while parsing annotation file " + arg); 14710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (e.getMessage() != null) { 14810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println(e.getMessage()); 14910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 15010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 15110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 15210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 15310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 15410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali throw new Error("Unrecognized file extension: " + arg); 15510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 15610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 15710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 15810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 159b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size()); 16010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 16110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 162b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf("Insertions:%n"); 16310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (Insertion insertion : insertions) { 164b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf(" %s%n", insertion); 16510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 16610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 16710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 16810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String javafilename : javafiles) { 16910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 17010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 17110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println("Processing " + javafilename); 17210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 174f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File javafile = new File(javafilename); 175f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 176f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File outfile; 177f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File unannotated = new File(javafilename + ".unannotated"); 178f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 179f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // It doesn't make sense to check timestamps; 180f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // if the .java.unannotated file exists, then just use it. 181f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // A user can rename that file back to just .java to cause the 182f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // .java file to be read. 183f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (unannotated.exists()) { 184f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 185f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", unannotated, javafile); 186f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 187f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = unannotated.renameTo(javafile); 188f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 189f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 190f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst unannotated, javafile)); 191f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 192f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 193f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = javafile; 194f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 195f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst String baseName; 196f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (javafile.isAbsolute()) { 197f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getName(); 198f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 199f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getPath(); 200f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 201f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = new File(outdir, baseName); 202f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 203f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 204f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Set<String> imports = new LinkedHashSet<String>(); 205f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 20610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String fileLineSep = System.getProperty("line.separator"); 20710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Source src; 20810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Get the source file, and use it to obtain parse trees. 20910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 21010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // fileLineSep is set here so that exceptions can be caught 21110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali fileLineSep = UtilMDE.inferLineSeparator(javafilename); 21210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src = new Source(javafilename); 21310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (CompilerException e) { 21410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 21510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 21610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 21710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 21810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 21910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 22010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 22110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (CompilationUnitTree tree : src.parse()) { 22210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 22310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Create a finder, and use it to get positions. 22410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali TreeFinder finder = new TreeFinder(tree); 2255c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst if (debug) { 2265c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst finder.debug = true; 2275c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst } 228ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst SetMultimap<Integer, Insertion> positions = finder.getPositions(tree, insertions); 22910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 23010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Apply the positions to the source file. 23110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 23210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.printf("%d positions in tree for %s%n", positions.size(), javafilename); 23310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 23410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 23557ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst Set<Integer> positionKeysUnsorted = positions.keySet(); 23657ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst Set<Integer> positionKeysSorted = new TreeSet<Integer>(new TreeFinder.ReverseIntegerComparator()); 23757ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst positionKeysSorted.addAll(positionKeysUnsorted); 23857ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst for (Integer pos : positionKeysSorted) { 239ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pos)); 240ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst Collections.reverse(toInsertList); 241d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst assert pos >= 0 242d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst : "pos is negative: " + pos + " " + toInsertList.get(0) + " " + javafilename; 243ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst for (Insertion iToInsert : toInsertList) { 244ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst String toInsert = iToInsert.getText(); 245ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (! toInsert.startsWith("@")) { 246ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst throw new Error("Insertion doesn't start with '@': " + toInsert); 24700623041550b198d6a389410ea3a34687cddced5Michael Ernst } 248ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (abbreviate) { 2493f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst Pair<String,String> ps = removePackage(toInsert); 2503f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst if (ps.a != null) { 2513f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst imports.add(ps.a); 252ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 2533f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst toInsert = ps.b; 254ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 255ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (comments) { 256ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = "/*" + toInsert + "*/"; 25710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 25810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 259ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Possibly add whitespace after the insertion 260ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst boolean gotSeparateLine = false; 261ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (iToInsert.getSeparateLine()) { 262ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst int indentation = 0; 263ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst while ((pos - indentation != 0) 264ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // horizontal whitespace 265ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst && (src.charAt(pos-indentation-1) == ' ' 266ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\t')) { 267ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst indentation++; 268ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 269ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if ((pos - indentation == 0) 270ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // horizontal whitespace 271ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || (src.charAt(pos-indentation-1) == '\f' 272ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\n' 273ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\r')) { 274ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = toInsert + fileLineSep + src.substring(pos-indentation, pos); 275ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst gotSeparateLine = true; 276ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 277ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 278c71d4e21017f8f4412a2d399676e358858999623Michael Ernst 279ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Possibly add a leading space before the insertion 280ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if ((! gotSeparateLine) && (pos != 0)) { 281ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst char precedingChar = src.charAt(pos-1); 282ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (! (Character.isWhitespace(precedingChar) 283ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // No space if it's the first formal or generic parameter 284ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || precedingChar == '(' 285ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || precedingChar == '<')) { 286ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = " " + toInsert; 287ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 2884735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst } 289ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst 290ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // If it's already there, don't re-insert. This is a hack! 291ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Also, I think this is already checked when constructing the 292ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // innertions. 293ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst int precedingTextPos = pos-toInsert.length()-1; 294ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (precedingTextPos >= 0) { 295ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst String precedingTextPlusChar 296ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst = src.getString().substring(precedingTextPos, pos); 297ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'"); 298ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length())) 299ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || toInsert.equals(precedingTextPlusChar.substring(1))) { 300ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (debug) { 301ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst System.out.println("Already present, skipping"); 302ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 303ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst continue; 3044735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst } 30510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 306ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // add trailing whitespace 307ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (! gotSeparateLine) { 308ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = toInsert + " "; 309ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 310ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst src.insert(pos, toInsert); 311ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (debug) { 312ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst System.out.println("Post-insertion source: " + src.getString()); 313ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 31410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 31510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 31610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 31710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 31810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // insert import statements 31910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali { 32010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 32110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println(imports.size() + " imports to insert"); 32257ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst for (String classname : imports) { 32357ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst System.out.println(" " + classname); 32457ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst } 32510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 32610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern importPattern = Pattern.compile("(?m)^import\\b"); 32710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)"); 32810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali int importIndex = 0; // default: beginning of file 32910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String srcString = src.getString(); 33010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Matcher m; 33110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = importPattern.matcher(srcString); 33210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 33310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.start(); 33410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 33510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // if (debug) { 33610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // System.out.println("Didn't find import in " + srcString); 33710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // } 33810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = packagePattern.matcher(srcString); 33910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 34010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.end(); 34110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 34210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 34310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String lineSep = System.getProperty("line.separator"); 34410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String classname : imports) { 34510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String toInsert = "import " + classname + ";" + fileLineSep; 34610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.insert(importIndex, toInsert); 34710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex += toInsert.length(); 34810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 34910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 35010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 35110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Write the source file. 35210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 353f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 354f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 355f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", javafile, unannotated); 356f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 357f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = javafile.renameTo(unannotated); 358f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 359f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 360f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst javafile, unannotated)); 361f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 362f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 363f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile.getParentFile().mkdirs(); 364f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 36510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali OutputStream output = new FileOutputStream(outfile); 366f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 367f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Writing %s%n", outfile); 368f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 36910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.write(output); 370f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst output.close(); 37110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 37210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Problem while writing file " + outfile); 37310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 37410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 37510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 37610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 37710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 37810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 3795534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 3805534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// Utility methods 3815534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 3825534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 3835534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String pathToString(TreePath path) { 3845534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (path == null) 3855534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "null"; 386f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst return treeToString(path.getLeaf()); 387f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst } 388f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst 389f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst public static String treeToString(Tree node) { 390f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst String asString = node.toString(); 3915534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst String oneLine = firstLine(asString); 3925534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "\"" + oneLine + "\""; 3935534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 3945534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 3955534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /** 3965534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * Return the first non-empty line of the string, adding an ellipsis 3975534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * (...) if the string was truncated. 3985534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst */ 3995534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String firstLine(String s) { 4005534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst while (s.startsWith("\n")) { 4015534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst s = s.substring(1); 4025534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4035534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst int newlineIndex = s.indexOf('\n'); 4045534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (newlineIndex == -1) { 4055534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s; 4065534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } else { 4075534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s.substring(0, newlineIndex) + "..."; 4085534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4095534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4105534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 4113f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst /** Remove the leading package. */ 4123f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst public static Pair<String,String> removePackage(String s) { 4133f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst int nameEnd = s.indexOf("("); 4143f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst if (nameEnd == -1) { 4153f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst nameEnd = s.length(); 4163f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst } 4173f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst int dotIndex = s.lastIndexOf(".", nameEnd); 4183f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst if (dotIndex != -1) { 4193f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst String packageName = s.substring(0, nameEnd); 4203f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst if (packageName.startsWith("@")) { 4213f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst return Pair.of(packageName.substring(1), 4223f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst "@" + s.substring(dotIndex + 1)); 4233f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst } else { 4243f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst return Pair.of(packageName, 4253f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst s.substring(dotIndex + 1)); 4263f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst } 4273f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst } else { 4283f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst return Pair.of((String)null, s); 4293f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst } 4303f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst } 4313f7a095516f29e07e5c1923b9201de0a7881596fMichael Ernst 43210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali} 433