Main.java revision 18ff42d676bd77c607f8839e1739beebd43e76d3
110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipackage annotator; 210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.io.*; 410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.*; 510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport java.util.regex.*; 6e67b0ef921bf74296a1c601b85e2bdcca1e1a86cMichael Ernstimport plume.*; 710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.Insertion; 910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.find.TreeFinder; 1010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source; 1110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.Source.CompilerException; 1210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.IndexFileSpecification; 1310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Aliimport annotator.specification.Specification; 1410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 15f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernstimport com.sun.source.tree.*; 165534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernstimport com.sun.source.util.TreePath; 1710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 1857ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernstimport com.google.common.collect.*; 1957ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst 2010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali/** 2110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * This is the main class for the annotator, which inserts annotations in 22c15d52e959f2b3052978e40552159de77c403428Michael Ernst * Java source code. You can call it as <tt>java annotator.Main</tt> or by 23c15d52e959f2b3052978e40552159de77c403428Michael Ernst * using the shell script <tt>insert-annotations-to-source</tt>. 24c15d52e959f2b3052978e40552159de77c403428Michael Ernst * <p> 25c15d52e959f2b3052978e40552159de77c403428Michael Ernst * 26c15d52e959f2b3052978e40552159de77c403428Michael Ernst * It takes as input 2710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <ul> 2876be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl * <li>annotation (index) files, which indicate the annotations to insert</li> 2910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <li>Java source files, into which the annotator inserts annotations</li> 3010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * </ul> 3110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Use the --help option for full usage details. 3210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * <p> 3310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * 3410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Annotations that are not for the specified Java files are ignored. 3510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 3610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Alipublic class Main { 3710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 3810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** Directory in which output files are written. */ 3910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-d <directory> Directory in which output files are written") 4010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static String outdir = "annotated/"; 4110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 42fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst /** 43fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * If true, overwrite original source files (making a backup first). 44fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * Furthermore, if the backup files already exist, they are used instead 45fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * of the .java files. This behavior permits a user to tweak the .jaif 46fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * file and re-run the annotator. 47fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * <p> 48fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * 49fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * Note that if the user runs the annotator with --in-place, makes edits, 50fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * and then re-runs the annotator with this --in-place option, those 51fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * edits are lost. Similarly, if the user runs the annotator twice in a 52fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * row with --in-place, only the last set of annotations will appear in 53fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * the codebase at the end. 54fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * <p> 55fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * 56fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * To preserve changes when using the --in-place option, first remove the 57fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * backup files. Or, use the <tt>-d .</tt> option, which makes (and 58fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst * reads) no backup, instead of --in-place. 59fd37e8455f846ac7d34c53ebe1079797599ebd4fMichael Ernst */ 60f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst @Option("-i Overwrite original source files") 61f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst public static boolean in_place = false; 62f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 6310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-h Print usage information and exit") 6410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean help = false; 6510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 6610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-a Abbreviate annotation names") 6710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean abbreviate = true; 6810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 6910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-c Insert annotations in comments") 7010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean comments = false; 7110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 72bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst @Option("-o Omit given annotation") 73bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst public static String omit_annotation; 74bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst 7510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("-v Verbose (print progress information)") 7610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean verbose; 7710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 7810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali @Option("Debug (print debug information)") 7910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static boolean debug = false; 8010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 8110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Implementation details: 8210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 1. The annotator partially compiles source 8310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // files using the compiler API (JSR-199), obtaining an AST. 8410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 2. The annotator reads the specification file, producing a set of 8510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // annotator.find.Insertions. Insertions completely specify what to 8610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // write (as a String, which is ultimately translated according to the 8710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file) and how to write it (as annotator.find.Criteria). 8810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // 3. It then traverses the tree, looking for nodes that satisfy the 8910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Insertion Criteria, translating the Insertion text against the 9010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // keyword file, and inserting the annotations into the source file. 9110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 9210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali /** 9310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * Runs the annotator, parsing the source and spec files and applying 9410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali * the annotations. 9510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali */ 9610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali public static void main(String[] args) { 9710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 9810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 9967693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst System.out.printf("insert-annotations-to-source (%s)", 10067693622be4bed63a07df6f0ce34f7c8bd52baf5Michael Ernst annotations.io.classfile.ClassFileReader.INDEX_UTILS_VERSION); 10110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 10210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 103f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Options options = new Options("Main [options] ann-file... java-file...", Main.class); 10476be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl String[] file_args = options.parse_or_usage (args); 105f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 1061220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst if (debug) { 1071220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst TreeFinder.debug = true; 1081220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst } 1091220f0e5dd7642f90aab557b3bda8e177ce06316Michael Ernst 11010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (help) { 11110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali options.print_usage(); 11210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(0); 11310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 11410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 115f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place && outdir != "annotated/") { // interned 116f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("The --outdir and --in-place options are mutually exclusive."); 117f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.exit(1); 118f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 119f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 12010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (file_args.length < 2) { 121f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length); 12210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 12310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 12410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 12510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The insertions specified by the annotation files. 12610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> insertions = new ArrayList<Insertion>(); 12710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // The Java files into which to insert. 12810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<String> javafiles = new ArrayList<String>(); 12910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 13010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String arg : file_args) { 13110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (arg.endsWith(".java")) { 13210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali javafiles.add(arg); 1338ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl } else if (arg.endsWith(".jaif") || 1348ef7819881d25642b26b60a274f9c148d4356e3dWerner Dietl arg.endsWith(".jann")) { 13510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 13610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Specification spec = new IndexFileSpecification(arg); 13710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali List<Insertion> parsedSpec = spec.parse(); 1384735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst if (verbose || debug) { 13910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.printf("Read %d annotations from %s%n", 14010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali parsedSpec.size(), arg); 14110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 142bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst if (omit_annotation != null) { 143bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst List<Insertion> filtered = new ArrayList<Insertion>(parsedSpec.size()); 144bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst for (Insertion insertion : parsedSpec) { 145bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst if (! omit_annotation.equals(insertion.getText())) { 146bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst filtered.add(insertion); 147bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 148bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 149bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst parsedSpec = filtered; 150bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst if (verbose || debug) { 151bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst System.out.printf("After filtering: %d annotations from %s%n", 152bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst parsedSpec.size(), arg); 153bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 154bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst } 155bbda219dcbd366aaf1786384a8171e0409fd5a11Michael Ernst insertions.addAll(parsedSpec); 1562c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } catch (RuntimeException e) { 1572c4067b440b23911687e4a353584b088eccf17fbMichael Ernst if (e.getCause() != null 1582c4067b440b23911687e4a353584b088eccf17fbMichael Ernst && e.getCause() instanceof FileNotFoundException) { 1592c4067b440b23911687e4a353584b088eccf17fbMichael Ernst System.err.println("File not found: " + arg); 1602c4067b440b23911687e4a353584b088eccf17fbMichael Ernst System.exit(1); 1612c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } else { 1622c4067b440b23911687e4a353584b088eccf17fbMichael Ernst throw e; 1632c4067b440b23911687e4a353584b088eccf17fbMichael Ernst } 16410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (FileIOException e) { 16510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Error while parsing annotation file " + arg); 16610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (e.getMessage() != null) { 16710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println(e.getMessage()); 16810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 16910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 17010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 17110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 17310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali throw new Error("Unrecognized file extension: " + arg); 17410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 17610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 17710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 178b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf("%d insertions, %d .java files%n", insertions.size(), javafiles.size()); 17910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 18010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 181b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf("Insertions:%n"); 18210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (Insertion insertion : insertions) { 183b3ca4989984b4c0c85719bbac77ec93477099b32Michael Ernst System.out.printf(" %s%n", insertion); 18410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 18510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 18610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 18710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String javafilename : javafiles) { 18810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 18910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (verbose) { 19010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println("Processing " + javafilename); 19110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 19210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 193f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File javafile = new File(javafilename); 194f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 195f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File outfile; 196f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst File unannotated = new File(javafilename + ".unannotated"); 197f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 198f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // It doesn't make sense to check timestamps; 199f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // if the .java.unannotated file exists, then just use it. 200f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // A user can rename that file back to just .java to cause the 201f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst // .java file to be read. 202f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (unannotated.exists()) { 203f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 204f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", unannotated, javafile); 205f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 206f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = unannotated.renameTo(javafile); 207f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 208f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 209f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst unannotated, javafile)); 210f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 211f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 212f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = javafile; 213f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 214f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst String baseName; 215f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (javafile.isAbsolute()) { 216f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getName(); 217f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 218f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst baseName = javafile.getPath(); 219f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 220f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile = new File(outdir, baseName); 221f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 222f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 223f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst Set<String> imports = new LinkedHashSet<String>(); 224f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst 22510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String fileLineSep = System.getProperty("line.separator"); 22610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Source src; 22710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Get the source file, and use it to obtain parse trees. 22810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 22910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // fileLineSep is set here so that exceptions can be caught 23010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali fileLineSep = UtilMDE.inferLineSeparator(javafilename); 23110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src = new Source(javafilename); 232c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (verbose) { 233c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.printf("Parsed %s%n", javafilename); 234c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 23510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (CompilerException e) { 23610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 23710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 23810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 23910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 24010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali return; 24110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 24210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 243c15d52e959f2b3052978e40552159de77c403428Michael Ernst int num_insertions = 0; 244c15d52e959f2b3052978e40552159de77c403428Michael Ernst 24510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (CompilationUnitTree tree : src.parse()) { 24610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 24710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Create a finder, and use it to get positions. 24810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali TreeFinder finder = new TreeFinder(tree); 2495c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst if (debug) { 2501d9ddfee01826d63c802f5356c250e3841a68e71wdietl TreeFinder.debug = true; 2515c6791fb104c246b6a37144b59dbfaad1f800de6Michael Ernst } 252ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst SetMultimap<Integer, Insertion> positions = finder.getPositions(tree, insertions); 25310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 25410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Apply the positions to the source file. 255c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (debug || verbose) { 256c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.err.printf("getPositions returned %d positions in tree for %s%n", positions.size(), javafilename); 25710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 25810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 25957ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst Set<Integer> positionKeysUnsorted = positions.keySet(); 26057ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst Set<Integer> positionKeysSorted = new TreeSet<Integer>(new TreeFinder.ReverseIntegerComparator()); 26157ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst positionKeysSorted.addAll(positionKeysUnsorted); 26257ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst for (Integer pos : positionKeysSorted) { 263ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pos)); 264ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst Collections.reverse(toInsertList); 265a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst if (debug) { 266a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst System.out.printf("insertion pos: %d%n", pos); 267a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst } 268d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst assert pos >= 0 269d2c419e0399881f6e63361a637ccb90cc4a898bdMichael Ernst : "pos is negative: " + pos + " " + toInsertList.get(0) + " " + javafilename; 270ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst for (Insertion iToInsert : toInsertList) { 271d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak String toInsert = iToInsert.getText(comments, abbreviate); 2720f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst if (! (toInsert.startsWith("@") 273d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak || toInsert.startsWith("extends "))) { 27418ff42d676bd77c607f8839e1739beebd43e76d3Eric Spishak throw new Error("Illegal insertion: " + toInsert); 27500623041550b198d6a389410ea3a34687cddced5Michael Ernst } 276ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (abbreviate) { 277d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak String packageName = iToInsert.getPackageName(); 278d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak if (packageName != null) { 279d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak if (debug && !imports.contains(packageName)) { 28009e02e2c623aae704a60e026404686c147532d80Michael Ernst System.out.printf("Need import %s%n due to insertion %s%n", 281d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak packageName, toInsert); 28209e02e2c623aae704a60e026404686c147532d80Michael Ernst } 283d6e7a874aafafbcc2dd63cd89c58b7483366bc08Eric Spishak imports.add(packageName); 2840f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst } 28510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 28610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 287ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Possibly add whitespace after the insertion 288ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst boolean gotSeparateLine = false; 289ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (iToInsert.getSeparateLine()) { 290a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst // System.out.printf("getSeparateLine=true for insertion at pos %d: %s%n", pos, iToInsert); 291ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst int indentation = 0; 292ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst while ((pos - indentation != 0) 293ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // horizontal whitespace 294ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst && (src.charAt(pos-indentation-1) == ' ' 295ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\t')) { 296a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst // System.out.printf("src.charAt(pos-indentation-1 == %d-%d-1)='%s'%n", 297a1b0d6cb20867adf4fad0359336510469353ad29Michael Ernst // pos, indentation, src.charAt(pos-indentation-1)); 298ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst indentation++; 299ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 300ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if ((pos - indentation == 0) 301ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // horizontal whitespace 302ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || (src.charAt(pos-indentation-1) == '\f' 303ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\n' 304ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || src.charAt(pos-indentation-1) == '\r')) { 305ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = toInsert + fileLineSep + src.substring(pos-indentation, pos); 306ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst gotSeparateLine = true; 307ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 308ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 309c71d4e21017f8f4412a2d399676e358858999623Michael Ernst 310ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Possibly add a leading space before the insertion 311ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if ((! gotSeparateLine) && (pos != 0)) { 312ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst char precedingChar = src.charAt(pos-1); 313ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (! (Character.isWhitespace(precedingChar) 314ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // No space if it's the first formal or generic parameter 315ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || precedingChar == '(' 316ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || precedingChar == '<')) { 317ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = " " + toInsert; 318ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 3194735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst } 320ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst 321ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // If it's already there, don't re-insert. This is a hack! 322ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // Also, I think this is already checked when constructing the 32376be24f6310e0f8e8120e223e14bc0b4690408d4Werner Dietl // insertions. 324ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst int precedingTextPos = pos-toInsert.length()-1; 325ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (precedingTextPos >= 0) { 326ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst String precedingTextPlusChar 327ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst = src.getString().substring(precedingTextPos, pos); 328ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'"); 329ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (toInsert.equals(precedingTextPlusChar.substring(0, toInsert.length())) 330ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst || toInsert.equals(precedingTextPlusChar.substring(1))) { 331ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (debug) { 332050043fa0fe1286433a9c5985f6e7f1514b3e19bwdietl System.out.println("Inserting " + toInsert + " at " + pos + " in code of length " + src.getString().length() + " with preceding text '" + precedingTextPlusChar + "'"); 3333e5089aea50851069fffb66a96b731c6b06a2666wdietl System.out.println("Already present, skipping"); 334ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 335ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst continue; 3364735bdd95fe3025e721476ae821d0aca6127f80aMichael Ernst } 33710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 338ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst // add trailing whitespace 33909e02e2c623aae704a60e026404686c147532d80Michael Ernst // (test is not for "extends " because we just added a leading space, above) 3400f7ed8e9f456276945e3cc04203c31dbce61a0b5Michael Ernst if ((! gotSeparateLine) && (! toInsert.startsWith(" extends "))) { 341ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst toInsert = toInsert + " "; 342ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 343ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst src.insert(pos, toInsert); 344c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (verbose) { 345c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.print("."); 346c15d52e959f2b3052978e40552159de77c403428Michael Ernst num_insertions++; 347c15d52e959f2b3052978e40552159de77c403428Michael Ernst if ((num_insertions % 50) == 0) { 348c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.println(); // terminate the line that contains dots 349c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 350c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 351ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst if (debug) { 352ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst System.out.println("Post-insertion source: " + src.getString()); 353ed468b7eb950854ef28a3407e1887dfec12fee67Michael Ernst } 35410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 35510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 35610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 357c15d52e959f2b3052978e40552159de77c403428Michael Ernst if (verbose) { 358c15d52e959f2b3052978e40552159de77c403428Michael Ernst if ((num_insertions % 50) != 0) { 359c15d52e959f2b3052978e40552159de77c403428Michael Ernst System.out.println(); // terminate the line that contains dots 360c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 361c15d52e959f2b3052978e40552159de77c403428Michael Ernst } 36210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 36310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // insert import statements 36410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali { 36510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (debug) { 36610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.out.println(imports.size() + " imports to insert"); 36757ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst for (String classname : imports) { 36857ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst System.out.println(" " + classname); 36957ea23519ad96aca616e5fe41b4a1db896b91096Michael Ernst } 37010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 37110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern importPattern = Pattern.compile("(?m)^import\\b"); 37210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)"); 37310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali int importIndex = 0; // default: beginning of file 37410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String srcString = src.getString(); 37510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali Matcher m; 37610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = importPattern.matcher(srcString); 37710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 37810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.start(); 37910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } else { 38010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // if (debug) { 38110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // System.out.println("Didn't find import in " + srcString); 38210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // } 38310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali m = packagePattern.matcher(srcString); 38410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali if (m.find()) { 38510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex = m.end(); 38610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 38710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 38810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali for (String classname : imports) { 38910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali String toInsert = "import " + classname + ";" + fileLineSep; 39010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.insert(importIndex, toInsert); 39110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali importIndex += toInsert.length(); 39210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 39310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 39410353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 39510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali // Write the source file. 39610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali try { 397f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (in_place) { 398f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 399f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Renaming %s to %s%n", javafile, unannotated); 400f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 401f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst boolean success = javafile.renameTo(unannotated); 402f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (! success) { 403f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst throw new Error(String.format("Failed renaming %s to %s", 404f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst javafile, unannotated)); 405f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 406f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } else { 407f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst outfile.getParentFile().mkdirs(); 408f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 40910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali OutputStream output = new FileOutputStream(outfile); 410f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst if (verbose) { 411f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst System.out.printf("Writing %s%n", outfile); 412f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst } 41310353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali src.write(output); 414f5fbd2ee90bc394cb18e8cf7dbaf3ecbbd4e7ad7Michael Ernst output.close(); 41510353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } catch (IOException e) { 41610353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.err.println("Problem while writing file " + outfile); 41710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali e.printStackTrace(); 41810353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali System.exit(1); 41910353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 42010353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 42110353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali } 42210353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali 4235534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 4245534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// Utility methods 4255534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /// 4265534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 4275534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String pathToString(TreePath path) { 4285534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (path == null) 4295534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "null"; 430f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst return treeToString(path.getLeaf()); 431f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst } 432f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst 433f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst public static String treeToString(Tree node) { 434f8955bfb5d46ff31634832d5143a95f2faaa14beMichael Ernst String asString = node.toString(); 4355534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst String oneLine = firstLine(asString); 4365534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return "\"" + oneLine + "\""; 4375534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4385534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 4395534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst /** 4405534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * Return the first non-empty line of the string, adding an ellipsis 4415534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst * (...) if the string was truncated. 4425534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst */ 4435534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst public static String firstLine(String s) { 4445534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst while (s.startsWith("\n")) { 4455534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst s = s.substring(1); 4465534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4475534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst int newlineIndex = s.indexOf('\n'); 4485534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst if (newlineIndex == -1) { 4495534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s; 4505534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } else { 4515534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst return s.substring(0, newlineIndex) + "..."; 4525534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4535534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst } 4545534e50f6966d53ba8c8104a4bf847df0cd53409Michael Ernst 455aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb /** 456aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb * Separates the annotation class from its arguments. 457aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb * 458aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb * @return given <code>@foo(bar)</code> it returns the pair <code>{ @foo, (bar) }</code>. 459aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb */ 460aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb public static Pair<String,String> removeArgs(String s) { 461aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb int pidx = s.indexOf("("); 462aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb return (pidx == -1) ? 463615ec652dc360e33296fd763a4fa56e59c35b23cMichael Ernst Pair.of(s, (String)null) : 464aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb Pair.of(s.substring(0, pidx), s.substring(pidx)); 465aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb } 466aad82c9e3793937a33a4e6b2cd2e38f6478e0704mdb 46710353ed766fc48a0af6bd33d934439e695c03e3Mahmood Ali} 468