package annotations.io.classfile;
/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/
import java.io.*;
import plume.*;
import com.sun.tools.javac.main.CommandLine;
import org.objectweb.asm.ClassReader;
import annotations.el.AScene;
import annotations.io.IndexFileWriter;
/**
* A <code> ClassFileReader </code> provides methods for reading in annotations
* from a class file into an {@link annotations.el.AScene}.
*/
public class ClassFileReader {
public static final String INDEX_UTILS_VERSION
= "Annotation File Utilities v3.6.44";
@Option("-b omit annotations from bridge (compiler-created) methods")
public static boolean ignore_bridge_methods = false;
@Option("-h print usage information and exit")
public static boolean help = false;
@Option("-v print version information and exit")
public static boolean version = false;
private static String linesep = System.getProperty("line.separator");
static String usage
= "extract-annotations [options] class1 class2 ..."
+ linesep
+ "Each argument is a class a.b.C (that is on your classpath) or a class file"
+ linesep
+ "a/b/C.class. Extracts the annotations from each such argument and prints"
+ linesep
+ "them in index-file format to a.b.C.jaif . Arguments beginning with a"
+ linesep
+ "single '@' are interpreted as argument files to be read and expanded into"
+ linesep
+ "the command line. Options:";
/**
* From the command line, read annotations from a class file and write
* them to an index file. Also see the Anncat tool, which is more
* versatile (and which calls this as a subroutine).
* <p>
*
* For usage information, supply the -h or --help option.
* <p>
*
* For programmatic access to this tool, use the read() methods instead.
* <p>
*
* @param args options and classes to analyze;
* @throws IOException if a class file cannot be found
*/
public static void main(String[] args) throws IOException {
Options options = new Options(usage, ClassFileReader.class);
String[] file_args;
try {
String[] cl_args = CommandLine.parse(args);
file_args = options.parse_or_usage(cl_args);
} catch (IOException ex) {
System.err.println(ex);
System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\".");
System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'.");
System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)");
file_args = null; // Eclipse compiler issue workaround
System.exit(1);
}
if (version) {
System.out.printf("extract-annotations (%s)", INDEX_UTILS_VERSION);
}
if (help) {
options.print_usage();
}
if (version || help) {
System.exit(-1);
}
if (file_args.length == 0) {
options.print_usage("No arguments given.");
System.exit(-1);
}
// check args for well-formed names
for (String arg : file_args) {
if (!checkClass(arg)) {
System.exit(-1);
}
}
for (String origName : file_args) {
System.out.println("reading: " + origName);
String className = origName;
if (origName.endsWith(".class")) {
origName = origName.replace(".class", "");
}
AScene scene = new AScene();
try {
if (className.endsWith(".class")) {
read(scene, className);
} else {
readFromClass(scene, className);
}
String outputFile = origName + ".jaif";
System.out.println("printing results to : " + outputFile);
IndexFileWriter.write(scene, outputFile);
} catch (IOException e) {
System.out.println("There was an error in reading class: " + origName);
System.out.println(
"Did you ensure that this class is on your classpath?");
return;
} catch (Exception e) {
System.out.println("Unknown error trying to extract annotations from: " +
origName);
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("Please submit a bug report at");
System.out.println(" https://github.com/typetools/annotation-tools/issues");
System.out.println("Be sure to include a copy of the output trace, instructions on how");
System.out.println("to reproduce this error, and all input files. Thanks!");
return;
}
}
}
/**
* If s is not a valid representation of a class, print a warning message
* and return false.
*/
public static boolean checkClass(String arg) {
// check for invalid class file paths with '.'
if (!arg.contains(".class") && arg.contains("/")) {
System.out.println("Error: bad class " + arg);
System.out.println("Use a fully qualified class name such as java.lang.Object");
System.out.println("or a filename such as .../path/to/MyClass.class");
return false;
}
return true;
}
/**
* Reads the annotations from the class file <code> fileName </code>
* and inserts them into <code> scene </code>.
* <code> fileName </code> should be a file name that can be resolved from
* the current working directory, which means it should end in ".class"
* for standard Java class files.
*
* @param scene the scene into which the annotations should be inserted
* @param fileName the file name of the class the annotations should be
* read from
* @throws IOException if there is a problem reading from
* <code> fileName </code>
*/
public static void read(AScene scene, String fileName)
throws IOException {
read(scene, new FileInputStream(fileName));
}
/**
* Reads the annotations from the class <code> className </code>,
* assumed to be in your classpath,
* and inserts them into <code> scene </code>.
*
* @param scene the scene into which the annotations should be inserted
* @param className the name of the class to read in
* @throws IOException if there is a problem reading <code> className </code>
*/
public static void readFromClass(AScene scene, String className)
throws IOException {
read(scene, new ClassReader(className));
}
/**
* Reads the annotations from the class file <code> fileName </code>
* and inserts them into <code> scene </code>.
*
* @param scene the scene into which the annotations should be inserted
* @param in an input stream containing the class that the annotations
* should be read from
* @throws IOException if there is a problem reading from <code> in </code>
*/
public static void read(AScene scene, InputStream in)
throws IOException {
read(scene, new ClassReader(in));
}
public static void read(AScene scene, ClassReader cr) {
ClassAnnotationSceneReader ca =
new ClassAnnotationSceneReader(cr, scene, ignore_bridge_methods);
cr.accept(ca, true);
}
}