Main.java revision 00623041550b198d6a389410ea3a34687cddced5
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 39f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // It's already possible to emulate this (without the backing up) via 40f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // -d . 41f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // but the --in-place argument is more convenient and explicit. 42f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst /** Directory in which output files are written. */ 43f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst @Option("-i Overwrite original source files") 44f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst public static boolean in_place = false; 45f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 4610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-h Print usage information and exit") 4710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean help = false; 4810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 4910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-a Abbreviate annotation names") 5010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean abbreviate = true; 5110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 5210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-c Insert annotations in comments") 5310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean comments = false; 5410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 5510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-v Verbose (print progress information)") 5610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean verbose; 5710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 5810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("Debug (print debug information)") 5910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean debug = false; 6010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 6110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Implementation details: 6210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 1. The annotator partially compiles source 6310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // files using the compiler API (JSR-199), obtaining an AST. 6410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 2. The annotator reads the specification file, producing a set of 6510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // annotator.find.Insertions. Insertions completely specify what to 6610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // write (as a String, which is ultimately translated according to the 6710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file) and how to write it (as annotator.find.Criteria). 6810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 3. It then traverses the tree, looking for nodes that satisfy the 6910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Insertion Criteria, translating the Insertion text against the 7010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file, and inserting the annotations into the source file. 7110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** 7310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Runs the annotator, parsing the source and spec files and applying 7410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * the annotations. 7510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 7610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static void main(String[] args) { 7710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 7910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println(INDEX_UTILS_VERSION); 8010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 8110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 82f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Options options = new Options("Main [options] ann-file... java-file...", Main.class); 83f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst String[] file_args = options.parse_and_usage (args); 84f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 8510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (help) { 8610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali options.print_usage(); 8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(0); 8810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 8910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 90f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place && outdir != "annotated/") { // interned 91f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("The --outdir and --in-place options are mutually exclusive."); 92f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.exit(1); 93f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 94f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 9510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (file_args.length < 2) { 96f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length); 9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 9810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 9910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 10010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The insertions specified by the annotation files. 10110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> insertions = new ArrayList<Insertion>(); 10210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The Java files into which to insert. 10310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<String> javafiles = new ArrayList<String>(); 10410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 10510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String arg : file_args) { 10610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (arg.endsWith(".java")) { 10710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali javafiles.add(arg); 10810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else if (arg.endsWith(".jaif")) { 10910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 11010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Specification spec = new IndexFileSpecification(arg); 11110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> parsedSpec = spec.parse(); 11210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali insertions.addAll(parsedSpec); 11310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 11410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.printf("Read %d annotations from %s%n", 11510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali parsedSpec.size(), arg); 11610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 11710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (FileIOException e) { 11810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Error while parsing annotation file " + arg); 11910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (e.getMessage() != null) { 12010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println(e.getMessage()); 12110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 12210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 12310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 12510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 12610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali throw new Error("Unrecognized file extension: " + arg); 12710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 12810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 12910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 13010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 13110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size()); 13210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 13310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 13410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.printf("Insertions:%n"); 13510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (Insertion insertion : insertions) { 13610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.printf(" %s%n", insertion); 13710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 13810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 13910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 14010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String javafilename : javafiles) { 14110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 14210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 14310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println("Processing " + javafilename); 14410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 14510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 146f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File javafile = new File(javafilename); 147f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 148f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File outfile; 149f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File unannotated = new File(javafilename + ".unannotated"); 150f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 151f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // It doesn't make sense to check timestamps; 152f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // if the .java.unannotated file exists, then just use it. 153f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // A user can rename that file back to just .java to cause the 154f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // .java file to be read. 155f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (unannotated.exists()) { 156f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 157f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", unannotated, javafile); 158f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 159f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = unannotated.renameTo(javafile); 160f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 161f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 162f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst unannotated, javafile)); 163f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 164f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 165f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = javafile; 166f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 167f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst String baseName; 168f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (javafile.isAbsolute()) { 169f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getName(); 170f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 171f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getPath(); 172f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 173f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = new File(outdir, baseName); 174f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 175f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 176f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Set<String> imports = new LinkedHashSet<String>(); 177f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 17810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String fileLineSep = System.getProperty("line.separator"); 17910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Source src; 18010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Get the source file, and use it to obtain parse trees. 18110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 18210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // fileLineSep is set here so that exceptions can be caught 18310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali fileLineSep = UtilMDE.inferLineSeparator(javafilename); 18410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src = new Source(javafilename); 18510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (CompilerException e) { 18610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 18710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 18810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 18910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 19010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 19110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 19210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 19310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (CompilationUnitTree tree : src.parse()) { 19410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 19510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Create a finder, and use it to get positions. 19610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali TreeFinder finder = new TreeFinder(tree); 19710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Map<Integer, String> positions = finder.getPositions(tree, insertions); 19810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 19910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Apply the positions to the source file. 20010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 20110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.printf("%d positions in tree for %s%n", positions.size(), javafilename); 20210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 20310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 20410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (Integer pos : positions.keySet()) { 20510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String toInsert = positions.get(pos).trim(); 20610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (! toInsert.startsWith("@")) { 20710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali throw new Error("Insertion doesn't start with '@': " + toInsert); 20810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 20910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (abbreviate) { 21000623041550b198d6a389410ea3a34687cddced5Michael Ernst int nameEnd = toInsert.indexOf("("); 21100623041550b198d6a389410ea3a34687cddced5Michael Ernst if (nameEnd == -1) { 21200623041550b198d6a389410ea3a34687cddced5Michael Ernst nameEnd = toInsert.length(); 21300623041550b198d6a389410ea3a34687cddced5Michael Ernst } 21400623041550b198d6a389410ea3a34687cddced5Michael Ernst int dotIndex = toInsert.lastIndexOf(".", nameEnd); 21510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (dotIndex != -1) { 21600623041550b198d6a389410ea3a34687cddced5Michael Ernst imports.add(toInsert.substring(1, nameEnd)); 21710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali toInsert = "@" + toInsert.substring(dotIndex + 1); 21810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 21910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 22010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (comments) { 22110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali toInsert = "/*" + toInsert + "*/"; 22210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 22310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 22410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali char precedingChar = src.charAt(pos-1); 22510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (! (Character.isWhitespace(precedingChar) 22610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // No space if it's the first formal or generic parameter 22710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali || precedingChar == '(' 22810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali || precedingChar == '<')) { 22910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali toInsert = " " + toInsert; 23010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 23110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // If it's already there, don't re-insert. This is a hack! 23210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String precedingTextPlusChar 23310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali = src.getString().substring(pos-toInsert.length()-1, pos); 23410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'"); 23510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length())) 23610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali || toInsert.equals(precedingTextPlusChar.substring(1))) { 23710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 23810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println("Already present, skipping"); 23910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali continue; 24110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.insert(pos, toInsert + " "); 24310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 24410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println("Post-insertion source: " + src.getString()); 24510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 24910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // insert import statements 25010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali { 25110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 25210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println(imports.size() + " imports to insert"); 25310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 25410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern importPattern = Pattern.compile("(?m)^import\\b"); 25510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)"); 25610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali int importIndex = 0; // default: beginning of file 25710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String srcString = src.getString(); 25810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Matcher m; 25910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = importPattern.matcher(srcString); 26010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 26110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.start(); 26210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 26310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // if (debug) { 26410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // System.out.println("Didn't find import in " + srcString); 26510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // } 26610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = packagePattern.matcher(srcString); 26710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 26810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.end(); 26910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 27010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 27110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String lineSep = System.getProperty("line.separator"); 27210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String classname : imports) { 27310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String toInsert = "import " + classname + ";" + fileLineSep; 27410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.insert(importIndex, toInsert); 27510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex += toInsert.length(); 27610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 27710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 27810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 27910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Write the source file. 28010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 281f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 282f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 283f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", javafile, unannotated); 284f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 285f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = javafile.renameTo(unannotated); 286f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 287f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 288f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst javafile, unannotated)); 289f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 290f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 291f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile.getParentFile().mkdirs(); 292f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 29310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali OutputStream output = new FileOutputStream(outfile); 294f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 295f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Writing %s%n", outfile); 296f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 29710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.write(output); 298f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst output.close(); 29910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 30010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Problem while writing file " + outfile); 30110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 30210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 30310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 30410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 30510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 30610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 3075534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 3085534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// Utility methods 3095534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 3105534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 3115534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String pathToString(TreePath path) { 3125534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (path == null) 3135534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "null"; 314f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst return treeToString(path.getLeaf()); 315f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst } 316f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst 317f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst public static String treeToString(Tree node) { 318f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst String asString = node.toString(); 3195534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst String oneLine = firstLine(asString); 3205534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "\"" + oneLine + "\""; 3215534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 3225534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 3235534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /** 3245534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * Return the first non-empty line of the string, adding an ellipsis 3255534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * (...) if the string was truncated. 3265534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst */ 3275534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String firstLine(String s) { 3285534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst while (s.startsWith("\n")) { 3295534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst s = s.substring(1); 3305534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 3315534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst int newlineIndex = s.indexOf('\n'); 3325534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (newlineIndex == -1) { 3335534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s; 3345534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } else { 3355534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s.substring(0, newlineIndex) + "..."; 3365534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 3375534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 3385534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 33910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali} 340