package org.jmlspecs.openjml.jmldoc; import java.io.File; import java.io.PrintWriter; import java.util.LinkedList; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.openjml.JmlOption; import org.jmlspecs.openjml.JmlSpecs; import org.jmlspecs.openjml.Strings; import org.jmlspecs.openjml.Utils; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JavacMessages; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Options; import com.sun.tools.javadoc.Start; /** This is the class containing the entry points for the jmldoc tool. * See the material in the <A HREF="package-summary.html">package description</A> for use and design discussion. */ public class Main extends org.jmlspecs.openjml.Main { // THESE ARE NOT (YET?) USED // TODO static public String JmlTableHeadingColor = "#FFCCCC"; static public String JmlTableRowColor = "#FFFFCC"; private Main() throws Exception {} /** The entry point for command-line execution of the tool. The conventional * array of arguments contains options and their arguments, files to be * documented and any annotation processing classes. * * @param args the command-line arguments */ //@ requires \nonnullelements(args); public static void main(@NonNull String[] args) { System.exit(execute(args)); } /** * The compilation context used for JML parsing (which is different than the * context used for javadoc parsing - see the <A HREF="package.html">package * documentation</A>. */ public static Context jmlContext = new Context(); // FIXME - not thread safe /** * The programmatic interface, used to execute the tool on a set of options * and files. The return value is the value used as the exit code on the * command-line. * @param args The command line parameters. * @return The return code. */ //@ requires \nonnullelements(args); public static int execute(@NonNull String[] args) { int errorcode = com.sun.tools.javac.main.Main.Result.ERROR.exitCode; // 1 try { if (args == null) { Context context = new Context(); // This is a temporary context just for this error message. // It is not the one used for the options and compilation Log log = Log.instance(context); JavacMessages.instance(context).add(Strings.messagesJML); log.error("jmldoc.main.null.args","org.jmlspecs.openjml.jmldoc.Main"); errorcode = com.sun.tools.javac.main.Main.Result.CMDERR.exitCode; // 2 } else { // We have to interpret the -useJavaCompiler option before we start // the compiler (which does the normal option processing). // Since this is rare, we'll require that it be the first // option. boolean useJavaCompiler = args.length > 0 && args[0].equals(JmlOption.USEJAVACOMPILER.optionName()); if (useJavaCompiler) { jmlContext = null; args = processArgs(args,jmlContext); // Have to filter out all of the JML options errorcode = com.sun.tools.javadoc.Main.execute(args); } else { new org.jmlspecs.openjml.jmldoc.ConfigurationJml(); // stores an instance in a static location jmlContext = new Context(); jmlContext.put(IProgressListener.class,new PrintProgressReporter(jmlContext,System.out)); Context c = new Context(); Start jdoc = new JmlStart("jmldoc",c);//,"org.jmlspecs.openjml.jmldoc.StandardJml"); init(c); registerTools(jmlContext,new PrintWriter(System.out,true),null); Utils.instance(jmlContext).doc = true; JavacFileManager.preRegister(jmlContext); // can't create it until Log has been set up - is it? FIXME args = processArgs(args,jmlContext); errorcode = jdoc.begin(args); } } } catch (Exception e) { // Most exceptions are caught prior to this, so this will happen only for the // most catastrophic kinds of failure such as failures to initialize // properly. (You can test this by programmatically throwing an exception in the try // block above.) Context context = new Context(); // This is a temporary context just for this error message. // It is not the one used for the options and compilation Log log = Log.instance(context); JavacMessages.instance(context).add(Strings.messagesJML); log.error("jmldoc.toplevel.exception",e); e.printStackTrace(System.err); errorcode = com.sun.tools.javac.main.Main.Result.SYSERR.exitCode; // 3 } return errorcode; } public static void init(Context context) { DocEnvJml.preRegister(context); } /** Internal method used to process and remove from the argument list any * jmldoc specific arguments. This is called prior to the arguments being * processed by the Javadoc tool itself. * @param args the command-line arguments * @param context the compiler context in which the arguments apply; null if * we want to skip an JML functionality * @return a reduced list of command-line arguments */ //@ requires \nonnullelements(args); //@ ensures \nonnullelements(\result); static public @NonNull String[] processArgs(@NonNull String[] args, Context context) { Options options = Options.instance(context == null ? new Context() : context); JmlOption n=null; LinkedList<String> newargs = new LinkedList<String>(); //int cpindex = -1; int i = 0; while (i < args.length) { String a = args[i]; i++; n = JmlOption.find(a); if (n != null) { if (JmlOption.DIR.optionName().equals(a) || JmlOption.DIRS.optionName().equals(a)) { java.util.List<File> todo = new LinkedList<File>(); if (i < args.length) { todo.add(new File(args[i++])); } else { Log.instance(context).error("jml.last.option.wants.parameter",n.optionName()); } if (JmlOption.DIRS.optionName().equals(a)) { while (i<args.length && args[i].length() > 0 && args[i].charAt(0) != '-') { todo.add(new File(args[i])); i++; } } while (!todo.isEmpty()) { File file = todo.remove(0); if (file.isDirectory()) { for (File ff: file.listFiles()) { todo.add(ff); } } else if (!file.isFile()) { Log.instance(context).error("jml.option.is.not.a.file.or.dir",file); } else if (file.getName().endsWith(".java")) { // FIXME - handle checking suffixes more generically newargs.add(file.toString()); } else if (file.getName().endsWith(".jml")) { // FIXME - is this right for jml newargs.add(file.toString()); } else { // Just skip it } } } else if (JmlOption.ENDOPTIONS.optionName().equals(a)) { while (i < args.length) { newargs.add(args[i++]); } } else if (n.hasArg()) { if (i < args.length) { options.put(n.optionName(),args[i++]); } else { Log.instance(context).error("jml.last.option.wants.parameter",n.optionName()); } } else { options.put(n.optionName(),""); } } else if (a.equals("-classpath")) { newargs.add(a); options.put(a,args[i]); //cpindex = newargs.size(); newargs.add(args[i++]); } else if (a.equals("-sourcepath")) { newargs.add(a); options.put(a,args[i]); newargs.add(args[i++]); } else { newargs.add(a); } } if (context != null) { // FIXME Utils.instance(context).jmlverbose = Options.instance(context).get(JmlOption.JMLDEBUG.optionName()) != null; JmlSpecs.instance(context).initializeSpecsPath(); if (JmlOption.isOption(context,JmlOption.INTERNALRUNTIME)) { appendRuntime(context); } } String[] result = newargs.toArray(new String[newargs.size()]); // append annotation definitions onto result[cpindex] - what if there is none - need to append it to classpath definition return result; } } /* TODO * GENERAL: *** Clean up all files * Should we use the .xml to produce the JML * LAYOUT: * Extra space around PRE * Font for annotations * Font and color for specs * Indentation of JML specs for methods depends on presence of javadocs (and tags?) * Extra horizontal rule after the JML class specifications; spacing in general * Rationalize newlines in annotations ENVIRONMENT *** Auto inclusion of annotations on the class path for the javadoc part INCOMPLETE *** Verify all pretty printing works * Pretty printing (as in the source) and wrapping of long expressions DOCUMENTING MODEL CLASSES: * Toplevel Model classes * Generate pages and links for model classes, interfaces - link them into package listings and next/previous links * model classes: inherited specs * Javadoc comments for model classes and interfaces EMPTY MEMBER LIST PROBLEM: ** CEmpty: Need jml field summary when there are no java fields ** CEmpty: need jml nested class summary when there are no java nested classes ** JML inherited fields/methods are not shown if there are no Java inherited fields/methods OTHER MISSING DOC * Method: arguments and results do not have JML comments * Fields: JML comments do not turn into annotations * Classes, Nested classes: check that JML comments are listed as annotations ** Show specification sequence; show location of specs in spec sequence? * JML elements are not in index and in tree and in use and in deprecated OTHER * Read BML * Create jmldoc from binary + spec files * Test auxiliary jml files * Test redundant; test heavyweight clauses JAVADOC FLAGS * Test use of -noqualifier, -use, -overview, -sourcepath, -classpath, -specs * Test use of -nodeprecated, -nodeprecatedlist, -notree, -noindex, -sourcefile (links in refines?) * Test use of -nocomment on JML documentation BUGS ** Enum bug - documents, but reports errors *** standalone bug - what is this? ** classpath bug (testSourcePath2?) ** class A should list methods inherited from BB - problem when using -protected *** Nested model class containing a method declaration with no body - whether or not is model ** Crashes that occurred when Comparable.jml was bad * Test: visibility with hiding of inherited members *** Poor recovery from parsing errors *** Does not support anonymous classes within JML annotations ** quantifiers and lbl expressions without parentheses *** White space in .. ranges * ** retest/report bug in DocEnv with unnamed packages * * Note: problem with javadocs applying to both Jml and Java elements * Note: including annotations in signatures of summaries, even though javadoc does not * Note: synonyms are mapped to a canonical name (e.g. pre -> requires) * * */