package org.jmlspecs.openjml.jmldoc;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.jmlspecs.annotation.NonNull;
import org.jmlspecs.openjml.JmlPretty;
import org.jmlspecs.openjml.JmlSpecs;
import org.jmlspecs.openjml.JmlTree;
import org.jmlspecs.openjml.JmlSpecs.TypeSpecs;
import com.sun.javadoc.ClassDoc;
import com.sun.tools.doclets.formats.html.ClassWriterImpl;
import com.sun.tools.doclets.formats.html.ConfigurationImpl;
import com.sun.tools.doclets.formats.html.markup.Comment;
import com.sun.tools.doclets.formats.html.markup.HtmlTree;
import com.sun.tools.doclets.formats.html.markup.RawHtml;
import com.sun.tools.doclets.internal.toolkit.Content;
import com.sun.tools.doclets.internal.toolkit.util.ClassTree;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javadoc.ClassDocImpl;
/**
* This class extends ClassWriterImpl in order to include in the HTML output
* information about the JML specifications of the class whose javadoc page is
* being written.
*
* @author David R. Cok
*/
public class ClassWriterJml extends ClassWriterImpl {
/** Constructs an instance of the class.
*
* @param classDoc the Doc element being printed
* @param prevClass the Doc element of the class before this in the javadoc order
* @param nextClass the Doc element of the class after this in the javadoc order
* @param classTree the class tree for the given class
* @throws Exception
*/
public ClassWriterJml (ConfigurationImpl config, ClassDoc classDoc,
ClassDoc prevClass, ClassDoc nextClass, ClassTree classTree)
throws Exception {
super(config,classDoc,prevClass,nextClass,classTree);
}
// FIXME - major change in b144
// /** This overrides the parent class method in order to append the JML specs
// * of a class (e.g. invariants) after the class description near the beginning
// * of the javadoc output.
// */
// @Override
// public void writeClassDescription() {
// super.writeClassDescription();
// ClassSymbol newsym = null;
// if (classDoc instanceof ClassDocImpl) {
// ClassSymbol oldsym = ((ClassDocImpl)classDoc).tsym;
// Context context = org.jmlspecs.openjml.jmldoc.Main.jmlContext;
// Name newname = Names.instance(context).fromString(oldsym.flatname.toString());
// newsym = Symtab.instance(context).classes.get(newname);
// TypeSpecs tspecs = JmlSpecs.instance(context).get(newsym);
// String ss = Utils.jmlAnnotations(newsym);
// headerPrinted = false;
// boolean hsp = hasSpecsToPrint(tspecs);
// if (hsp || ss.length()!=0){
// printSpecHeader("JML Specifications");
// if (ss.length()!=0) {
// strong("Annotations:");
// print(ss);
// br();
// }
// if (hsp) printSpecs(tspecs);
// }
//
// for (ClassSymbol ssym: Utils.getSupers(newsym)) {
// printInheritedSpecs(context,ssym);
// }
//
// if (headerPrinted) printSpecEnd();
//
// }
// p();
// }
/** Prints the given specs (any clauses that are not declarations, e.g.
* not field or method declarations).
*
* @param tspecs the specs to print
*/
public Content getSpecs(TypeSpecs tspecs) {
for (JmlTree.JmlTypeClause clause: tspecs.clauses) {
if (clause instanceof JmlTree.JmlTypeClauseDecl) continue;
if (clause instanceof JmlTree.JmlTypeClauseRepresents) continue;
return new RawHtml("<PRE>" + JmlPretty.write(clause,false) + "</PRE>");
}
return HtmlTree.EMPTY;
}
/** Prints specs with a header that these are inherited from a super class
*
* @param context the compilation context in use
* @param csym the super class or interface whose specs are to be printed
*/
public Content getInheritedSpecs(Context context, ClassSymbol csym) {
TypeSpecs tspecs = JmlSpecs.instance(context).get(csym);
if (hasSpecsToPrint(tspecs)){
Content content = new RawHtml("<strong>JML Specifications inherited from " + csym + ": </strong>");
content.addContent(getSpecs(tspecs));
return content;
}
return HtmlTree.EMPTY;
}
/** Returns true if the argument contains specs worth printing (this does not
* include model or ghost declarations).
* @param tspecs the specs of a class or interface
* @return true if the argument contains stuff to print
*/
public boolean hasSpecsToPrint(TypeSpecs tspecs) {
if (!tspecs.modifiers.annotations.isEmpty()) return true;
return !tspecs.clauses.isEmpty();
}
/** Set to true once the 'JML Specifications' header has been printed; this
* happens the first time a class or superclass is found with something
* to print. */
private boolean headerPrinted = false;
/** Prints a header containing the given text. This is written to match
* the other header blocks produced in various places in the javadoc code
* (e.g. SubHolderWriterHolder.printTableHeadingBackground).
*
* @param text the title to put in the header block
*/
public Content getSpecHeader(@NonNull String text) {
if (headerPrinted) return;
Utils.writeHeader(this,null,null,text,1);
headerPrinted = true;
// Write the beginning of a row
println("<TR BGCOLOR=\"white\" CLASS=\"TableRowColor\">");
println("<TD ALIGN=\"left\" VALIGN=\"top\" WIDTH=\"1%\">");
}
/** Prints the HTML that ends a table begun by printSpecHeader */
public Content getSpecEnd() {
println("</TD></TR>");
tableEnd();
space();
}
@Override
protected Comment getGeneratedBy(boolean timestamp) {
String text = "Generated by jmldoc (build " + ConfigurationImpl.BUILD_DATE + ":" + JavaCompiler.version() + ") on";
if (timestamp) {
Calendar calendar = new GregorianCalendar(TimeZone.getDefault());
Date today = calendar.getTime();
text += " (build " + ConfigurationImpl.BUILD_DATE + ":" + JavaCompiler.version() + configuration.getDocletSpecificBuildDate() + ") on " + today;
}
return new Comment(text);
}
}