1 package annotations.io.classfile; 2 3 /*>>> 4 import org.checkerframework.checker.nullness.qual.*; 5 */ 6 7 import java.io.*; 8 9 import plume.*; 10 11 import com.sun.tools.javac.main.CommandLine; 12 13 import org.objectweb.asm.ClassReader; 14 15 import annotations.el.AScene; 16 import annotations.io.IndexFileWriter; 17 18 /** 19 * A <code> ClassFileReader </code> provides methods for reading in annotations 20 * from a class file into an {@link annotations.el.AScene}. 21 */ 22 public class ClassFileReader { 23 24 public static final String INDEX_UTILS_VERSION 25 = "Annotation File Utilities v3.6.44"; 26 27 @Option("-b omit annotations from bridge (compiler-created) methods") 28 public static boolean ignore_bridge_methods = false; 29 30 @Option("-h print usage information and exit") 31 public static boolean help = false; 32 33 @Option("-v print version information and exit") 34 public static boolean version = false; 35 36 private static String linesep = System.getProperty("line.separator"); 37 38 static String usage 39 = "extract-annotations [options] class1 class2 ..." 40 + linesep 41 + "Each argument is a class a.b.C (that is on your classpath) or a class file" 42 + linesep 43 + "a/b/C.class. Extracts the annotations from each such argument and prints" 44 + linesep 45 + "them in index-file format to a.b.C.jaif . Arguments beginning with a" 46 + linesep 47 + "single '@' are interpreted as argument files to be read and expanded into" 48 + linesep 49 + "the command line. Options:"; 50 51 /** 52 * From the command line, read annotations from a class file and write 53 * them to an index file. Also see the Anncat tool, which is more 54 * versatile (and which calls this as a subroutine). 55 * <p> 56 * 57 * For usage information, supply the -h or --help option. 58 * <p> 59 * 60 * For programmatic access to this tool, use the read() methods instead. 61 * <p> 62 * 63 * @param args options and classes to analyze; 64 * @throws IOException if a class file cannot be found 65 */ main(String[] args)66 public static void main(String[] args) throws IOException { 67 Options options = new Options(usage, ClassFileReader.class); 68 String[] file_args; 69 70 try { 71 String[] cl_args = CommandLine.parse(args); 72 file_args = options.parse_or_usage(cl_args); 73 } catch (IOException ex) { 74 System.err.println(ex); 75 System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\"."); 76 System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'."); 77 System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)"); 78 file_args = null; // Eclipse compiler issue workaround 79 System.exit(1); 80 } 81 82 if (version) { 83 System.out.printf("extract-annotations (%s)", INDEX_UTILS_VERSION); 84 } 85 if (help) { 86 options.print_usage(); 87 } 88 if (version || help) { 89 System.exit(-1); 90 } 91 92 if (file_args.length == 0) { 93 options.print_usage("No arguments given."); 94 System.exit(-1); 95 } 96 97 // check args for well-formed names 98 for (String arg : file_args) { 99 if (!checkClass(arg)) { 100 System.exit(-1); 101 } 102 } 103 104 for (String origName : file_args) { 105 System.out.println("reading: " + origName); 106 String className = origName; 107 if (origName.endsWith(".class")) { 108 origName = origName.replace(".class", ""); 109 } 110 111 AScene scene = new AScene(); 112 try { 113 if (className.endsWith(".class")) { 114 read(scene, className); 115 } else { 116 readFromClass(scene, className); 117 } 118 String outputFile = origName + ".jaif"; 119 System.out.println("printing results to : " + outputFile); 120 IndexFileWriter.write(scene, outputFile); 121 } catch (IOException e) { 122 System.out.println("There was an error in reading class: " + origName); 123 System.out.println( 124 "Did you ensure that this class is on your classpath?"); 125 return; 126 } catch (Exception e) { 127 System.out.println("Unknown error trying to extract annotations from: " + 128 origName); 129 System.out.println(e.getMessage()); 130 e.printStackTrace(); 131 System.out.println("Please submit a bug report at"); 132 System.out.println(" https://github.com/typetools/annotation-tools/issues"); 133 System.out.println("Be sure to include a copy of the output trace, instructions on how"); 134 System.out.println("to reproduce this error, and all input files. Thanks!"); 135 return; 136 } 137 } 138 } 139 140 /** 141 * If s is not a valid representation of a class, print a warning message 142 * and return false. 143 */ checkClass(String arg)144 public static boolean checkClass(String arg) { 145 // check for invalid class file paths with '.' 146 if (!arg.contains(".class") && arg.contains("/")) { 147 System.out.println("Error: bad class " + arg); 148 System.out.println("Use a fully qualified class name such as java.lang.Object"); 149 System.out.println("or a filename such as .../path/to/MyClass.class"); 150 return false; 151 } 152 return true; 153 } 154 155 /** 156 * Reads the annotations from the class file <code> fileName </code> 157 * and inserts them into <code> scene </code>. 158 * <code> fileName </code> should be a file name that can be resolved from 159 * the current working directory, which means it should end in ".class" 160 * for standard Java class files. 161 * 162 * @param scene the scene into which the annotations should be inserted 163 * @param fileName the file name of the class the annotations should be 164 * read from 165 * @throws IOException if there is a problem reading from 166 * <code> fileName </code> 167 */ read(AScene scene, String fileName)168 public static void read(AScene scene, String fileName) 169 throws IOException { 170 read(scene, new FileInputStream(fileName)); 171 } 172 173 /** 174 * Reads the annotations from the class <code> className </code>, 175 * assumed to be in your classpath, 176 * and inserts them into <code> scene </code>. 177 * 178 * @param scene the scene into which the annotations should be inserted 179 * @param className the name of the class to read in 180 * @throws IOException if there is a problem reading <code> className </code> 181 */ readFromClass(AScene scene, String className)182 public static void readFromClass(AScene scene, String className) 183 throws IOException { 184 read(scene, new ClassReader(className)); 185 } 186 187 /** 188 * Reads the annotations from the class file <code> fileName </code> 189 * and inserts them into <code> scene </code>. 190 * 191 * @param scene the scene into which the annotations should be inserted 192 * @param in an input stream containing the class that the annotations 193 * should be read from 194 * @throws IOException if there is a problem reading from <code> in </code> 195 */ read(AScene scene, InputStream in)196 public static void read(AScene scene, InputStream in) 197 throws IOException { 198 read(scene, new ClassReader(in)); 199 } 200 read(AScene scene, ClassReader cr)201 public static void read(AScene scene, ClassReader cr) { 202 ClassAnnotationSceneReader ca = 203 new ClassAnnotationSceneReader(cr, scene, ignore_bridge_methods); 204 cr.accept(ca, true); 205 } 206 207 } 208