package org.jmlspecs.openjml.jmldoc;
import org.jmlspecs.annotation.NonNull;
import org.jmlspecs.openjml.JmlSpecs;
import com.sun.javadoc.ClassDoc;
import com.sun.tools.doclets.formats.html.AbstractMemberWriter;
import com.sun.tools.doclets.formats.html.SubWriterHolderWriter;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
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 contains utility functions used by the rest of the package. */
public class Utils {
//FIXME _ is there a way to discover this?
/** The fully-qualified name of the package the annotations are defined in. */
@NonNull
private final static String packageString = "org.jmlspecs.annotation";
// FIXME - this is not thread/context safe
/** An internal variable, lazily initialized, that holds the symbol for the
* package representing 'org.jmlspecs.annotation', which holds all the
* JML annotation interfaces.
*/
private static Symbol annotationPackage = null;
/** Returns the Symbol (in the JML context) of the package that all of
* the annotations are in.
* @return the Package Symbol for the annotation package
*/
public static Symbol annotationPackage() {
Symbol s = annotationPackage;
if (s == null) {
Name pn = Names.instance(Main.jmlContext).fromString(packageString);
annotationPackage = Symtab.instance(Main.jmlContext).packages.get(pn);
}
return s;
}
/** A utility function that returns a String containing text version of the
* JML annotations belonging to the given symbol.
* @param symbol the symbol whose annotations are to be printed
* @return a String with a text representation of the JML annotations
* (with no end-of-line characters)
*/
public static @NonNull String jmlAnnotations(@NonNull Symbol symbol) {
String s = "";
Symbol ap = annotationPackage();
if (symbol.getAnnotationMirrors() == null) return s;
for(Attribute.Compound compound: symbol.getAnnotationMirrors()) {
if (compound.type.tsym.owner == ap) s = s + (" @" + compound.type.tsym.name);
}
return s;
}
/** A utility method that returns true if the argument has content worth printing
* @param specs the object containing method specifications
* @return true if there are specifications worth printing
*/
public static boolean hasSpecs(JmlSpecs.MethodSpecs specs) {
if (specs == null) return false;
if (!specs.cases.cases.isEmpty()) return true;
return false;
}
/** A utility function that returns the class symbol in the JML compilation
* context corresponding to the class represented by the classdoc argument.
* @param classdoc the class whose symbol is wanted
* @return the Symbol in the new context for the class
*/
public static ClassSymbol findNewClassSymbol(ClassDoc classdoc) {
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());
return Symtab.instance(context).classes.get(newname);
}
return null;
}
/** A utility function that returns a list of the classes and interfaces that
* the argument directly or indirectly extends or implements. Interfaces are
* listed at most once; classes are listed before interfaces; the argument
* itself is not in the list.
* @param csym the class or interface whose supers are wanted
* @return a list of super classes and interfaces
*/
// FIXME - check this works for interfaces
public static @NonNull java.util.List<ClassSymbol> getSupers(@NonNull ClassSymbol csym) {
java.util.List<ClassSymbol> list = new java.util.ArrayList<ClassSymbol>();
ClassSymbol c = csym;
while (true) {
c = (ClassSymbol)c.getSuperclass().tsym;
if (c == null) break;
list.add(c);
}
c = csym;
for (com.sun.tools.javac.code.Type t: c.getInterfaces()) {
if (!list.contains(t.tsym)) list.add((ClassSymbol)t.tsym);
}
for (int i=0; i<list.size(); i++) {
c = list.get(i);
for (com.sun.tools.javac.code.Type t: c.getInterfaces()) {
if (!list.contains(t.tsym)) list.add((ClassSymbol)t.tsym);
}
}
return list;
}
/** This writes a colored header. Note that the color specified here
* is generally overridden by a hard-coded property.
*
* @param classDoc the class whose ghost and model fields are to be described
*/
public static void writeHeader(
@NonNull SubWriterHolderWriter writer,
@NonNull AbstractMemberWriter mw,
@NonNull ClassDoc classDoc,
@NonNull String title,
int tableColumns) {
//printSummaryAnchor(cd);
//mw.printTableSummary();
writer.tableIndexSummary();
writer.tableHeaderStart("#CCCCFF",tableColumns);
writer.tableCaptionStart();
//mw.printSummaryLabel();
writer.strong(title);
writer.tableHeaderEnd();
writer.tableCaptionEnd();
// if (mw != null) mw.printSummaryTableHeader(classDoc); // DRCok - does not compiler with b144, but what should it be?
}
/** Returns true if Symbol msym should be in the inherited list for the given
* class. msym must be an element of a super class or interface.
* @param msym the element being tested
* @param currentClassSym the class being documented
* @return true if msym is visible in currentClassSym
*/
public static boolean isInherited(Symbol msym, ClassSymbol currentClassSym) {
long m = msym.flags();
if ((m & Flags.PRIVATE) != 0) return false;
if (msym.owner == currentClassSym) return false;
if ((m & Flags.AccessFlags) == 0 &&
(currentClassSym.packge() != msym.packge())) return false;
return true;
}
}