Main.java revision 07a61175c257cf7fb7738d5eede16733440c1b56
110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipackage annotator; 210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.io.*; 410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.*; 510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.regex.*; 6e67b0ef921bf74296a1c601b85e2bdcca1e1a86cMichael Ernstimport plume.*; 710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 8e13e8963268c825e80bb92e310135617d7275656Eric Spishakimport annotator.find.Criteria; 910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.Insertion; 1010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.TreeFinder; 1110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source; 1210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source.CompilerException; 1310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.IndexFileSpecification; 1410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.Specification; 1510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 16f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernstimport com.sun.source.tree.*; 175534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernstimport com.sun.source.util.TreePath; 1810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 1957ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernstimport com.google.common.collect.*; 2057ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst 2110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali/** 2210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * This is the main class for the annotator, which inserts annotations in 23c15d52e959f2b3052978e40552159de77c403428Michael Ernst * Java source code. You can call it as <tt>java annotator.Main</tt> or by 24c15d52e959f2b3052978e40552159de77c403428Michael Ernst * using the shell script <tt>insert-annotations-to-source</tt>. 25c15d52e959f2b3052978e40552159de77c403428Michael Ernst * <p> 26c15d52e959f2b3052978e40552159de77c403428Michael Ernst * 27c15d52e959f2b3052978e40552159de77c403428Michael Ernst * It takes as input 2810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <ul> 2976be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl * <li>annotation (index) files, which indicate the annotations to insert</li> 3010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <li>Java source files, into which the annotator inserts annotations</li> 3110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * </ul> 3210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Use the --help option for full usage details. 3310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <p> 3410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * 3510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Annotations that are not for the specified Java files are ignored. 3610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 3710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipublic class Main { 3810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 3910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** Directory in which output files are written. */ 4010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-d <directory> Directory in which output files are written") 4110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static String outdir = "annotated/"; 4210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 43fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst /** 44fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * If true, overwrite original source files (making a backup first). 45fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * Furthermore, if the backup files already exist, they are used instead 46fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * of the .java files. This behavior permits a user to tweak the .jaif 47fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * file and re-run the annotator. 48fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * <p> 49fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * 50fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * Note that if the user runs the annotator with --in-place, makes edits, 51fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * and then re-runs the annotator with this --in-place option, those 52fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * edits are lost. Similarly, if the user runs the annotator twice in a 53fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * row with --in-place, only the last set of annotations will appear in 54fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * the codebase at the end. 55fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * <p> 56fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * 57fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * To preserve changes when using the --in-place option, first remove the 58fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * backup files. Or, use the <tt>-d .</tt> option, which makes (and 59fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * reads) no backup, instead of --in-place. 60fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst */ 61f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst @Option("-i Overwrite original source files") 62f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst public static boolean in_place = false; 63f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 6410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-h Print usage information and exit") 6510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean help = false; 6610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 6710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-a Abbreviate annotation names") 6810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean abbreviate = true; 6910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-c Insert annotations in comments") 7110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean comments = false; 7210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 73bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst @Option("-o Omit given annotation") 74bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst public static String omit_annotation; 75bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst 7610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-v Verbose (print progress information)") 7710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean verbose; 7810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("Debug (print debug information)") 8010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean debug = false; 8110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 8210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Implementation details: 8310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 1. The annotator partially compiles source 8410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // files using the compiler API (JSR-199), obtaining an AST. 8510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 2. The annotator reads the specification file, producing a set of 8610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // annotator.find.Insertions. Insertions completely specify what to 8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // write (as a String, which is ultimately translated according to the 8810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file) and how to write it (as annotator.find.Criteria). 8910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 3. It then traverses the tree, looking for nodes that satisfy the 9010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Insertion Criteria, translating the Insertion text against the 9110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file, and inserting the annotations into the source file. 9210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 9310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** 9410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Runs the annotator, parsing the source and spec files and applying 9510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * the annotations. 9610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static void main(String[] args) { 9810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 9910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 10067693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst System.out.printf("insert-annotations-to-source (%s)", 10167693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst annotations.io.classfile.ClassFileReader.INDEX_UTILS_VERSION); 10210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 10310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 104f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Options options = new Options("Main [options] ann-file... java-file...", Main.class); 10576be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl String[] file_args = options.parse_or_usage (args); 106f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 1071220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst if (debug) { 1081220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst TreeFinder.debug = true; 109e13e8963268c825e80bb92e310135617d7275656Eric Spishak Criteria.debug = true; 1101220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst } 1111220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst 11210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (help) { 11310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali options.print_usage(); 11410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(0); 11510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 11610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 117f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place && outdir != "annotated/") { // interned 118f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("The --outdir and --in-place options are mutually exclusive."); 119f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.exit(1); 120f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 121f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 12210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (file_args.length < 2) { 123f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length); 12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 12510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 12610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 12710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The insertions specified by the annotation files. 12810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> insertions = new ArrayList<Insertion>(); 12910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The Java files into which to insert. 13010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<String> javafiles = new ArrayList<String>(); 13110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 13210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String arg : file_args) { 13310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (arg.endsWith(".java")) { 13410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali javafiles.add(arg); 1358ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl } else if (arg.endsWith(".jaif") || 1368ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl arg.endsWith(".jann")) { 13710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 13810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Specification spec = new IndexFileSpecification(arg); 13910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> parsedSpec = spec.parse(); 1404735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst if (verbose || debug) { 14110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.printf("Read %d annotations from %s%n", 14210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali parsedSpec.size(), arg); 14310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 144bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst if (omit_annotation != null) { 145bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst List<Insertion> filtered = new ArrayList<Insertion>(parsedSpec.size()); 146bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst for (Insertion insertion : parsedSpec) { 147bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst if (! omit_annotation.equals(insertion.getText())) { 148bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst filtered.add(insertion); 149bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 150bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 151bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst parsedSpec = filtered; 152bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst if (verbose || debug) { 153bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst System.out.printf("After filtering: %d annotations from %s%n", 154bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst parsedSpec.size(), arg); 155bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 156bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 157bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst insertions.addAll(parsedSpec); 1582c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } catch (RuntimeException e) { 1592c4067b440b23911687e4a353584b088eccf17fbMichael Ernst if (e.getCause() != null 1602c4067b440b23911687e4a353584b088eccf17fbMichael Ernst && e.getCause() instanceof FileNotFoundException) { 1612c4067b440b23911687e4a353584b088eccf17fbMichael Ernst System.err.println("File not found: " + arg); 1622c4067b440b23911687e4a353584b088eccf17fbMichael Ernst System.exit(1); 1632c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } else { 1642c4067b440b23911687e4a353584b088eccf17fbMichael Ernst throw e; 1652c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } 16610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (FileIOException e) { 16710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Error while parsing annotation file " + arg); 16810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (e.getMessage() != null) { 16910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println(e.getMessage()); 17010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 17210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 17310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 17510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali throw new Error("Unrecognized file extension: " + arg); 17610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 17910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 180b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size()); 18110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 18210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 183b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf("Insertions:%n"); 18410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (Insertion insertion : insertions) { 185b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf(" %s%n", insertion); 18610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 18710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 18810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 18910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String javafilename : javafiles) { 19010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 19110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 19210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println("Processing " + javafilename); 19310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 19410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 195f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File javafile = new File(javafilename); 196f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 197f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File outfile; 198f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File unannotated = new File(javafilename + ".unannotated"); 199f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 200f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // It doesn't make sense to check timestamps; 201f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // if the .java.unannotated file exists, then just use it. 202f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // A user can rename that file back to just .java to cause the 203f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // .java file to be read. 204f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (unannotated.exists()) { 205f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 206f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", unannotated, javafile); 207f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 208f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = unannotated.renameTo(javafile); 209f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 210f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 211f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst unannotated, javafile)); 212f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 213f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 214f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = javafile; 215f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 216f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst String baseName; 217f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (javafile.isAbsolute()) { 218f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getName(); 219f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 220f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getPath(); 221f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 222f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = new File(outdir, baseName); 223f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 224f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 225f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Set<String> imports = new LinkedHashSet<String>(); 226f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 22710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String fileLineSep = System.getProperty("line.separator"); 22810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Source src; 22910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Get the source file, and use it to obtain parse trees. 23010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 23110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // fileLineSep is set here so that exceptions can be caught 23210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali fileLineSep = UtilMDE.inferLineSeparator(javafilename); 23310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src = new Source(javafilename); 234c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (verbose) { 235c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.printf("Parsed %s%n", javafilename); 236c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 23710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (CompilerException e) { 23810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 23910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 24010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 24110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 24210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 24310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 245c15d52e959f2b3052978e40552159de77c403428Michael Ernst int num_insertions = 0; 246c15d52e959f2b3052978e40552159de77c403428Michael Ernst 24710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (CompilationUnitTree tree : src.parse()) { 24810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 24910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Create a finder, and use it to get positions. 25010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali TreeFinder finder = new TreeFinder(tree); 2515c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst if (debug) { 2521d9ddfee01826d63c802f5356c250e3841a68e71wdietl TreeFinder.debug = true; 2535c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst } 254ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst SetMultimap<Integer, Insertion> positions = finder.getPositions(tree, insertions); 25510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 25610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Apply the positions to the source file. 257c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (debug || verbose) { 258c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.err.printf("getPositions returned %d positions in tree for %s%n", positions.size(), javafilename); 25910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 26010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 26157ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst Set<Integer> positionKeysUnsorted = positions.keySet(); 26257ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst Set<Integer> positionKeysSorted = new TreeSet<Integer>(new TreeFinder.ReverseIntegerComparator()); 26357ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst positionKeysSorted.addAll(positionKeysUnsorted); 26457ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst for (Integer pos : positionKeysSorted) { 265ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pos)); 266ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst Collections.reverse(toInsertList); 267a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst if (debug) { 268a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst System.out.printf("insertion pos: %d%n", pos); 269a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst } 270d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst assert pos >= 0 271d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst : "pos is negative: " + pos + " " + toInsertList.get(0) + " " + javafilename; 272ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst for (Insertion iToInsert : toInsertList) { 273d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak String toInsert = iToInsert.getText(comments, abbreviate); 2740f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst if (! (toInsert.startsWith("@") 27507a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak || toInsert.startsWith("extends ") 27607a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak || toInsert.startsWith("((") 27707a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak || toInsert.equals("))"))) { 27818ff42d676bd77c607f8839e1739beebd43e76d3Eric Spishak throw new Error("Illegal insertion: " + toInsert); 27900623041550b198d6a389410ea3a34687cddced5Michael Ernst } 280ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (abbreviate) { 281d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak String packageName = iToInsert.getPackageName(); 282d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak if (packageName != null) { 283d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak if (debug && !imports.contains(packageName)) { 28409e02e2c623aae704a60e026404686c147532d80Michael Ernst System.out.printf("Need import %s%n due to insertion %s%n", 285d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak packageName, toInsert); 28609e02e2c623aae704a60e026404686c147532d80Michael Ernst } 287d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak imports.add(packageName); 2880f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst } 28910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 29010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 291ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Possibly add whitespace after the insertion 292ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst boolean gotSeparateLine = false; 293ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (iToInsert.getSeparateLine()) { 294a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst // System.out.printf("getSeparateLine=true for insertion at pos %d: %s%n", pos, iToInsert); 295ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst int indentation = 0; 296ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst while ((pos - indentation != 0) 297ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // horizontal whitespace 298ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst && (src.charAt(pos-indentation-1) == ' ' 299ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\t')) { 300a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst // System.out.printf("src.charAt(pos-indentation-1 == %d-%d-1)='%s'%n", 301a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst // pos, indentation, src.charAt(pos-indentation-1)); 302ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst indentation++; 303ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 304ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if ((pos - indentation == 0) 305ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // horizontal whitespace 306ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || (src.charAt(pos-indentation-1) == '\f' 307ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\n' 308ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\r')) { 309ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = toInsert + fileLineSep + src.substring(pos-indentation, pos); 310ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst gotSeparateLine = true; 311ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 312ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 313c71d4e21017f8f4412a2d399676e358858999623Michael Ernst 314ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Possibly add a leading space before the insertion 315ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if ((! gotSeparateLine) && (pos != 0)) { 316ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst char precedingChar = src.charAt(pos-1); 317ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (! (Character.isWhitespace(precedingChar) 318ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // No space if it's the first formal or generic parameter 31907a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak // or if it's a CloseParenthesesInsertion 320ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || precedingChar == '(' 32107a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak || precedingChar == '<' 32207a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak || precedingChar == '[' 32307a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak || toInsert.equals("))"))) { 324ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = " " + toInsert; 325ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 3264735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst } 327ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst 328ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // If it's already there, don't re-insert. This is a hack! 329ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Also, I think this is already checked when constructing the 33076be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl // insertions. 331ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst int precedingTextPos = pos-toInsert.length()-1; 332ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (precedingTextPos >= 0) { 333ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst String precedingTextPlusChar 334ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst = src.getString().substring(precedingTextPos, pos); 335ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'"); 336ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length())) 337ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || toInsert.equals(precedingTextPlusChar.substring(1))) { 338ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (debug) { 339050043fa0fe1286433a9c5985f6e7f1514b3e19bwdietl System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'"); 3403e5089aea50851069fffb66a96b731c6b06a2666wdietl System.out.println("Already present, skipping"); 341ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 342ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst continue; 3434735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst } 34410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 345ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // add trailing whitespace 34609e02e2c623aae704a60e026404686c147532d80Michael Ernst // (test is not for "extends " because we just added a leading space, above) 34707a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak if ((! gotSeparateLine) && (! toInsert.startsWith(" extends ")) 34807a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak && (! toInsert.startsWith("((")) && (! toInsert.startsWith(" ((")) 34907a61175c257cf7fb7738d5eede16733440c1b56Eric Spishak && (! toInsert.equals("))"))) { 350ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = toInsert + " "; 351ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 352ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst src.insert(pos, toInsert); 353c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (verbose) { 354c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.print("."); 355c15d52e959f2b3052978e40552159de77c403428Michael Ernst num_insertions++; 356c15d52e959f2b3052978e40552159de77c403428Michael Ernst if ((num_insertions % 50) == 0) { 357c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.println(); // terminate the line that contains dots 358c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 359c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 360ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (debug) { 361ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst System.out.println("Post-insertion source: " + src.getString()); 362ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 36310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 36410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 36510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 366c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (verbose) { 367c15d52e959f2b3052978e40552159de77c403428Michael Ernst if ((num_insertions % 50) != 0) { 368c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.println(); // terminate the line that contains dots 369c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 370c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 37110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 37210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // insert import statements 37310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali { 37410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 37510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println(imports.size() + " imports to insert"); 37657ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst for (String classname : imports) { 37757ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst System.out.println(" " + classname); 37857ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst } 37910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 38010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern importPattern = Pattern.compile("(?m)^import\\b"); 38110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)"); 38210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali int importIndex = 0; // default: beginning of file 38310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String srcString = src.getString(); 38410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Matcher m; 38510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = importPattern.matcher(srcString); 38610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 38710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.start(); 38810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 38910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // if (debug) { 39010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // System.out.println("Didn't find import in " + srcString); 39110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // } 39210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = packagePattern.matcher(srcString); 39310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 39410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.end(); 39510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 39610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 39710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String classname : imports) { 39810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String toInsert = "import " + classname + ";" + fileLineSep; 39910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.insert(importIndex, toInsert); 40010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex += toInsert.length(); 40110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 40210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 40310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 40410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Write the source file. 40510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 406f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 407f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 408f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", javafile, unannotated); 409f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 410f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = javafile.renameTo(unannotated); 411f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 412f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 413f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst javafile, unannotated)); 414f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 415f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 416f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile.getParentFile().mkdirs(); 417f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 41810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali OutputStream output = new FileOutputStream(outfile); 419f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 420f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Writing %s%n", outfile); 421f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 42210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.write(output); 423f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst output.close(); 42410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 42510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Problem while writing file " + outfile); 42610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 42710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 42810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 42910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 43010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 43110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 4325534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 4335534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// Utility methods 4345534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 4355534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 4365534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String pathToString(TreePath path) { 4375534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (path == null) 4385534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "null"; 439f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst return treeToString(path.getLeaf()); 440f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst } 441f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst 442f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst public static String treeToString(Tree node) { 443f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst String asString = node.toString(); 4445534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst String oneLine = firstLine(asString); 4455534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "\"" + oneLine + "\""; 4465534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4475534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 4485534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /** 4495534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * Return the first non-empty line of the string, adding an ellipsis 4505534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * (...) if the string was truncated. 4515534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst */ 4525534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String firstLine(String s) { 4535534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst while (s.startsWith("\n")) { 4545534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst s = s.substring(1); 4555534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4565534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst int newlineIndex = s.indexOf('\n'); 4575534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (newlineIndex == -1) { 4585534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s; 4595534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } else { 4605534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s.substring(0, newlineIndex) + "..."; 4615534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4625534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4635534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 464aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb /** 465aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb * Separates the annotation class from its arguments. 466aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb * 467aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb * @return given <code>@foo(bar)</code> it returns the pair <code>{ @foo, (bar) }</code>. 468aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb */ 469aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb public static Pair<String,String> removeArgs(String s) { 470aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb int pidx = s.indexOf("("); 471aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb return (pidx == -1) ? 472615ec652dc360e33296fd763a4fa56e59c35b23cMichael Ernst Pair.of(s, (String)null) : 473aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb Pair.of(s.substring(0, pidx), s.substring(pidx)); 474aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb } 475aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb 47610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali} 477