home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: Java
/
Java.zip
/
ipfdoclt.zip
/
IPFDoclet.java
< prev
next >
Wrap
Text File
|
1999-02-15
|
47KB
|
1,074 lines
import com.sun.javadoc.*;
import java.util.*;
import java.io.*;
/**
* This class is a doclet that generates Javadoc help as IPF tags. IPF is IBM's
* help system for OS/2. There are also viewers available for other platforms,
* for example, Windows NT. The advantages of IPF over html are several: it is
* much more compact - usually about a third the total size once compiled to inf
* format, the viewer has a much smaller memory footprint than most web browsers,
* and the inf files are searchable, which is generally not possible without some
* kind of search engine for html. On the down side, the IPF format is not as rich
* as html, so the output tends not to be as pretty.
* <p>
* This doclet adds the following command line options to javadoc:
* <dl>
* <dt>-f <file>
* <dd>Specify the output filename. Defaults to javadoc.ipf
* <dt>-title "title"
* <dd>Specify the title for the inf file
* <dt>-graphics
* <dd>When specified graphics will be used for some headings
* <dt>-map
* <dd>Specify the map file to use. The map file maps package names to
* INF files so that links outside of the file work properly.
* </dl>
*/
public class IPFDoclet {
/** The output is written to this Writer. */
static private PrintWriter out = null;
/** This variable is incremented every time a heading id is specified, so that each
heading id will be unique.
*/
static private int ref = 1;
/** The name of the output file. This defaults to javadoc.ipf, but can be overridden
by the -f command line option.
*/
static private String outputFileName="javadoc.ipf";
/** The title to use for the inf file. Defaults to "Javadoc Documentation" */
static private String title="Javadoc Documentation";
/** The name of the mapping file. Defaults to javadoc.map" */
static private String mappingFileName="javadoc.map";
/** The name of this inf file as it will be inserted into the map file. This is just the
<code>outputFileName</code> with the extension changed to .inf.
*/
static private String mapName="";
/** This properties object is used to load the mapping file */
static private Properties mapping = null;
/** An array of all the package names documented in the IPF being generated. */
static private String[] ourPackages = null;
/** This hashtable is used to match heading ids to fully qualified class names. We use these to look
up the ids for @see tags and other links internal to the generated ipf file.
*/
static private Hashtable classIds = null;
/** Holds the link id of the package currently being processed */
static private int currentPackageId = 1;
/** Holds the id of the panel with the list of all packages */
static private int packageListId = 1;
/** Holds the id of the panel listing all classes in the generated file */
static private int allClassesRef = 1;
/** True if command line specified that graphics should be used */
static private boolean graphics = false;
/** This is the RootDoc object passed to the start method. We keep a static reference to it
because it has the DocErrorReporter in it that we may need to use.
*/
static private RootDoc doc=null;
/**
* Called to start processing documentation. This is the entry point for the doclet, and
* it handles outputting all the preliminary panels such as the package list, and all classes
* files, and sets up the class ids. It then processes each package one by one by calling
* <code>processPackage</code>.
* @param _doc The document object to process
* @return true on success.
*/
public static boolean start(RootDoc _doc) {
doc = _doc; // Save _doc in a static field
// Read process any command line options
readOptions(doc.options());
// Read the external mapping list
mapping = new Properties();
File propertiesFile = new File(mappingFileName);
if (propertiesFile.exists()) {
try {
FileInputStream in = new FileInputStream(propertiesFile);
mapping.load(in);
in.close();
}
catch (IOException exc) {
doc.printError(exc.getMessage());
}
}
// Get a File object for the output file
File f = new File(outputFileName);
// Strip the extension from the output file name and append the extension .inf. This is
// the name that is inserted into the mapping file for all the packages handled by this
// inf file.
mapName = f.getName();
int index = mapName.lastIndexOf(".");
if (index != -1) {
mapName = mapName.substring(0,index);
}
mapName += ".inf";
// Assign ids to all of the classes to be processed. They are stored in the hashtable
// keyed by their fully qualfied class name
ClassDoc[] classes = doc.classes();
classIds = new Hashtable(classes.length);
for (int i=0;i<classes.length;i++) {
classIds.put(classes[i].qualifiedName(), new Integer(ref++));
}
// Open the output file
try {
out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(new File(outputFileName))));
}
catch (IOException exc) {
doc.printError("Error opening output file");
return false;
}
// Write the IPF header information to the output file
header();
// Get a list of all packages being processed
PackageDoc[] packages = doc.specifiedPackages();
int packageRef = 0;
// Generate the window listing all the packages
if (packages.length > 0) {
out.println(":h1 group=1 x=left y=top width=20% height=40% res="+ref+".All Packages");
out.println(":hp2.This File&colon.:ehp2.");
packageListId = ref; // Store the id of this window
ref++;
allClassesRef = ref; // Store the id of the All Classes window and write a link to it.
out.println(".br");
out.println(":link reftype=hd res="+ref+".All Classes:elink.");
out.println(".br");
ref++;
packageRef = ref; // Store the id that refers to the package window
ourPackages = new String[packages.length]; // An array of the package names of all the packages we are processing
// Write out a link to each package window. The ids of these will sequentially increment from packageRef.
for (int i=0;i<packages.length;i++) {
out.println(":link reftype=hd res="+ref+"."+packages[i].name()+":elink.");
ref++;
out.println(".br");
// Put the name of the package in ourPackages array
ourPackages[i] = packages[i].name();
}
// Output external links for all the packages we know about from other files. These are found
// in the mapping file. External links use the package name as the link id.
out.println(":p.:hp2.Linked Files&colon.:ehp2.");
out.println(".br");
// Copy the package names into an ArrayList so they can be sorted alphabetically.
ArrayList linkedPackages = new ArrayList();
Enumeration enum = mapping.propertyNames();
while (enum.hasMoreElements()) {
linkedPackages.add(enum.nextElement());
}
Collections.sort(linkedPackages);
for (int i=0;i<linkedPackages.size();i++) {
String pack = (String) linkedPackages.get(i);
if (!isPackageInternal(pack)) {
out.println(":link reftype=hd database='"+(String)mapping.get(pack)+"' refid='"+pack+"'."+pack+":elink.");
out.println(".br");
}
}
}
// Print out the allClasses list
if (packages.length != 0) {
out.println(":h1 group=2 x=left y=bottom width=20% height=60% res="+allClassesRef+".All Classes");
}
else {
allClassesRef = ref;
ref++;
out.println(":h1 group=2 x=left y=bottom width=20% height=100% res="+allClassesRef+".All Classes");
}
out.println(".br");
for (int i=0;i<classes.length;i++) {
out.println(getLink(classes[i].qualifiedName(), classes[i].name()));
out.println(".br");
}
// Process each package.
if (packages.length > 0) {
classes = null; // We don't need this list anymore so let it get garbage collected.
for (int i=0;i<packages.length;i++) {
processPackage(packages[i], packageRef+i);
System.gc(); // Try and save some memory
}
// Put the packages in the map and write it back to disk.
for (int i=0;i<packages.length;i++) {
mapping.setProperty(packages[i].name(), mapName);
}
try {
FileOutputStream out = new FileOutputStream(propertiesFile);
mapping.store(out, "IPF Javadoc Mapping File");
out.close();
}
catch (IOException exc) {
System.err.println(exc.getMessage());
}
}
else {
for (int i=0;i<classes.length;i++) {
processClass(classes[i]);
}
}
// Write out the help page
help();
// Write out the trailer
trailer();
// Close the output file
out.close();
return true;
}
/**
* Writes the ipf header. This consists of a userdoc tag, the docprof tag, and the title tag.
*
*/
public static void header() {
out.println(":userdoc.");
out.println(":docprof toc=1234.");
out.println(":title."+title);
}
/**
* Writes the trailer for the ipf file. This is just a euserdoc tag.
*/
public static void trailer() {
out.println(":euserdoc.");
}
/**
* Writes out the help page. This is a brief description of the help format that is linked to
* in the yellow bar at the top of each method page.
*/
public static void help() {
out.println(":h1 name='help'.Help on IPF Javadoc Documentation");
out.println(":hp4.Help on IPF Javadoc Documentation:ehp4.");
out.println(":p.This file was generated automatically using a JDK 1.2 Javadoc Doclet. Doclets allow");
out.println("the Javadoc documentation to be output in a different format than the standard html format");
out.println("provided by Sun. In this case, it was used to output to OS/2 IPF format, which was then");
out.println("compiled to an INF file.");
out.println(":p.There are certain limitations imposed by the nature of the IPF format. For one thing,");
out.println("especially in Sun's documentation, considerable use is made of html tags in the documentation.");
out.println("The doclet does it's best to convert these to the corresponding IPF tags, but html is a richer");
out.println("format than IPF, and this cannot always be done. For example, in IPF it is not possible to change");
out.println("the font inside a table, which is done quite frequently in Sun's documentation.");
out.println(":p.This documentation does not contain all of the information present in Sun's documentation. For");
out.println("example, there is no deprecated section for each class, and inherited methods are not listed.");
out.println("This was simply my choice in formatting the documentation - if you would like to have this, then");
out.println("all you have to do is download the source for the doclet, and modify it to do so.");
out.println(":p.The best way to use this documentation is to double-click on the All Packages entry in the");
out.println("table of contents immediately after opening the file. This opens a panel listing all the packages");
out.println("in the file (as well as any in linked files that are available). Double-clicking on one of these");
out.println("will bring up a panel listing all the classes in the package, and you can then select the class");
out.println("you want to display.");
out.println(":p.Throughout the documentation, there may be links to JavaDoc documentation that is outside of");
out.println("the file. For example, in the class hierarchy tree, there will always be at least a pointer to");
out.println("java.lang.Object. If you have the appropriate inf files containing the javadoc information");
out.println("for these classes, the links will be live and will work properly (ie. when you double-click on the");
out.println("link, the panel will be automatically loaded from the external file). If you don't have the file");
out.println("then the link will not be active, and will be shown in black instead of blue. The other inf files");
out.println("should be in the same directory, or on the BOOKSHELF path.");
}
/**
* Processes an individual package. This means we write out a package window containing links to all the
* classes in the package, and then write out each class window.
* @param p The package to document
* @param id The id to use for the package window. These have already been predetermined in the
* calling routine.
*/
public static void processPackage(PackageDoc p, int id) {
doc.printNotice("Processing package "+p.name());
// Write the window header. This is a narrow window in the lower left (below the all packages window).
out.println(":h1 group=2 x=left y=bottom width=20% height=60% global res="+id+" id='"+p.name()+"'."+p.name());
currentPackageId=id;
// Get all the classes in the package, and sort them using an ArrayList.
ClassDoc[] classes=p.allClasses();
ArrayList classArray = new ArrayList(classes.length);
for (int i=0;i<classes.length;i++) {
classArray.add(classes[i]);
}
Collections.sort(classArray);
// Copy sorted ArrayList back into a regular array.
for (int i=0;i<classes.length;i++) {
classes[i] = (ClassDoc) classArray.get(i);
}
// If this isn't output, the first line will not be indented like the rest of them.
out.println(".br");
// Write out each class as a link. Interfaces are written with the hp1 property, which means they are
// italicized.
for (int i=0;i<classes.length;i++) {
if (classes[i].isInterface()) {
out.println(":link reftype=hd res="+getClassId(classes[i])+".:hp1."+classes[i].name()+":ehp1.:elink.");
}
else {
out.println(":link reftype=hd res="+getClassId(classes[i])+"."+classes[i].name()+":elink.");
}
out.println(".br"); // Break goes to next line
}
// Process each class
for (int i=0;i<classes.length;i++) {
processClass(classes[i]);
}
}
/**
* Gets the window id for a given class. Only works on classes that are going to be in the ipf file we
* are generating. It finds it by looking it up in the hashtable <code>classIds</code>.
* @param c The class to get the window id for.
* @return The window id of the specified class.
*/
public static int getClassId(ClassDoc c) {
return getClassId(c.qualifiedName());
}
/**
* Gets the window id for a given class. Only works on classes that are going to be in the ipf file we
* are generating. It finds it by looking it up in the hashtable <code>classIds</code>.
* @param className The fully qualified class name to get the window id for.
* @return The window id of the specified class.
*/
public static int getClassId(String className) {
Integer id = (Integer) classIds.get(className);
if (id == null) {
return 0;
}
return id.intValue();
}
/**
* Gets IPF tagging for link to specified class. This produces a string containing the complete
* link to elink sequence for a link to the class. If the class is one being documented in the file
* that is being generated, the link is an internal one to it's numeric id. If it is in an external
* database and the package can be found in the mapping file, the link is an external one to the
* id specified by the fully qualified class name. If it cannot be found in the map file, then the
* string returned is just the text, with no link information.
* @param className Fully qualified class name of the class to get the link for
* @param The text to be shown for the link
* @return String containing the link text.
*/
public static String getLink(String className, String text) {
// Try and lookup the class id for an internal link
int id = getClassId(className);
// If the class could not be found internally, then do an external link
if (id == 0) {
// Get the package name for the class
String pack = className;
int index = pack.lastIndexOf(".");
if (index != -1) {
pack = pack.substring(0,index);
}
// Look the package up in the mapping file
String database = mapping.getProperty(pack);
// If we found the package, link to the external database found
if (database != null) {
return ":link reftype=hd refid='"+className+"' database='"+database+"'."+text+":elink.";
}
else {
// If we didn't find the package, just return the text - we can't provide a link.
return text;
}
}
else {
// Return an internal link.
return ":link reftype=hd res="+id+"."+text+":elink.";
}
}
/**
* Produces the ipf for a particular class. This consists of writing out the class panel with
* the description of the class, and field, constructor and method summaries, and then writing
* out the panels for each individual field, method, and constructor.
* @param c The class to document
*/
public static void processClass(ClassDoc c) {
// Write out the panel header information.
out.println(":h2 x=right y=top width=80% height=100% global res="+getClassId(c)+" id='"+c.qualifiedName()+"'."+c.name());
// Write out the yello menu bar across the top
out.println(":font facename='System Proportional' size=20x20.:color fc=black bc=yellow.");
out.print(":link reftype=hd res="+packageListId+".Overview:elink. :link reftype=hd res="+currentPackageId+".Package:elink. ");
out.print(":link reftype=hd refid='help'.Help:elink.");
// If in graphics mode, stick the Java logo to the right of the yellow bar
if (graphics) {
out.print(":artwork runin name='java.bmp'.");
}
// Write out the package name in small text.
out.println("");
out.println(":color fc=default bc=default.");
out.println(":font facename='Tms Rmn' size=12x12.");
out.println(":p."+c.containingPackage().name());
out.println(":p.");
out.println(":font facename='Tms Rmn' size=30x30.");
// Write out the class or interface name in large blue text.
if (c.isInterface()) {
out.println(":hp4.Interface "+c.name());
}
else {
out.println(":hp4.Class "+c.name());
}
out.println(":font facename='default' size=0x0.:ehp4.");
// Print the superclass hierarchy for this class, followed by a black horizontal line.
printClassTree(c);
out.println(":cgraphic.─────────────────────────────────────────────────────────────────────────-:ecgraphic.");
// Print out the modifiers for the class, followed by what class it extends, and what interfaces it implements
// (if any). For any type we can find a link to, we make it linkable.
out.println(":xmp."+c.modifiers()+" :hp2."+c.name()+":ehp2.");
if (c.superclass() != null) {
out.println("extends "+getLink(c.superclass().qualifiedName(),c.superclass().name()));
}
ClassDoc[] interfaces = c.interfaces();
if (interfaces.length > 0) {
out.print("implements ");
for (int i=0;i<interfaces.length;i++) {
out.print(getLink(interfaces[i].qualifiedName(),interfaces[i].name()));
if (i != interfaces.length-1) {
out.print(",");
}
}
out.println("");
}
out.println(":exmp.");
// Print out the comment text. Convert html tags to ipf
out.println(":p."+processCommentText(c.commentText()));
out.println("");
// Write out any @see tags as links.
SeeTag[] seeTags = c.seeTags();
if (seeTags.length > 0) {
out.println(":p.:hp2.See Also&colon.:ehp2.");
out.print(":p. ");
for (int i=0;i<seeTags.length;i++) {
ClassDoc doc = seeTags[i].referencedClass();
if (doc != null) {
out.print(getLink(doc.qualifiedName(),doc.name()));
}
else {
out.print(seeTags[i].referencedClassName());
}
if (i != seeTags.length-1) {
out.println(", ");
}
}
}
// Get a list of all the fields, and sort it with an ArrayList
FieldDoc[] fields = c.fields();
ArrayList fieldArray=new ArrayList();
for (int i=0;i<fields.length;i++) {
fieldArray.add(fields[i]);
}
Collections.sort(fieldArray);
for (int i=0;i<fields.length;i++) {
fields[i] = (FieldDoc) fieldArray.get(i);
}
// Write out the field summary table
int fieldRef = ref;
if (fields.length > 0) {
if (!graphics) {
out.println(":color fc=black bc=palegray. :font facename='System Proportional' size=20x20.");
out.println(":xmp.:hp2.Field Summary .:ehp2.:exmp.");
out.println(":font facename='default' size=0x0. :color fc=default bc=default.");
}
else {
out.println(":artwork runin name='FieldSummary.bmp'.");
}
out.println(":table cols='15 45'.");
for (int i=0;i<fields.length;i++) {
out.println(":row.");
out.println(":c."+fields[i].modifiers()+" "+fields[i].type().typeName()+fields[i].type().dimension());
out.println(":c.:link reftype=hd res="+ref+"."+fields[i].name()+":elink.");
ref++;
out.print(":p. ");
Tag[] summary = fields[i].firstSentenceTags();
for (int j=0;j<summary.length;j++) {
if (summary[j].kind().equalsIgnoreCase("text")) {
out.print(processCommentText(summary[j].text()));
}
}
out.println("");
}
out.println(":etable.");
}
// Get a list of all the constructors. Since they all have the same name, we don't sort this list.
ConstructorDoc[] constructors = c.constructors();
int constructorRef = ref;
// Write out the constructor summary table
if (constructors.length > 0) {
if (!graphics) {
out.println(":color fc=black bc=palegray. :font facename='System Proportional' size=20x20.");
out.println(":xmp.:hp2.Constructor Summary .:ehp2.:exmp.");
out.println(":font facename='default' size=0x0. :color fc=default bc=default.");
}
else {
out.println(":artwork runin name='ConstructorSummary.bmp'.");
}
out.println(":table cols='60'.");
for (int i=0;i<constructors.length;i++) {
out.println(":row.");
out.println(":c.:link reftype=hd res="+ref+"."+constructors[i].name()+":elink."+getParamString(constructors[i]));
ref++;
out.print(":p. ");
Tag[] summary = constructors[i].firstSentenceTags();
for (int j=0;j<summary.length;j++) {
if (summary[j].kind().equalsIgnoreCase("text")) {
out.print(processCommentText(summary[j].text()));
}
}
out.println("");
}
out.println(":etable.");
}
// Get a list of all the methods and sort it with an ArrayList
int methodRef = ref;
MethodDoc[] methods = c.methods();
ArrayList methodArray=new ArrayList();
for (int i=0;i<methods.length;i++) {
methodArray.add(methods[i]);
}
Collections.sort(methodArray);
for (int i=0;i<methods.length;i++) {
methods[i] = (MethodDoc) methodArray.get(i);
}
// Write out the method summary table
if (methods.length > 0) {
if (!graphics) {
out.println(":color fc=black bc=palegray. :font facename='System Proportional' size=20x20.");
out.println(":xmp.:hp2.Method Summary .:ehp2.:exmp.");
out.println(":font facename='default' size=0x0. :color fc=default bc=default.");
}
else {
out.println(":artwork runin name='MethodSummary.bmp'.");
}
out.println(":table cols='15 45'.");
for (int i=0;i<methods.length;i++) {
out.println(":row.");
out.println(":c."+methods[i].modifiers()+" " +methods[i].returnType().typeName()+methods[i].returnType().dimension());
out.println(":c.:link reftype=hd res="+ref+"."+methods[i].name()+":elink."+getParamString(methods[i]));
ref++;
out.print(":p. ");
Tag[] summary = methods[i].firstSentenceTags();
for (int j=0;j<summary.length;j++) {
if (summary[j].kind().equalsIgnoreCase("text")) {
out.print(processCommentText(summary[j].text()));
}
}
out.println("");
}
out.println(":etable.");
}
// Output all the fields in separate windows
if (fields.length > 0) {
processFields(fields, fieldRef);
}
// Output all the constructors in separate windows
if (constructors.length > 0) {
processConstructors(constructors,constructorRef);
}
// Output all the methods in separate windows
if (methods.length > 0) {
processMethods(methods,methodRef);
}
}
/**
* Write out a bunch of constructors in individual windows. This method is called
* by processClass to write out windows for each constructor for the class. These are
* one heading level below the class pane.
* @param docs Array of constructor information
* @param startRes The window id to use for the first constructor. Add one for second constructor, and so forth.
*/
public static void processConstructors(ConstructorDoc[] docs, int startRes) {
out.println(":h3.Constructors");
out.println(":p.");
for (int i=0;i<docs.length;i++) {
out.println(":h4 x=right y=top width=80% height=100% res="+startRes+"."+docs[i].name());
startRes++;
out.println(":font facename='Tms Rmn' size=24x24.");
out.println(":hp4."+docs[i].name()+":ehp4.");
out.println(":font facename='default' size=0x0.");
out.println(":p.");
out.println(docs[i].modifiers()+" :hp2."+docs[i].name()+":ehp2."+getParamString(docs[i]));
Parameter[] params = docs[i].parameters();
int j;
if (params.length > 0) {
out.println(":p.");
out.println(":lm margin=5.");
out.println(processCommentText(docs[i].commentText()));
ParamTag[] pTags = docs[i].paramTags();
out.println(":p.:hp2.Parameters&colon.:ehp2.");
out.println(":lm margin=10.");
for (j=0;j<params.length;j++) {
out.print(":p.:hp4."+params[j].name()+":ehp4. - ");
for (int l=0;l<pTags.length;l++) {
if (pTags[l].parameterName().equals(params[j].name())) {
out.print(processCommentText(pTags[l].parameterComment()));
break;
}
}
out.println("");
}
out.println(":lm margin=1.");
}
int prevRes = startRes - 2;
out.print(":p.");
if (prevRes >= 1) {
out.print(":link reftype=hd res="+prevRes+".Previous:elink. ");
}
int nextRes = startRes;
out.print(":link reftype=hd res="+nextRes+".Next:elink. ");
out.println(getLink(docs[i].containingClass().qualifiedName(), "Return to Class "+docs[i].containingClass().name()));
}
}
/**
* Write out a bunch of methods in individual windows. This method is called
* by processClass to write out windows for each method for the class. These are
* one heading level below the class pane.
* @param docs Array of nethod information
* @param startRes The window id to use for the first method. Add one for second method, and so forth.
*/
public static void processMethods(MethodDoc[] docs, int startRes) {
out.println(":h3.Methods");
out.println(":p.");
for (int i=0;i<docs.length;i++) {
out.println(":h4 x=right y=top width=80% height=100% res="+startRes+"."+docs[i].name());
startRes++;
out.println(":font facename='Tms Rmn' size=24x24.");
out.println(":hp4."+docs[i].name()+":ehp4.");
out.println(":font facename='default' size=0x0.");
out.println(":p.");
Type t = docs[i].returnType();
out.println(docs[i].modifiers()+" "+getLink(t.qualifiedTypeName(), t.typeName())+t.dimension()+" :hp2."+docs[i].name()+":ehp2."+getParamString(docs[i]));
String exc = getExceptionsString(docs[i]);
if (exc.length() > 0) {
out.println(".br");
out.println(" throws "+exc);
}
out.println(":lm margin=5.");
out.println(":p."+processCommentText(docs[i].commentText()));
Parameter[] params = docs[i].parameters();
int j;
if (params.length > 0) {
out.println(":p.:hp2.Parameters&colon.:ehp2.");
out.println(":lm margin=10.");
ParamTag[] pTags = docs[i].paramTags();
for (j=0;j<params.length;j++) {
out.print(":p."+params[j].name()+" - ");
for (int l=0;l<pTags.length;l++) {
if (pTags[l].parameterName().equals(params[j].name())) {
out.print(processCommentText(pTags[l].parameterComment()));
break;
}
}
out.println("");
}
out.println(":lm margin=1.");
}
// Do the return type
Tag[] tags = docs[i].tags();
for (j=0;j<tags.length;j++) {
if (tags[j].name().equalsIgnoreCase("@return")) {
out.println(":lm margin=5.");
out.println(":p.:hp2.Returns&colon.:ehp2.");
out.println(":lm margin=10.:p."+processCommentText(tags[j].text()));
}
out.println(":lm margin=1.");
}
// Exceptions
ThrowsTag[] throwsTags = docs[i].throwsTags();
if (throwsTags.length > 0) {
out.println(":lm margin=5.");
out.println(":p.:hp2.Throws&colon.:ehp2.");
out.println(":lm margin=10.");
for (j=0;j<throwsTags.length;j++) {
out.println(":p."+throwsTags[j].exceptionName()+" - "+processCommentText(throwsTags[j].exceptionComment()));
}
out.println(":lm margin=1.");
}
// See Tags
SeeTag[] seeTags = docs[i].seeTags();
if (seeTags.length > 0) {
out.println(":lm margin=5.");
out.println(":p.:hp2.See Also&colon.:ehp2.");
out.print(":lm margin=10.:p.");
for (j=0;j<seeTags.length;j++) {
ClassDoc doc = seeTags[j].referencedClass();
if (doc != null) {
out.print(getLink(doc.qualifiedName(),doc.name()));
}
else {
out.print(seeTags[j].referencedClassName());
}
if (j != seeTags.length-1) {
out.println(", ");
}
}
out.println(":lm margin=1.");
}
int prevRes = startRes - 2;
out.print(":p.");
if (prevRes >= 1) {
out.print(":link reftype=hd res="+prevRes+".Previous:elink. ");
}
int nextRes = startRes;
out.print(":link reftype=hd res="+nextRes+".Next:elink. ");
out.println(getLink(docs[i].containingClass().qualifiedName(), "Return to Class "+docs[i].containingClass().name()));
}
}
/**
* Write out a bunch of fields in individual windows. This method is called
* by processClass to write out windows for each field for the class. These are
* one heading level below the class pane.
* @param docs Array of field information
* @param startRes The window id to use for the first field. Add one for second field, and so forth.
*/
public static void processFields(FieldDoc[] docs, int startRes) {
out.println(":h3.Fields");
out.println(":p.");
for (int i=0;i<docs.length;i++) {
out.println(":h4 x=right y=top width=80% height=100% res="+startRes+"."+docs[i].name());
startRes++;
out.println(":font facename='Tms Rmn' size=24x24.");
out.println(":hp4."+docs[i].name()+":ehp4.");
out.println(":font facename='default' size=0x0.");
out.println(":p.");
out.println(docs[i].modifiers()+" "+docs[i].type().typeName()+docs[i].type().dimension()+" :hp2."+docs[i].name()+":ehp2.");
out.println(":p."+processCommentText(docs[i].commentText()));
int prevRes = startRes - 2;
out.print(":p.");
if (prevRes >= 1) {
out.print(":link reftype=hd res="+prevRes+".Previous:elink. ");
}
int nextRes = startRes;
out.print(":link reftype=hd res="+nextRes+".Next:elink. ");
out.println(getLink(docs[i].containingClass().qualifiedName(), "Return to Class "+docs[i].containingClass().name()));
}
}
/**
* Outputs ipf tagging for superclass hierarchy for a class. This is the little hierarchical
* chart shown in each class pane. Wherever possible we use live links to each superclass.
* @param doc The class to print the chart for
*/
public static void printClassTree(ClassDoc doc) {
// Build a list of all superclasses and then reverse it, since we want to show it in reverse order.
ArrayList superClasses = new ArrayList();
ClassDoc sClass = doc.superclass();
if (sClass == null) return;
while(sClass != null) {
superClasses.add(sClass);
sClass = sClass.superclass();
}
Collections.reverse(superClasses);
// Each level in indented 2 character positions more than the previous one
int indent=1;
for (int i=0;i<superClasses.size();i++) {
out.println(":lm margin="+indent+".");
// If not in the first indent level, we draw the little line that connects this level to the previous one.
if (indent > 1) {
out.println("|");
out.print("+--");
}
else {
out.print(":p.:xmp.");
}
sClass = (ClassDoc) superClasses.get(i);
out.println(getLink(sClass.qualifiedName(),sClass.qualifiedName()));
indent += 2;
}
out.println(":lm margin=1.:exmp.");
}
/**
* Determines if the specified package is one being processed in this file. If the package is not internal
* then references to it must be looked up in the mapping file. We need to know this to decide how to
* link to a package: with an internal or external link.
* @param pack The package name to check for
* @return true if the package is being documented in the ipf file being generated.
*/
public static boolean isPackageInternal(String pack) {
for (int i=0;i<ourPackages.length;i++) {
if (ourPackages[i].equals(pack)) {
return true;
}
}
return false;
}
/**
* Returns a string containing all the parameters for a method. The parameters are separated by
* commas and surrounded by brackets.
* @param method The method to get the parameter list for
* @return String with the parameter list
*/
public static String getParamString(ExecutableMemberDoc method) {
StringBuffer out = new StringBuffer("(");
Parameter[] p = method.parameters();
int i;
if (p.length > 0) {
for (i=0;i<p.length-1;i++) {
out.append(p[i]+", ");
}
out.append(p[i]);
}
out.append(")");
return new String(out);
}
/**
* Returns a string with a comma-separate list of all the exceptions that may be thrown by a method.
* @param method The method to get the exception list for.
* @return The comma-separated list of exceptions.
*/
public static String getExceptionsString(ExecutableMemberDoc method) {
StringBuffer out = new StringBuffer();
ClassDoc[] exc = method.thrownExceptions();
int i;
if (exc.length > 0) {
for (i=0;i<exc.length-1;i++) {
out.append(exc[i].typeName()+", ");
}
out.append(exc[i].typeName());
}
return new String(out);
}
/**
* This method is called to convert certain characters in a string to their symbolic representation in
* ipf. For example, you cannot leave a colon or an ampersand in clear text, because it will be interpreted
* as the start of a tag or symbol. So, they are replaced with their symbolic representation.
* Also, \n is replaced with \r\n, and the next character, which is always a space is removed. This gets
* around a little difference in the way javadoc returns tag strings and the way IPFC wants to handle them.
* @param in The string to process
* @return Processed string.
*/
public static String processSymbols(String in) {
StringBuffer out = new StringBuffer();
char c;
for (int i=0;i<in.length();i++) {
c = in.charAt(i);
if (c == ':') {
out.append("&colon.");
}
else if (c == '&') {
out.append("&.");
}
else if (c == '\n') {
out.append("\r\n");
// Remove the space that follows
i++;
}
else {
out.append(c);
}
}
return new String(out);
}
/**
* Process the comment or tag text to convert html tagging to ipf tagging. Sun, in particular, uses a lot
* of html tags in its comments to format them nicely when converted to javadoc format. Fortunately, many
* of these can easily be directly converted to ipf. Tags which are not recognized are omitted. This is not
* perfect, but usually what comes out is reasonably decent looking. <code>processSymbols</code> is also
* called by this method to fix up any tricky symbols.
* @param in The text to process
* @return Cleaned up test.
*/
public static String processCommentText(String in) {
in = processSymbols(in);
StringBuffer out = new StringBuffer();
StringTokenizer tok = new StringTokenizer(in, "<>", true );
String token = null;
boolean inTag = false;
String flag = null;
while (tok.hasMoreTokens()) {
token = tok.nextToken();
if (token.equals("<")) {
if (!inTag) {
inTag = true;
flag = null;
}
else {
out.append(token);
}
}
else if (token.equals(">")) {
if (inTag) {
inTag = false;
if (flag != null) {
if (flag.equalsIgnoreCase("b")) {
out.append(":hp2.");
}
else if (flag.equalsIgnoreCase("/b")) {
out.append(":ehp2.");
}
else if (flag.equalsIgnoreCase("p")) {
out.append(":p.");
}
else if (flag.equalsIgnoreCase("i")) {
out.append(":hp1.");
}
else if (flag.equalsIgnoreCase("/i")) {
out.append(":ehp1.");
}
else if (flag.equalsIgnoreCase("code")) {
out.append(":font facename='System Monospaced' size=12x12.");
}
else if (flag.equalsIgnoreCase("/code")) {
out.append(":font facename='default'.");
}
else if (flag.equalsIgnoreCase("ul")) {
out.append(":ul.");
}
else if (flag.equalsIgnoreCase("/ul")) {
out.append(":eul.");
}
else if (flag.equalsIgnoreCase("ol")) {
out.append(":ol.");
}
else if (flag.equalsIgnoreCase("/ol")) {
out.append(":eol.");
}
else if (flag.equalsIgnoreCase("li")) {
out.append(":li.");
}
else if (flag.equalsIgnoreCase("dl")) {
out.append(":parml break=fit tsize=15 compact.");
}
else if (flag.equalsIgnoreCase("/dl")) {
out.append(":eparml.");
}
else if (flag.equalsIgnoreCase("dt")) {
out.append(":pt.");
}
else if (flag.equalsIgnoreCase("dd")) {
out.append(":pd.");
}
else if (flag.equalsIgnoreCase("hr")) {
out.append(":cgraphic.────────────────────────────────────────────────────────────────────────────────:ecgraphic.");
}
else if (flag.equalsIgnoreCase("pre")) {
out.append(":fig.");
}
else if (flag.equalsIgnoreCase("/pre")) {
out.append(":efig.");
}
flag = null;
}
}
else {
out.append(token);
}
}
else {
if (inTag) {
flag = token;
}
else {
out.append(token);
}
}
}
return (new String(out));
}
/**
* Returns how many option words specify a given tag. This is part of the command-line option
* processing for javadoc. Any unregonized tags are passed in here, and it returns how
* many symbols that tag should be. For example, for -f which takes a filename as an argument,
* it would return 2 (1 for the tag itself, and one for the filename).
* @param option The option to get length for
* @return The length for the option.
*/
public static int optionLength(String option) {
if (option.equals("-f")) {
return 2;
}
else if (option.equals("-title")) {
return 2;
}
else if (option.equals("-map")) {
return 2;
}
else if (option.equals("-graphics")) {
return 1;
}
return 0;
}
/**
* Processes our extra command line options. There are several extra command line options supported
* by this doclet, and this scans through the options presented, and handles them appropriately.
* No error checking is performed at this time.
* @param options Array of options
*/
public static void readOptions(String[][] options) {
for (int i=0;i<options.length;i++) {
String[] opt = options[i];
if (opt[0].equals("-f")) {
outputFileName = opt[1];
}
else if (opt[0].equals("-title")) {
title=opt[1];
}
else if (opt[0].equals("-map")) {
mappingFileName=opt[1];
}
else if (opt[0].equals("-graphics")) {
graphics = true;
}
}
}
}