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