package org.jmlspecs.openjml.jmldoc;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.Processor;
import javax.tools.JavaFileObject;
import org.jmlspecs.openjml.JmlCompiler;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.RootDoc;
import com.sun.tools.doclets.formats.html.HtmlDoclet;
import com.sun.tools.doclets.internal.toolkit.AbstractDoclet;
import com.sun.tools.doclets.internal.toolkit.util.ClassTree;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.jvm.ClassReader;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Log.WriterKind;
import com.sun.tools.javadoc.ClassDocImpl;
/** Extends the parent class in order to add in JML functionality. The parent
* class does not expect to be extended, but to do otherwise would mean
* replicating too much code.
* @author David R. Cok
*
*/
public class HtmlJmlDoclet extends HtmlDoclet {
/** Starts in the standard way, by instantiating this class and calling
* the non-static start method.
* @param root the root of the javadoc class document tree
* @return true if the process executed without error
*/
public static boolean start(RootDoc root) {
HtmlDoclet doclet = new HtmlJmlDoclet();
return doclet.start(doclet, root);
}
protected void startGeneration(RootDoc root) throws Exception { // DRC - changed from private to protected
if (root.classes().length == 0) {
configuration.message.
error("doclet.No_Public_Classes_To_Document");
return;
}
//doJmlParsing(root);
super.startGeneration(root);
}
/** Overrides in order to declare this a valid doclet, since the parent class
* only likes itself.
*/
@Override
protected boolean isValidDoclet(AbstractDoclet doclet) {
return true;
}
/** Overrides in order to parse all of the JML files in order to get the
* specs, prior to generating class files.
*/
@Override
protected void generateClassFiles(RootDoc root, ClassTree classtree) {
doJmlParsing(root);
// TODO - control with a flag?
// Context context = org.jmlspecs.openjml.jmldoc.Main.jmlContext;
// if (Log.instance(context).nerrors > 0) {
// throw new Messager.ExitJavadoc(); // Aborts Javadoc if there are any errors
// }
// Instead we proceed to document whatever we can despite perhaps isolated errors
super.generateClassFiles(root,classtree);
}
/** The helper method that does all of the JML parsing, by collecting all of
* the class symbols generated by javadoc's analysis of the input files,
* and then initiating a compilation with a new compilation context on all
* of the input files.
* @param root the javadoc class tree
*/
protected void doJmlParsing(RootDoc root) {
Context context = org.jmlspecs.openjml.jmldoc.Main.jmlContext;
ClassReader.instance(context); // Instantiates the class reader, which needs to happen before the Symbol table
ListBuffer<JavaFileObject> list = new ListBuffer<JavaFileObject>();
Set<JavaFileObject> set = new HashSet<JavaFileObject>();
String[] packageNames = configuration.classDocCatalog.packageNames();
for (int packageNameIndex = 0; packageNameIndex < packageNames.length;
packageNameIndex++) {
for (ClassDoc classDoc : configuration.classDocCatalog.allClasses(
packageNames[packageNameIndex])) {
ClassSymbol oldsym = ((ClassDocImpl)classDoc).tsym;
if (set.add(oldsym.sourcefile)) { // true if not already present
list.append(oldsym.sourcefile);
}
}
}
PackageDoc[] packages = root.specifiedPackages();
for (int i = 0; i < packages.length; i++) {
for (ClassDoc classDoc : packages[i].allClasses()) {
ClassSymbol oldsym = ((ClassDocImpl)classDoc).tsym;
if (set.add(oldsym.sourcefile)) { // true if not already present
list.append(oldsym.sourcefile);
}
}
}
// Javadoc is somewhat verbose - we'll add this message to it
Log.instance(context).getWriter(WriterKind.NOTICE).println("Compiling with JML");
JmlCompiler.instance(context).compile(list.toList(),List.<String>nil(),List.<Processor>nil());
}
}