Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/sun/tools/javadoc/MIFDocumentationGenerator.java

/*
 * @(#)MIFDocumentationGenerator.java	1.4 95/12/07 Frank Yellin
 *
 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package sun.tools.javadoc;

import java.util.*;
import java.io.*;
import sun.tools.java.*;
import sun.tools.javac.SourceClass;
import sun.tools.javac.BatchEnvironment;
import sun.tools.tree.LocalField;


class MIFDocumentationGenerator extends DocumentationGenerator 
                     implements Constants {

    ClassDeclaration currentClass;
    Vector allFields = new Vector();

    static final int C_VARIABLE = 0;
    static final int C_CONSTRUCTOR = 1;
    static final int C_METHOD = 2;

    static final int MARKER_package = 18;
    static final int MARKER_classType = 19;
    static final int MARKER_class = 20;
    static final int MARKER_methodName = 22;

    static final char hardSpaceChar = MIFPrintStream.hardSpaceChar;


    /** Generate the title page for multiple packages.  
     *  v1 will be an array of package names for all "general" packages
     *  v2 will be an array of package names for all "debugging"
     */
    void genPackagesDocumentation(String v1[], String v2[]) { }


    /**
     * Generate the documentation for a single package.  The classes in
     * the documentation will be sorted into four categories 
     */
    void genPackageDocumentation(Identifier pkg, 
				 ClassDeclaration intfDecls[], 
				 ClassDeclaration classDecls[], 
				 ClassDeclaration exceptDecls[], 
				 ClassDeclaration errorDecls[]) {
        PrintStream ps = DocumentationGenerator.openFile(pkg + ".mif");
	MIFPrintStream out = new MIFPrintStream(ps);

	out.newParagraph("Package")
	   .markReference("Package " + pkg)
	   .mark(MARKER_package, pkg.toString())  // maybe get rid of this?
	   .literal(pkg);
	genPackageDocumentationType(out, classDecls,  "Classes");
	genPackageDocumentationType(out, exceptDecls, "Exceptions");
	genPackageDocumentationType(out, errorDecls,  "Errors");
	genPackageDocumentationType(out, intfDecls,   "Interfaces");
	out.close();
    }

    /** 
     * Generate the documentation for a specific category of classes
     * in a package 
     */

    private void 
    genPackageDocumentationType(MIFPrintStream out, 
			       ClassDeclaration classes[],
			       String mark) {
	if (classes.length == 0) 
	    return;
	sort(classes);

	out.newParagraph("ClassType")
	   .mark(MARKER_classType , mark)
	   .literal(mark);

	for (int i = 0 ; i < classes.length ; i++) {
	    try {
		ClassDefinition def = classes[i].getClassDefinition(env);
		genClassDocumentation(out, def);
	    } catch (ClassNotFound ex) {
		System.err.println("Warning: Couldn't find class info for "
				   + classes[i].getName());
	    }
	}
    }

    /** Generate the documentation for a single class */
    public void genClassDocumentation(ClassDefinition c, 
				      ClassDeclaration prev, 
				      ClassDeclaration next) {
        PrintStream ps = DocumentationGenerator.openFile(c.getName() + ".mif");
	MIFPrintStream out = new MIFPrintStream(ps);
	genClassDocumentation(out, c);
	out.close();
    }

   private void genClassDocumentation(MIFPrintStream out, ClassDefinition c) {
	System.out.println("generating documentation for " + c);
	currentClass = c.getClassDeclaration();
	Identifier classID = c.getName();
	Identifier baseID = c.getName().getName();
	Identifier pkgID = c.getName().getQualifier();
	String type = (c.isInterface() ? "Interface" : "Class");
	String longTitle = type + " " + classID;
	String shortTitle = type + " " + baseID;
	
	out.newParagraph("Title")
	   .emitPendingMarks()	// any pending package marks??
	   .markReference(classTag(currentClass))
	   .mark(MARKER_class, shortTitle)
	    // we'll want package and class in the header and footer
	   .mark(MIFPrintStream.MarkerHF1, pkgID)  // get rid of this?
	   .mark(MIFPrintStream.MarkerHF2, shortTitle)
	   .index((c.isInterface() ? "Interfaces" : "Classes") + ":" + 
		  classID)
	   .index(baseID + ":" + type + " in package " + pkgID)
	   .literal(shortTitle)
	   .topLevel();

	genSuperClasses(out, c);



	// Document header
	String cdoc = c.getDocumentation();
	if (cdoc != null) {
	    // put the comments into a more parseable form.
	    Vector mergeDoc = mergeDoc(cdoc);

	    String comment = getComment(mergeDoc);
	    String authors = getAuthors(mergeDoc);
	    String version = getVersion(mergeDoc);
	    Vector seeAlso = getSees(mergeDoc, currentClass);
	    
	    if (comment != null)
		out.html("ClassComment", comment);

	    if (version != null) { 
		out.newParagraph("ClassInfo").literal("Version:");
		out.newParagraph("ClassInfoData").literal(version);	
	    } 

	    if (authors != null) {
		out.newParagraph("ClassInfo").literal("Author:");
		out.newParagraph("ClassInfoData").literal(authors);
	    }

	    if (seeAlso != null) {
		out.newParagraph("ClassInfo").literal("See Also:");
		out.newParagraph("ClassInfoData");
		handleSeeStrings(out, seeAlso);
	    }
	}

	if (c.isPublic() || idNull.equals(c.getName().getQualifier())) {
	    // Generate lists
	    Vector variables = allVariables(c);
	    Vector constructors = allConstructors(c);
	    Vector methods = allMethods(c);

	    variables = localFieldsOf(c, variables);
	    methods = localFieldsOf(c, methods);
	    constructors = localFieldsOf(c, constructors);

	    // Full documentation on this methods's variables
	    genDocumentation(out, variables,    C_VARIABLE);
	    genDocumentation(out, constructors, C_CONSTRUCTOR);
	    genDocumentation(out, methods,      C_METHOD);
	} else {
	    out.newParagraph("body");
	    out.literal("This class is not public and can therefore ")
	       .literal("cannot be used outside this package.");
	}
    }

    // print the nice picture showing this class and its superclasses
    private static final double firstMargin = .5;
    private static final double tabIncrement = .25;
    private void 

    genSuperClasses(MIFPrintStream out, ClassDefinition c) { 
	ClassDeclaration clazz = c.getClassDeclaration();
	Vector tree = superclassesOf(clazz);
	int treeLength = tree.size();
	ClassDeclaration intf[] = c.getInterfaces();

	if (treeLength == 1) // java.lang.Object
	    return;
	out.newParagraph("Parents")
	   .indent(firstMargin, firstMargin, 0)
	   .println("   <PgfSpAfter  12pt>")
	   .println("   <PgfNumTabs " + (treeLength - 1) + ">");
	for (int i = 0; i < treeLength - 1; i++) {
	    double tab = firstMargin + (tabIncrement * (i + 1)); 
	    out.println("      <TabStop <TSX " + tab + "in> " + 
			               "<TSType Left> <TSLeaderStr ` ' > >");
	}

	if (c.isPublic()) 
	    out.literal("public ");
	if (c.isFinal()) 
	    out.literal("final ");
	out.literal(c.isInterface() ? "interface " : "class ");
	Identifier pkg = c.getName().getQualifier();
	Identifier baseName = c.getName().getName();
	if (pkg != null) 
	    out.literal(pkg).literal('.');;
	out.bold(baseName);

	if (!c.isInterface()) { 
	    for (int i = treeLength - 1, count = 1; --i >= 0; count++) { 
		ClassDeclaration item = (ClassDeclaration)tree.elementAt(i);
		out.literal('\n');
		for (int j = 0; j < count; j++) 
		    out.literal('\t');
		out.literal("extends").literal(hardSpaceChar);
		pkg = item.getName().getQualifier();
		baseName = item.getName().getName();
		if (pkg != null) 
		    out.literal(pkg).literal('.');;
		out.bold(baseName);
		XRef(out, item);
	    }
	} 
	if (intf.length > 0) {
	    out.literal(c.isInterface() ? "\n\textends " : "\n\timplements ");
	    for (int i = 0 ; i < intf.length ; i++) {
		if (i > 0) 
		    out.literal(", ");
		pkg = intf[i].getName().getQualifier();
		baseName = intf[i].getName().getName();
		if (pkg != null) 
		    out.literal(pkg).literal('.');;
		out.bold(baseName);
		XRef(out, intf[i]);
	    }
	}
    }

    static String DocumentationStrings[] = 
          { "Variables", "Constructors", "Methods" };

    // generate the documentation for the specific fields.
    private void 
    genDocumentation(MIFPrintStream out, Vector fields, int type) { 
	Hashtable seenNames = null;
	if (fields.size() <= 0) 
	    return;
	out.newParagraph("Heading1");
	if (type == C_CONSTRUCTOR) {
	    out.markReference(classTag(currentClass, 
				       currentClass.getName().getName()));
	}
	out.literal(DocumentationStrings[type]);
	for (int i = 0; i < fields.size(); i++) {
	    FieldDefinition f = (FieldDefinition)fields.elementAt(i);
	    if (type == C_VARIABLE) 
		genVariableDocumentation(out, f);
	    else if (type == C_CONSTRUCTOR) {
		genMethodDocumentation(out, f, false);
	    } else { 
		Identifier name = f.getName();
		if (seenNames == null) 
		    seenNames = new Hashtable();
		genMethodDocumentation(out, f, seenNames.get(name) == null);
		seenNames.put(name, name);
	    }
	    if (Main.showIndex) 
		allFields.addElement(f);
	}
    }

    // generate the documentation for a specific variable
    void genVariableDocumentation(MIFPrintStream out, FieldDefinition f) { 
	ClassDefinition fieldClass = f.getClassDefinition();
	String name = f.getName().toString();
	out.newParagraph("FieldBullet")
	   .markReference(classTag(currentClass, name))
	   .mark(MARKER_methodName, name)
           .index(name + ":" + 
		  f.getType().typeString(name.toString(), true, false) + ". " +
		  (f.isStatic() ? "Static variable in " : "Variable in ") +
		  (fieldClass.isInterface() ? "interface " : "class ") + 
		  f.getClassDeclaration().getName().getName())
	   .literal(name);

	out.newParagraph("FieldTty")
	   .literal(f.isStatic() ? "static " : "")
	   .literal(modString(f).trim())
	   .literal(' ')
	   .literal(typeString(f.getType()))
	   .literal(' ')
	   .literal(name)
	   .literal(typeArrayString(f.getType()));
    
	String doc = f.getDocumentation();
	if (doc != null) {
	    // put the comments into a more parseable form.
	    Vector mergeDoc = mergeDoc(doc);
	    String comment = getComment(mergeDoc);
	    Vector seeAlso = getSees(mergeDoc, currentClass);

	    if (comment != null) 
		out.html("FieldComment", comment);

	    if (seeAlso != null) {
		out.newParagraph("FieldInfo").literal("See Also:");
		out.newParagraph("FieldInfoData");
		handleSeeStrings(out, seeAlso);
	    }
	}
    }

    // generate the documentation for a specific method
    void genMethodDocumentation(MIFPrintStream out,
				FieldDefinition f,
				boolean nameMarker) {
	ClassDefinition fieldClass = f.getClassDefinition();
	Type fieldType = f.getType();
	boolean isConstructor = f.isConstructor();
	Identifier name = isConstructor ? fieldClass.getName().getName()
	                                : f.getName();

	out.newParagraph("FieldBullet");
	if (nameMarker)
	    // we want a reference to just the method name
	    out.markReference(classTag(currentClass, f.getName()));
	out.mark(MARKER_methodName, methodFullName(f))
	   .markReference(classTag(currentClass, methodFullName(f)))
 	   .index(name + ":" + 
		  f.getType().typeString(name.toString(), true, true) + ". " +
		  (f.isConstructor() ? "Constructor for " :
		        f.isStatic() ? "Static method in " : "Method in ") +
		  (fieldClass.isInterface() ? "interface " : "class ") + 
		  f.getClassDeclaration().getName().getName())
           .literal(name);


	out.newParagraph("FieldTty")
	   .literal(f.isStatic() ? "static " : "")
	   .literal(modString(f).trim())
	   .literal(' ');
	if (!isConstructor) {
	    out.literal(typeString(f.getType().getReturnType()))
	       .literal(typeArrayString(f.getType()));
	}
	// this is really gross
	out.newParagraph("FieldTty")
	   .indent(.25, .25 + (name.toString().length() + 1)* 0.080, 0)
	   .literal(name).literal('(');

	Type args[] = f.getType().getArgumentTypes();
	if (f.getArguments() == null) {
	    throw new RuntimeException("getArguments returns null for " + f);
	}
	Enumeration e = f.getArguments().elements();
	if (!f.isStatic()) 
	    e.nextElement();
	for (int i = 0; i < args.length ; i++) {
	    LocalField l = (LocalField)e.nextElement();
	    if (i > 0) 
		out.literal(", ");
	    out.literal(typeString(args[i]))
	       .literal(hardSpaceChar)
	       .literal(l.getName())
	       .literal(typeArrayString(args[i]));
	}
	out.literal(")");
	
	ClassDeclaration[] exp = f.getExceptions(env);
	if (exp.length > 0) {
	    out.newParagraph("FieldTty")
	       .indent(.25, .5, .25)
	       .literal("throws");
	    for (int i = 0; i < exp.length; i++) 
		out.literal((i > 0 ? ", " : " ") + exp[i].getName().getName());
	}
	out.topLevel();

	String doc = getDocumentation(f);
	FieldDefinition overrides = getOverride(f);
	if (overrides != null && doc == null) 
	    doc = "";

	if (doc != null) {
	    // put the comments into a more parseable form.
	    Vector mergeDoc = mergeDoc(doc);

	    String comment = getComment(mergeDoc);
	    String returns = getReturn(mergeDoc);
	    Vector seeAlso = getSees(mergeDoc, currentClass);
	    Vector parameters = getParameters(mergeDoc);
	    Vector exceptions = getThrows(mergeDoc, f);

	    if (comment != null) {
		out.html("FieldComment", comment);
	    }

	    if (parameters != null) { 
		out.newParagraph("FieldInfo").literal("Parameters:");
		for (int i = 0; i < parameters.size(); i += 2) {
		    String arg = (String)parameters.elementAt(i);
		    String argdoc = (String)parameters.elementAt(i + 1);
		    out.newParagraph("ParameterName").literal(arg);
		    out.html("ParameterData", argdoc);
		}
	    }

	    if (returns != null) { 
		out.newParagraph("FieldInfo").literal("Returns:");
		out.html("FieldInfoData", returns);
	    }

	    if (exceptions != null) { 
		for (int i = 0; i < exceptions.size(); i += 2) { 
		    ClassDeclaration exc = 
			  (ClassDeclaration)exceptions.elementAt(i);
		    String what = (String)exceptions.elementAt(i + 1);
		    out.newParagraph("FieldInfo")
		       .println("   <PgfPlacementStyle RunIn>")
		       .println("   <PgfRunInDefaultPunct `: '>")
		       .literal("Throws")
		       .newParagraph("FieldInfoData")
		       .charTag("ClassName", exc.getName().getName());
		    XRef(out, exc);
		    out.html("FieldInfoData", what);
		}
	    }

	    if (overrides != null) { 
		ClassDeclaration from = overrides.getClassDeclaration();
		out.newParagraph("FieldInfo").literal("Overrides:");
		out.newParagraph("FieldInfoData")
		   .charTag("MethodName", overrides.getName())
		   .literal(" in class ")
		   .charTag("ClassName", from.getName().getName());
		XRef(out, from, methodFullName(f));
		out.literal('.');
		overrides = null;
	    }

	    if (seeAlso != null) { 
		out.newParagraph("FieldInfo").literal("See Also:");
		out.newParagraph("FieldInfoData");
		handleSeeStrings(out, seeAlso);
	    }
	}
    }

    // Generate the index.  Not necessary for MIF, since Frame can do this
    // automatically
    void genFieldIndex() { 
	System.out.println("No need for index for MIF");
    }


    /** Generate a page containing the class hierarcy */
    void genClassTree(Hashtable tree, ClassDeclaration objectDecl) { 
	// Generate a picture of the tree
	currentClass = null;
        PrintStream ps = DocumentationGenerator.openFile("tree.mif");
        MIFPrintStream out = new MIFPrintStream(ps);
	out.newParagraph("Title").literal("Hierarchy");
	genClassTree(out, tree, objectDecl, 0);
	out.close();
    }

    private
    void genClassTree(MIFPrintStream out, Hashtable tree, 
		      ClassDeclaration decl, int level) {
      Identifier pkg = decl.getName().getQualifier();
      Identifier baseName = decl.getName().getName();

	out.newParagraph("Body").skip(0).indent(.25 * level, 0, 
						.25 * (level + 2));
	try { 
	    ClassDefinition defn = decl.getClassDefinition(env);
	    ClassDeclaration interfaces[] = defn.getInterfaces();

	    out.literal(defn.isClass() ? "class " : "interface ")
	       .literal(pkg).literal('.').bold(baseName);
	    XRef(out, decl);
	    if (interfaces.length > 0) {
	        out.literal(defn.isInterface() ? " extends" : " implements")
		   .literal(hardSpaceChar);
		for (int i = 0 ; i < interfaces.length ; i++) {
		    ClassDeclaration intf = interfaces[i];
		    if (i > 0)  
			out.literal(", ");
		    out.literal(intf.getName().getName());
		    XRef(out, intf);
		}
	    }
	} catch (ClassNotFound e) {
	}

	ClassDeclaration[] children = (ClassDeclaration[])tree.get(decl);
	if (children != null && children.length > 0) { 
	    level++;
	    for (int i = 0; i < children.length; i++) {
		genClassTree(out, tree, children[i], level);
	    }
	}
    }

    String authorString(String str) { return str; }
    String commentString(String str) { return str; } 
    String versionString(String str) { return str; } 
    String returnString(String str) { return str; } 

    String classString(ClassDeclaration c) { 
	return c.getName().getName().toString();
    } 

    private void handleSeeStrings(MIFPrintStream out, Vector seeAlso) {
	boolean first = true;
	for (Enumeration e = seeAlso.elements(); e.hasMoreElements(); ) {
	    ClassDeclaration decl = (ClassDeclaration)(e.nextElement());
	    String fieldName = (String)(e.nextElement());
	    String what = (String)(e.nextElement());

	    if (!first) 
		out.literal(", ");

	    if (fieldName == null) { 
		out.charTag("ClassName", decl.getName().getName());
		XRef(out, decl);
	    } else { 
		out.charTag("MethodName",  fieldName);
		if (decl != currentClass) 
		    out.literal(" in class ")
		       .charTag("ClassName", decl.getName().getName());
		XRef(out, decl, what);
	    }
	    first = false;
	}
	out.literal('.');
    }

    String methodFullName(FieldDefinition f) {
	Identifier name = f.isConstructor()
			    ? f.getClassDeclaration().getName().getName()
			    : f.getName();
	return f.getType().typeString(name.toString(), false, false);
    }

    String classTag(ClassDeclaration c) { 
	return "class " + c.getName();
    }

    String classTag(ClassDeclaration c, Object thing) { 
	return "class " + c.getName() + "." + thing;
    }

    String sourceFile(ClassDeclaration decl) {
        Identifier pkg = decl.getName().getQualifier();
	return pkg + ".fm";
    }

    void XRef(MIFPrintStream out, ClassDeclaration decl) {
	XRef(out, decl, null);
    } 

    void XRef(MIFPrintStream out, ClassDeclaration decl, Object what) {
	String itsSource = sourceFile(decl);
	if (currentClass != null && sourceFile(currentClass).equals(itsSource))
	    itsSource = null;
	String thing = (what == null) ? classTag(decl) : classTag(decl, what);
	out.literal(hardSpaceChar)
	   .XRef("(page)", thing, itsSource);
    }
}