package org.jmlspecs.openjml.jmldoc; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import org.jmlspecs.annotation.*; import org.jmlspecs.openjml.JmlPretty; import org.jmlspecs.openjml.JmlSpecs; import org.jmlspecs.openjml.JmlTree; import org.jmlspecs.openjml.JmlSpecs.TypeSpecs; import org.jmlspecs.openjml.JmlTree.JmlMethodDecl; import org.jmlspecs.openjml.JmlTree.JmlMethodSpecs; import org.jmlspecs.openjml.JmlTree.JmlTypeClause; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseDecl; import com.sun.javadoc.ClassDoc; import com.sun.javadoc.ConstructorDoc; import com.sun.javadoc.ExecutableMemberDoc; import com.sun.javadoc.ProgramElementDoc; import com.sun.javadoc.Tag; import com.sun.tools.doclets.formats.html.ConstructorWriterImpl; import com.sun.tools.doclets.formats.html.SubWriterHolderWriter; import com.sun.tools.javac.code.Scope; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; import com.sun.tools.javadoc.ClassDocImpl; import com.sun.tools.javadoc.ConstructorDocImpl; import com.sun.tools.javadoc.DocEnv; /** * This class extends its parent in order to write out documentation about (a) * model constructors and (b) the JML specifications of constructors and model * constructors. * * @author David R. Cok */ public class ConstructorWriterJml extends ConstructorWriterImpl { /** The Symbol of the class (in the JML compilation context) that owns * these constructors. */ protected @NonNull ClassSymbol currentClassSym; /** Constructs a writer for constructors, given a delegating writer and an owning class. * * @param writer the delegating writer (typically the corresponding ClassWriter) * @param classdoc the Doc of the class whose constructors are being documented */ public ConstructorWriterJml(@NonNull SubWriterHolderWriter writer, @NonNull ClassDoc classdoc) { super(writer, classdoc); currentClassSym = Utils.findNewClassSymbol(classdoc); } // FIXME - major change in b144 // /** Overrides the super class method in order to write out any specs after the tags. // * @param method the constructor begin documented // */ // public void writeTags(@NonNull ConstructorDoc method) { // super.writeTags(method); // writeJmlSpecs(method); // } /** Writes out the JML specifications for the constructor. * * @param method the Doc element of the constructor being documented */ public void writeJmlSpecs(@NonNull ConstructorDoc method) { if (method instanceof ConstructorDocImpl) { MethodSymbol oldMethodSym = ((ConstructorDocImpl)method).sym; Context context = org.jmlspecs.openjml.jmldoc.Main.jmlContext; Name newMethodName = Names.instance(context).fromString(oldMethodSym.name.toString()); // FIXME - improve this method of finding matches Scope.Entry e = currentClassSym.members().lookup(newMethodName); while (!(e.sym instanceof MethodSymbol) || !match((MethodSymbol)e.sym,oldMethodSym)) { e = e.sibling; } MethodSymbol newMethodSym = (MethodSymbol)e.sym; JmlSpecs.MethodSpecs mspecs = JmlSpecs.instance(context).getSpecs(newMethodSym); if (mspecs != null) { writer.br(); // Need this if there is tag info, otherwise not // FIXME String s = Utils.jmlAnnotations(newMethodSym); if (Utils.hasSpecs(mspecs) || s.length()!=0) { strong("JML Constructor Specifications: "); writer.print(s); writer.preNoNewLine(); //writer.print(JmlPretty.write(mspecs.mods,false)); writer.print(JmlPretty.write(mspecs.cases,false)); writer.preEnd(); } } } } /** Returns true if the two method symbols refer to the same constructor - * same name, same parameter names, same parameter types, same type arguments. * The first argument is a method symbol in the JML compilation context; * the second argument is a method symbol in the Javadoc compilation context. * @param m constructor symbol in JML compilation context * @param mm method symbol in the Javadoc compilation context * @return true if the two symbols refer to the same method */ // FIXME public boolean match(MethodSymbol m, MethodSymbol mm) { if (!m.isConstructor()) return false; List<VarSymbol> mp = m.params(); List<VarSymbol> mmp = mm.params(); if (mp.size() != mmp.size()) return false; while (mp.tail != null) { if (!mp.head.name.toString().equals(mmp.head.name.toString())) return false; if (!mp.head.type.toString().equals(mmp.head.type.toString())) return false; // FIXME - is this how to compare types? mp = mp.tail; mmp = mmp.tail; } return true; } // FIXME - major change in b144 // /** Overrides the parent method in order to write out details about // * JML model constructors after the footer for the Java constructors // * is written. // * @param classDoc the class whose constructors are being documented // */ // public void writeFooter(@NonNull ClassDoc classDoc) { // super.writeFooter(classDoc); // writeJmlModelConstructors(classDoc); // } public void writeJmlModelConstructors(@NonNull ClassDoc classDoc) { // Hard coding this // <ConstructorDetails> // <Header/> // <ConstructorDoc> // <ConstructorHeader/> // <Signature/> // <DeprecationInfo/> // <ConstructorComments/> // <TagInfo/> // <ConstructorFooter/> // </ConstructorDoc> // <Footer/> // </ConstructorDetails> // DocEnv denv = ((ClassDocImpl)classDoc).docenv(); // Find model methods to see if we need to do anything at all LinkedList<JmlMethodDecl> list = new LinkedList<JmlMethodDecl>(); TypeSpecs tspecs = JmlSpecs.instance(org.jmlspecs.openjml.jmldoc.Main.jmlContext).get(currentClassSym); for (JmlTypeClause tc: tspecs.decls) { if (tc instanceof JmlTypeClauseDecl && ((JmlTypeClauseDecl)tc).decl instanceof JmlMethodDecl) { JmlMethodDecl d = (JmlMethodDecl)((JmlTypeClauseDecl)tc).decl; //boolean use = JmlAttr.instance(org.jmlspecs.openjml.jmldoc.Main.jmlContext).findMod(d.mods,JmlToken.MODEL) != null; if (d.sym.isConstructor() && denv.shouldDocument(d.sym)) { list.add(d); } } } if (list.isEmpty()) return; // Header writer.printTableHeadingBackground("JML Model Constructor Detail"); writer.println(); // FIXME - major change in b144 // boolean isFirst = true; // for (JmlMethodDecl tc: list) { // ConstructorDoc md = new ConstructorDocImpl(((ClassDocImpl)classDoc).docenv(),tc.sym,tc.docComment,tc,null); // // Method header // writeConstructorHeader(md,isFirst); isFirst = false; // // // signature // writeSignature(md); // // // deprecation // writeDeprecated(md); // // // field comments // writeComments(md); // // // tag info // // FIXME super.writeTags(md); // writeJmlSpecs(md); // // // Method footer // writeConstructorFooter(); // } // // // //Footer // super.writeFooter(classDoc); } // FIXME - major change in b144 // public void writeMemberSummaryFooter(ClassDoc classDoc) { // super.writeMemberSummaryFooter(classDoc); // writeJmlConstructorSummary(classDoc); // } // There are no inherited constructors // public void writeInheritedMemberSummaryFooter(ClassDoc classDoc) { // //writeJmlInheritedNestedClassSummaryFooter(classDoc); // super.writeInheritedMemberSummaryFooter(classDoc); // } // public void writeJmlInheritedNestedClassSummaryFooter(ClassDoc classDoc) { // ClassSymbol csym = Main.findNewClassSymbol(classDoc); // TypeSpecs tspecs = JmlSpecs.instance(Main.jmlContext).get(csym); // ArrayList<MethodDoc> list = new ArrayList<MethodDoc>(); // for (JmlTypeClause tc : tspecs.clauses) { // // FIXME - check package and not in the same package // if (tc instanceof JmlTypeClauseDecl && ((JmlTypeClauseDecl)tc).decl instanceof JCTree.JCMethodDecl) { // MethodSymbol msym = ((JCTree.JCMethodDecl)((JmlTypeClauseDecl)tc).decl).sym; // if ((msym.flags() & Flags.PRIVATE) != 0) continue; // MethodDoc modelMethod = new MethodDocImpl(((ClassDocImpl)classDoc).docenv(),msym,"DOCCOMMENT",(JCTree.JCMethodDecl)((JmlTypeClauseDecl)tc).decl,null); // list.add(modelMethod); // } // } // // if (!list.isEmpty()) { // writer.br(); // writer.bold("Inherited JML model methods: "); // Collections.sort(list); // boolean isFirst = true; // for (MethodDoc method: list) { // writer.printInheritedSummaryMember(this, classDoc, method, isFirst); // isFirst = false; // } // } // } public void writeJmlConstructorSummary(ClassDoc classDoc) { ArrayList<ConstructorDoc> list = new ArrayList<ConstructorDoc>(); DocEnv denv = ((ClassDocImpl)classDoc).docenv(); TypeSpecs tspecs = JmlSpecs.instance(Main.jmlContext).get(currentClassSym); for (JmlTypeClause tc : tspecs.decls) { // FIXME - control visibility if (tc instanceof JmlTypeClauseDecl && ((JmlTypeClauseDecl)tc).decl instanceof JCTree.JCMethodDecl) { JmlMethodDecl mdecl = ((JmlMethodDecl)((JmlTypeClauseDecl)tc).decl); MethodSymbol msym = mdecl.sym; if (msym.isConstructor() && denv.shouldDocument(msym)) { ConstructorDoc constructor = new ConstructorDocImpl(((ClassDocImpl)classDoc).docenv(),msym,mdecl.docComment,mdecl,null); list.add(constructor); } } } if (list.isEmpty()) return; Collections.sort(list); writeJmlConstructorSummaryHeader(classDoc); // The following loop is copied with modifications from MemberSummaryBuilder.buildSummary for (int i = 0; i<list.size(); i++) { ConstructorDoc member = list.get(i); Tag[] firstSentenceTags = member.firstSentenceTags(); // if (firstSentenceTags.length == 0) { // //Inherit comments from overridden or implemented method if // //necessary. // DocFinder.Output inheritedDoc = // DocFinder.search(new DocFinder.Input(member)); // if (inheritedDoc.holder != null && // inheritedDoc.holder.firstSentenceTags().length > 0) { // firstSentenceTags = inheritedDoc.holder.firstSentenceTags(); // } // } // FIXME - major change in b144 // writeMemberSummary(classDoc, member, firstSentenceTags, // i == 0, i == list.size() - 1); } // super.writeMemberSummaryFooter(classDoc); } public void writeJmlConstructorSummaryHeader(ClassDoc classDoc) { //printSummaryAnchor(cd); Utils.writeHeader(writer,this,classDoc,"JML Model Constructor Summary",1); } /** This is overridden in order to include annotation information in the * method summary entry. Immediately after this, the modifiers are written. * @param member the (method) Doc element whose annotation is to be printed */ @Override protected void printModifier(ProgramElementDoc member) { writer.writeAnnotationInfo(member); super.printModifier(member); } // FIXME - major change in b144 // /** This override has the effect of always printing out annotation information // * when a method signature is written; in particular, in javadoc it is not // * written out in the parameters within the constructor summary section. // * @param member the Doc element whose information is being printed // * @param includeAnnotations boolean switch now being ignored // */ // @Override // protected void writeParameters(ExecutableMemberDoc member, // boolean includeAnnotations) { // super.writeParameters(member,true); // } }