home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Java / Java.zip / ipfdoclt.zip / IPFDoclet.java < prev    next >
Text File  |  1999-02-15  |  47KB  |  1,074 lines

  1. import com.sun.javadoc.*;
  2. import java.util.*;
  3. import java.io.*;
  4.  
  5. /**
  6.  * This class is a doclet that generates Javadoc help as IPF tags.  IPF is IBM's
  7.  * help system for OS/2.  There are also viewers available for other platforms,
  8.  * for example, Windows NT.  The advantages of IPF over html are several:  it is
  9.  * much more compact - usually about a third the total size once compiled to inf
  10.  * format, the viewer has a much smaller memory footprint than most web browsers,
  11.  * and the inf files are searchable, which is generally not possible without some
  12.  * kind of search engine for html.  On the down side, the IPF format is not as rich
  13.  * as html, so the output tends not to be as pretty.
  14.  * <p>
  15.  * This doclet adds the following command line options to javadoc:
  16.  * <dl>
  17.  * <dt>-f <file>
  18.  * <dd>Specify the output filename.  Defaults to javadoc.ipf
  19.  * <dt>-title "title"
  20.  * <dd>Specify the title for the inf file
  21.  * <dt>-graphics
  22.  * <dd>When specified graphics will be used for some headings
  23.  * <dt>-map
  24.  * <dd>Specify the map file to use.  The map file maps package names to
  25.  *                     INF files so that links outside of the file work properly.
  26.  * </dl>
  27. */
  28. public class IPFDoclet {
  29. /** The output is written to this Writer. */
  30.     static private PrintWriter out = null;
  31. /** This variable is incremented every time a heading id is specified, so that each
  32.     heading id will be unique.
  33. */
  34.     static private int ref = 1;
  35. /** The name of the output file.  This defaults to javadoc.ipf, but can be overridden
  36.     by the -f command line option.
  37. */
  38.     static private String outputFileName="javadoc.ipf";
  39. /** The title to use for the inf file.  Defaults to "Javadoc Documentation" */
  40.     static private String title="Javadoc Documentation";
  41. /** The name of the mapping file.  Defaults to javadoc.map" */
  42.     static private String mappingFileName="javadoc.map";
  43. /** The name of this inf file as it will be inserted into the map file.  This is just the
  44.     <code>outputFileName</code> with the extension changed to .inf.
  45. */
  46.     static private String mapName="";
  47. /** This properties object is used to load the mapping file */
  48.     static private Properties mapping = null;
  49. /** An array of all the package names documented in the IPF being generated. */
  50.     static private String[] ourPackages = null;
  51. /** This hashtable is used to match heading ids to fully qualified class names.  We use these to look
  52.     up the ids for @see tags and other links internal to the generated ipf file.
  53. */
  54.     static private Hashtable classIds = null;
  55. /** Holds the link id of the package currently being processed */
  56.     static private int currentPackageId = 1;
  57. /** Holds the id of the panel with the list of all packages */
  58.     static private int packageListId = 1;
  59. /** Holds the id of the panel listing all classes in the generated file */
  60.     static private int allClassesRef = 1;
  61. /** True if command line specified that graphics should be used */
  62.     static private boolean graphics = false;
  63. /** This is the RootDoc object passed to the start method.  We keep a static reference to it
  64.     because it has the DocErrorReporter in it that we may need to use.
  65. */
  66.     static private RootDoc doc=null;
  67.  
  68. /**
  69.  * Called to start processing documentation.  This is the entry point for the doclet, and
  70.  * it handles outputting all the preliminary panels such as the package list, and all classes
  71.  * files, and sets up the class ids.  It then processes each package one by one by calling
  72.  * <code>processPackage</code>.
  73.  * @param _doc The document object to process
  74.  * @return true on success.
  75.  */
  76.     public static boolean start(RootDoc _doc) {
  77.         doc = _doc; // Save _doc in a static field
  78.  
  79. // Read process any command line options
  80.         readOptions(doc.options());
  81.  
  82. // Read the external mapping list
  83.         mapping = new Properties();
  84.         File propertiesFile = new File(mappingFileName);
  85.         if (propertiesFile.exists()) {
  86.             try {
  87.                 FileInputStream in = new FileInputStream(propertiesFile);
  88.                 mapping.load(in);
  89.                 in.close();
  90.             }
  91.             catch (IOException exc) {
  92.                 doc.printError(exc.getMessage());
  93.             }
  94.         }
  95.  
  96. // Get a File object for the output file
  97.         File f = new File(outputFileName);
  98.  
  99. // Strip the extension from the output file name and append the extension .inf.  This is
  100. // the name that is inserted into the mapping file for all the packages handled by this
  101. // inf file.
  102.         mapName = f.getName();
  103.         int index = mapName.lastIndexOf(".");
  104.         if (index != -1) {
  105.             mapName = mapName.substring(0,index);
  106.         }
  107.         mapName += ".inf";
  108.  
  109. // Assign ids to all of the classes to be processed.  They are stored in the hashtable
  110. // keyed by their fully qualfied class name
  111.         ClassDoc[] classes = doc.classes();
  112.         classIds = new Hashtable(classes.length);
  113.         for (int i=0;i<classes.length;i++) {
  114.             classIds.put(classes[i].qualifiedName(), new Integer(ref++));
  115.         }
  116.  
  117. // Open the output file
  118.         try {
  119.             out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(new File(outputFileName))));
  120.         }
  121.         catch (IOException exc) {
  122.             doc.printError("Error opening output file");
  123.             return false;
  124.         }
  125.  
  126. // Write the IPF header information to the output file
  127.         header();
  128.  
  129. // Get a list of all packages being processed
  130.         PackageDoc[] packages = doc.specifiedPackages();
  131.  
  132.         int packageRef = 0;
  133. // Generate the window listing all the packages
  134.         if (packages.length > 0) {
  135.             out.println(":h1 group=1 x=left y=top width=20% height=40% res="+ref+".All Packages");
  136.             out.println(":hp2.This File&colon.:ehp2.");
  137.             packageListId = ref;    // Store the id of this window
  138.             ref++;
  139.             allClassesRef = ref;    // Store the id of the All Classes window and write a link to it.
  140.             out.println(".br");
  141.             out.println(":link reftype=hd res="+ref+".All Classes:elink.");
  142.             out.println(".br");
  143.             ref++;
  144.  
  145.             packageRef = ref;   // Store the id that refers to the package window
  146.             ourPackages = new String[packages.length];  // An array of the package names of all the packages we are processing
  147.  
  148. // Write out a link to each package window.  The ids of these will sequentially increment from packageRef.
  149.             for (int i=0;i<packages.length;i++) {
  150.                 out.println(":link reftype=hd res="+ref+"."+packages[i].name()+":elink.");
  151.                 ref++;
  152.                 out.println(".br");
  153. // Put the name of the package in ourPackages array
  154.                 ourPackages[i] = packages[i].name();
  155.             }
  156.  
  157. // Output external links for all the packages we know about from other files.  These are found
  158. // in the mapping file.  External links use the package name as the link id.
  159.             out.println(":p.:hp2.Linked Files&colon.:ehp2.");
  160.             out.println(".br");
  161.  
  162. // Copy the package names into an ArrayList so they can be sorted alphabetically.
  163.             ArrayList linkedPackages = new ArrayList();
  164.             Enumeration enum = mapping.propertyNames();
  165.             while (enum.hasMoreElements()) {
  166.                 linkedPackages.add(enum.nextElement());
  167.             }
  168.             Collections.sort(linkedPackages);
  169.             for (int i=0;i<linkedPackages.size();i++) {
  170.                 String pack = (String) linkedPackages.get(i);
  171.                 if (!isPackageInternal(pack)) {
  172.                     out.println(":link reftype=hd database='"+(String)mapping.get(pack)+"' refid='"+pack+"'."+pack+":elink.");
  173.                     out.println(".br");
  174.                 }
  175.             }
  176.         }
  177.  
  178. // Print out the allClasses list
  179.         if (packages.length != 0) {
  180.             out.println(":h1 group=2 x=left y=bottom width=20% height=60% res="+allClassesRef+".All Classes");
  181.         }
  182.         else {
  183.             allClassesRef = ref;
  184.             ref++;
  185.             out.println(":h1 group=2 x=left y=bottom width=20% height=100% res="+allClassesRef+".All Classes");
  186.         }
  187.  
  188.         out.println(".br");
  189.         for (int i=0;i<classes.length;i++) {
  190.             out.println(getLink(classes[i].qualifiedName(), classes[i].name()));
  191.             out.println(".br");
  192.         }
  193.  
  194. // Process each package.
  195.         if (packages.length > 0) {
  196.             classes = null; // We don't need this list anymore so let it get garbage collected.
  197.             for (int i=0;i<packages.length;i++) {
  198.                 processPackage(packages[i], packageRef+i);
  199.                 System.gc();    // Try and save some memory
  200.             }
  201. // Put the packages in the map and write it back to disk.
  202.             for (int i=0;i<packages.length;i++) {
  203.                 mapping.setProperty(packages[i].name(), mapName);
  204.             }
  205.             try {
  206.                 FileOutputStream out = new FileOutputStream(propertiesFile);
  207.                 mapping.store(out, "IPF Javadoc Mapping File");
  208.                 out.close();
  209.             }
  210.             catch (IOException exc) {
  211.                 System.err.println(exc.getMessage());
  212.             }
  213.         }
  214.         else {
  215.             for (int i=0;i<classes.length;i++) {
  216.                 processClass(classes[i]);
  217.             }
  218.         }
  219. // Write out the help page
  220.         help();
  221. // Write out the trailer
  222.         trailer();
  223. // Close the output file
  224.         out.close();
  225.         return true;
  226.     }
  227.  
  228. /**
  229.  * Writes the ipf header.  This consists of a userdoc tag, the docprof tag, and the title tag.
  230.  *
  231.  */
  232.     public static void header() {
  233.         out.println(":userdoc.");
  234.         out.println(":docprof toc=1234.");
  235.         out.println(":title."+title);
  236.     }
  237.  
  238.  
  239. /**
  240.  * Writes the trailer for the ipf file.  This is just a euserdoc tag.
  241.  */
  242.     public static void trailer() {
  243.         out.println(":euserdoc.");
  244.     }
  245.  
  246. /**
  247.  * Writes out the help page.  This is a brief description of the help format that is linked to
  248.  * in the yellow bar at the top of each method page.
  249.  */
  250.     public static void help() {
  251.         out.println(":h1 name='help'.Help on IPF Javadoc Documentation");
  252.         out.println(":hp4.Help on IPF Javadoc Documentation:ehp4.");
  253.         out.println(":p.This file was generated automatically using a JDK 1.2 Javadoc Doclet.  Doclets allow");
  254.         out.println("the Javadoc documentation to be output in a different format than the standard html format");
  255.         out.println("provided by Sun.  In this case, it was used to output to OS/2 IPF format, which was then");
  256.         out.println("compiled to an INF file.");
  257.         out.println(":p.There are certain limitations imposed by the nature of the IPF format.  For one thing,");
  258.         out.println("especially in Sun's documentation, considerable use is made of html tags in the documentation.");
  259.         out.println("The doclet does it's best to convert these to the corresponding IPF tags, but html is a richer");
  260.         out.println("format than IPF, and this cannot always be done.  For example, in IPF it is not possible to change");
  261.         out.println("the font inside a table, which is done quite frequently in Sun's documentation.");
  262.         out.println(":p.This documentation does not contain all of the information present in Sun's documentation.  For");
  263.         out.println("example, there is no deprecated section for each class, and inherited methods are not listed.");
  264.         out.println("This was simply my choice in formatting the documentation - if you would like to have this, then");
  265.         out.println("all you have to do is download the source for the doclet, and modify it to do so.");
  266.         out.println(":p.The best way to use this documentation is to double-click on the All Packages entry in the");
  267.         out.println("table of contents immediately after opening the file.  This opens a panel listing all the packages");
  268.         out.println("in the file (as well as any in linked files that are available).  Double-clicking on one of these");
  269.         out.println("will bring up a panel listing all the classes in the package, and you can then select the class");
  270.         out.println("you want to display.");
  271.         out.println(":p.Throughout the documentation, there may be links to JavaDoc documentation that is outside of");
  272.         out.println("the file.  For example, in the class hierarchy tree, there will always be at least a pointer to");
  273.         out.println("java.lang.Object.  If you have the appropriate inf files containing the javadoc information");
  274.         out.println("for these classes, the links will be live and will work properly (ie. when you double-click on the");
  275.         out.println("link, the panel will be automatically loaded from the external file).  If you don't have the file");
  276.         out.println("then the link will not be active, and will be shown in black instead of blue.  The other inf files");
  277.         out.println("should be in the same directory, or on the BOOKSHELF path.");
  278.  
  279.  
  280.     }
  281.  
  282.  
  283. /**
  284.  * Processes an individual package.  This means we write out a package window containing links to all the
  285.  * classes in the package, and then write out each class window.
  286.  * @param p The package to document
  287.  * @param id The id to use for the package window.  These have already been predetermined in the
  288.  *           calling routine.
  289.  */
  290.     public static void processPackage(PackageDoc p, int id) {
  291.         doc.printNotice("Processing package "+p.name());
  292. // Write the window header.  This is a narrow window in the lower left (below the all packages window).
  293.         out.println(":h1 group=2 x=left y=bottom width=20% height=60% global res="+id+" id='"+p.name()+"'."+p.name());
  294.         currentPackageId=id;
  295.  
  296. // Get all the classes in the package, and sort them using an ArrayList.
  297.         ClassDoc[] classes=p.allClasses();
  298.         ArrayList classArray = new ArrayList(classes.length);
  299.         for (int i=0;i<classes.length;i++) {
  300.             classArray.add(classes[i]);
  301.         }
  302.         Collections.sort(classArray);
  303.  
  304. // Copy sorted ArrayList back into a regular array.
  305.         for (int i=0;i<classes.length;i++) {
  306.             classes[i] = (ClassDoc) classArray.get(i);
  307.         }
  308. // If this isn't output, the first line will not be indented like the rest of them.
  309.         out.println(".br");
  310.  
  311. // Write out each class as a link.  Interfaces are written with the hp1 property, which means they are
  312. // italicized.
  313.         for (int i=0;i<classes.length;i++) {
  314.             if (classes[i].isInterface()) {
  315.                 out.println(":link reftype=hd res="+getClassId(classes[i])+".:hp1."+classes[i].name()+":ehp1.:elink.");
  316.             }
  317.             else {
  318.                 out.println(":link reftype=hd res="+getClassId(classes[i])+"."+classes[i].name()+":elink.");
  319.             }
  320.             out.println(".br"); // Break goes to next line
  321.         }
  322.  
  323. // Process each class
  324.         for (int i=0;i<classes.length;i++) {
  325.             processClass(classes[i]);
  326.         }
  327.     }
  328.  
  329.  
  330. /**
  331.  * Gets the window id for a given class.  Only works on classes that are going to be in the ipf file we
  332.  * are generating.  It finds it by looking it up in the hashtable <code>classIds</code>.
  333.  * @param c The class to get the window id for.
  334.  * @return The window id of the specified class.
  335.  */
  336.     public static int getClassId(ClassDoc c) {
  337.         return getClassId(c.qualifiedName());
  338.     }
  339.  
  340. /**
  341.  * Gets the window id for a given class.  Only works on classes that are going to be in the ipf file we
  342.  * are generating.  It finds it by looking it up in the hashtable <code>classIds</code>.
  343.  * @param className The fully qualified class name to get the window id for.
  344.  * @return The window id of the specified class.
  345.  */
  346.     public static int getClassId(String className) {
  347.         Integer id = (Integer) classIds.get(className);
  348.         if (id == null) {
  349.             return 0;
  350.         }
  351.         return id.intValue();
  352.     }
  353.  
  354. /**
  355.  * Gets IPF tagging for link to specified class.  This produces a string containing the complete
  356.  * link to elink sequence for a link to the class.  If the class is one being documented in the file
  357.  * that is being generated, the link is an internal one to it's numeric id.  If it is in an external
  358.  * database and the package can be found in the mapping file, the link is an external one to the
  359.  * id specified by the fully qualified class name.  If it cannot be found in the map file, then the
  360.  * string returned is just the text, with no link information.
  361.  * @param className Fully qualified class name of the class to get the link for
  362.  * @param The text to be shown for the link
  363.  * @return String containing the link text.
  364.  */
  365.     public static String getLink(String className, String text) {
  366. // Try and lookup the class id for an internal link
  367.         int id = getClassId(className);
  368.  
  369. // If the class could not be found internally, then do an external link
  370.         if (id == 0) {
  371.  
  372. // Get the package name for the class
  373.             String pack = className;
  374.             int index = pack.lastIndexOf(".");
  375.             if (index != -1) {
  376.                 pack = pack.substring(0,index);
  377.             }
  378.  
  379. // Look the package up in the mapping file
  380.             String database = mapping.getProperty(pack);
  381. // If we found the package, link to the external database found
  382.             if (database != null) {
  383.                 return ":link reftype=hd refid='"+className+"' database='"+database+"'."+text+":elink.";
  384.             }
  385.             else {
  386. // If we didn't find the package, just return the text - we can't provide a link.
  387.                 return text;
  388.             }
  389.         }
  390.         else {
  391. // Return an internal link.
  392.             return ":link reftype=hd res="+id+"."+text+":elink.";
  393.         }
  394.     }
  395.  
  396.  
  397. /**
  398.  * Produces the ipf for a particular class.  This consists of writing out the class panel with
  399.  * the description of the class, and field, constructor and method summaries, and then writing
  400.  * out the panels for each individual field, method, and constructor.
  401.  * @param c The class to document
  402.  */
  403.     public static void processClass(ClassDoc c) {
  404. // Write out the panel header information.
  405.         out.println(":h2 x=right y=top width=80% height=100% global res="+getClassId(c)+" id='"+c.qualifiedName()+"'."+c.name());
  406. // Write out the yello menu bar across the top
  407.         out.println(":font facename='System Proportional' size=20x20.:color fc=black bc=yellow.");
  408.         out.print(":link reftype=hd res="+packageListId+".Overview:elink. :link reftype=hd res="+currentPackageId+".Package:elink.                                         ");
  409.         out.print(":link reftype=hd refid='help'.Help:elink.");
  410.  
  411. // If in graphics mode, stick the Java logo to the right of the yellow bar
  412.         if (graphics) {
  413.             out.print(":artwork runin name='java.bmp'.");
  414.         }
  415.  
  416. // Write out the package name in small text.
  417.         out.println("");
  418.         out.println(":color fc=default bc=default.");
  419.         out.println(":font facename='Tms Rmn' size=12x12.");
  420.         out.println(":p."+c.containingPackage().name());
  421.         out.println(":p.");
  422.         out.println(":font facename='Tms Rmn' size=30x30.");
  423.  
  424. // Write out the class or interface name in large blue text.
  425.         if (c.isInterface()) {
  426.             out.println(":hp4.Interface "+c.name());
  427.         }
  428.         else {
  429.             out.println(":hp4.Class "+c.name());
  430.         }
  431.         out.println(":font facename='default' size=0x0.:ehp4.");
  432.  
  433. // Print the superclass hierarchy for this class, followed by a black horizontal line.
  434.         printClassTree(c);
  435.         out.println(":cgraphic.─────────────────────────────────────────────────────────────────────────-:ecgraphic.");
  436.  
  437. // Print out the modifiers for the class, followed by what class it extends, and what interfaces it implements
  438. // (if any).  For any type we can find a link to, we make it linkable.
  439.         out.println(":xmp."+c.modifiers()+" :hp2."+c.name()+":ehp2.");
  440.         if (c.superclass() != null) {
  441.             out.println("extends "+getLink(c.superclass().qualifiedName(),c.superclass().name()));
  442.         }
  443.         ClassDoc[] interfaces = c.interfaces();
  444.         if (interfaces.length > 0) {
  445.             out.print("implements ");
  446.             for (int i=0;i<interfaces.length;i++) {
  447.                 out.print(getLink(interfaces[i].qualifiedName(),interfaces[i].name()));
  448.                 if (i != interfaces.length-1) {
  449.                     out.print(",");
  450.                 }
  451.             }
  452.             out.println("");
  453.         }
  454.         out.println(":exmp.");
  455.  
  456. // Print out the comment text.  Convert html tags to ipf
  457.         out.println(":p."+processCommentText(c.commentText()));
  458.         out.println("");
  459.  
  460. // Write out any @see tags as links.
  461.         SeeTag[] seeTags = c.seeTags();
  462.         if (seeTags.length > 0) {
  463.             out.println(":p.:hp2.See Also&colon.:ehp2.");
  464.             out.print(":p.    ");
  465.             for (int i=0;i<seeTags.length;i++) {
  466.                 ClassDoc doc = seeTags[i].referencedClass();
  467.                 if (doc != null) {
  468.                     out.print(getLink(doc.qualifiedName(),doc.name()));
  469.                 }
  470.                 else {
  471.                     out.print(seeTags[i].referencedClassName());
  472.                 }
  473.                 if (i != seeTags.length-1) {
  474.                     out.println(", ");
  475.                 }
  476.             }
  477.         }
  478.  
  479. // Get a list of all the fields, and sort it with an ArrayList
  480.         FieldDoc[] fields = c.fields();
  481.         ArrayList fieldArray=new ArrayList();
  482.         for (int i=0;i<fields.length;i++) {
  483.             fieldArray.add(fields[i]);
  484.         }
  485.         Collections.sort(fieldArray);
  486.         for (int i=0;i<fields.length;i++) {
  487.             fields[i] = (FieldDoc) fieldArray.get(i);
  488.         }
  489.  
  490. // Write out the field summary table
  491.         int fieldRef = ref;
  492.         if (fields.length > 0) {
  493.             if (!graphics) {
  494.                 out.println(":color fc=black bc=palegray. :font facename='System Proportional' size=20x20.");
  495.                 out.println(":xmp.:hp2.Field Summary                                                                    .:ehp2.:exmp.");
  496.                 out.println(":font facename='default' size=0x0. :color fc=default bc=default.");
  497.             }
  498.             else {
  499.                 out.println(":artwork runin name='FieldSummary.bmp'.");
  500.             }
  501.             out.println(":table cols='15 45'.");
  502.             for (int i=0;i<fields.length;i++) {
  503.                 out.println(":row.");
  504.                 out.println(":c."+fields[i].modifiers()+" "+fields[i].type().typeName()+fields[i].type().dimension());
  505.                 out.println(":c.:link reftype=hd res="+ref+"."+fields[i].name()+":elink.");
  506.                 ref++;
  507.                 out.print(":p.  ");
  508.                 Tag[] summary = fields[i].firstSentenceTags();
  509.                 for (int j=0;j<summary.length;j++) {
  510.                     if (summary[j].kind().equalsIgnoreCase("text")) {
  511.                         out.print(processCommentText(summary[j].text()));
  512.                     }
  513.                 }
  514.                 out.println("");
  515.             }
  516.             out.println(":etable.");
  517.         }
  518.  
  519. // Get a list of all the constructors.  Since they all have the same name, we don't sort this list.
  520.         ConstructorDoc[] constructors = c.constructors();
  521.         int constructorRef = ref;
  522.  
  523. // Write out the constructor summary table
  524.         if (constructors.length > 0) {
  525.             if (!graphics) {
  526.                 out.println(":color fc=black bc=palegray. :font facename='System Proportional' size=20x20.");
  527.                 out.println(":xmp.:hp2.Constructor Summary                                                             .:ehp2.:exmp.");
  528.                 out.println(":font facename='default' size=0x0. :color fc=default bc=default.");
  529.             }
  530.             else {
  531.                 out.println(":artwork runin name='ConstructorSummary.bmp'.");
  532.             }
  533.  
  534.             out.println(":table cols='60'.");
  535.             for (int i=0;i<constructors.length;i++) {
  536.                 out.println(":row.");
  537.                 out.println(":c.:link reftype=hd res="+ref+"."+constructors[i].name()+":elink."+getParamString(constructors[i]));
  538.                 ref++;
  539.                 out.print(":p.  ");
  540.                 Tag[] summary = constructors[i].firstSentenceTags();
  541.                 for (int j=0;j<summary.length;j++) {
  542.                     if (summary[j].kind().equalsIgnoreCase("text")) {
  543.                         out.print(processCommentText(summary[j].text()));
  544.                     }
  545.                 }
  546.                 out.println("");
  547.             }
  548.             out.println(":etable.");
  549.  
  550.         }
  551.  
  552. // Get a list of all the methods and sort it with an ArrayList
  553.         int methodRef = ref;
  554.         MethodDoc[] methods = c.methods();
  555.         ArrayList methodArray=new ArrayList();
  556.         for (int i=0;i<methods.length;i++) {
  557.             methodArray.add(methods[i]);
  558.         }
  559.         Collections.sort(methodArray);
  560.         for (int i=0;i<methods.length;i++) {
  561.             methods[i] = (MethodDoc) methodArray.get(i);
  562.         }
  563.  
  564. // Write out the method summary table
  565.         if (methods.length > 0) {
  566.             if (!graphics) {
  567.                 out.println(":color fc=black bc=palegray. :font facename='System Proportional' size=20x20.");
  568.                 out.println(":xmp.:hp2.Method Summary                                                                  .:ehp2.:exmp.");
  569.                 out.println(":font facename='default' size=0x0. :color fc=default bc=default.");
  570.             }
  571.             else {
  572.                 out.println(":artwork runin name='MethodSummary.bmp'.");
  573.             }
  574.             out.println(":table cols='15 45'.");
  575.             for (int i=0;i<methods.length;i++) {
  576.                 out.println(":row.");
  577.                 out.println(":c."+methods[i].modifiers()+" " +methods[i].returnType().typeName()+methods[i].returnType().dimension());
  578.                 out.println(":c.:link reftype=hd res="+ref+"."+methods[i].name()+":elink."+getParamString(methods[i]));
  579.                 ref++;
  580.                 out.print(":p.  ");
  581.                 Tag[] summary = methods[i].firstSentenceTags();
  582.                 for (int j=0;j<summary.length;j++) {
  583.                     if (summary[j].kind().equalsIgnoreCase("text")) {
  584.                         out.print(processCommentText(summary[j].text()));
  585.                     }
  586.                 }
  587.                 out.println("");
  588.             }
  589.             out.println(":etable.");
  590.  
  591.         }
  592.  
  593. // Output all the fields in separate windows
  594.         if (fields.length > 0) {
  595.             processFields(fields, fieldRef);
  596.         }
  597. // Output all the constructors in separate windows
  598.         if (constructors.length > 0) {
  599.             processConstructors(constructors,constructorRef);
  600.         }
  601. // Output all the methods in separate windows
  602.         if (methods.length > 0) {
  603.             processMethods(methods,methodRef);
  604.         }
  605.     }
  606.  
  607.  
  608. /**
  609.  * Write out a bunch of constructors in individual windows.  This method is called
  610.  * by processClass to write out windows for each constructor for the class.  These are
  611.  * one heading level below the class pane.
  612.  * @param docs Array of constructor information
  613.  * @param startRes The window id to use for the first constructor.  Add one for second constructor, and so forth.
  614.  */
  615.     public static void processConstructors(ConstructorDoc[] docs, int startRes) {
  616.         out.println(":h3.Constructors");
  617.  
  618.         out.println(":p.");
  619.         for (int i=0;i<docs.length;i++) {
  620.             out.println(":h4 x=right y=top width=80% height=100% res="+startRes+"."+docs[i].name());
  621.             startRes++;
  622.             out.println(":font facename='Tms Rmn' size=24x24.");
  623.             out.println(":hp4."+docs[i].name()+":ehp4.");
  624.             out.println(":font facename='default' size=0x0.");
  625.             out.println(":p.");
  626.             out.println(docs[i].modifiers()+" :hp2."+docs[i].name()+":ehp2."+getParamString(docs[i]));
  627.             Parameter[] params = docs[i].parameters();
  628.             int j;
  629.             if (params.length > 0) {
  630.                 out.println(":p.");
  631.                 out.println(":lm margin=5.");
  632.                 out.println(processCommentText(docs[i].commentText()));
  633.                 ParamTag[] pTags = docs[i].paramTags();
  634.                 out.println(":p.:hp2.Parameters&colon.:ehp2.");
  635.                 out.println(":lm margin=10.");
  636.  
  637.                 for (j=0;j<params.length;j++) {
  638.                     out.print(":p.:hp4."+params[j].name()+":ehp4. - ");
  639.                     for (int l=0;l<pTags.length;l++) {
  640.                         if (pTags[l].parameterName().equals(params[j].name())) {
  641.                             out.print(processCommentText(pTags[l].parameterComment()));
  642.                             break;
  643.                         }
  644.                     }
  645.                     out.println("");
  646.  
  647.                 }
  648.                 out.println(":lm margin=1.");
  649.             }
  650.             int prevRes = startRes - 2;
  651.             out.print(":p.");
  652.             if (prevRes >= 1) {
  653.                 out.print(":link reftype=hd res="+prevRes+".Previous:elink. ");
  654.             }
  655.             int nextRes = startRes;
  656.             out.print(":link reftype=hd res="+nextRes+".Next:elink. ");
  657.             out.println(getLink(docs[i].containingClass().qualifiedName(), "Return to Class "+docs[i].containingClass().name()));
  658.         }
  659.     }
  660.  
  661.  
  662. /**
  663.  * Write out a bunch of methods in individual windows.  This method is called
  664.  * by processClass to write out windows for each method for the class.  These are
  665.  * one heading level below the class pane.
  666.  * @param docs Array of nethod information
  667.  * @param startRes The window id to use for the first method.  Add one for second method, and so forth.
  668.  */
  669.     public static void processMethods(MethodDoc[] docs, int startRes) {
  670.         out.println(":h3.Methods");
  671.  
  672.         out.println(":p.");
  673.         for (int i=0;i<docs.length;i++) {
  674.             out.println(":h4 x=right y=top width=80% height=100% res="+startRes+"."+docs[i].name());
  675.             startRes++;
  676.             out.println(":font facename='Tms Rmn' size=24x24.");
  677.             out.println(":hp4."+docs[i].name()+":ehp4.");
  678.             out.println(":font facename='default' size=0x0.");
  679.             out.println(":p.");
  680.             Type t = docs[i].returnType();
  681.             out.println(docs[i].modifiers()+" "+getLink(t.qualifiedTypeName(), t.typeName())+t.dimension()+" :hp2."+docs[i].name()+":ehp2."+getParamString(docs[i]));
  682.             String exc = getExceptionsString(docs[i]);
  683.             if (exc.length() > 0) {
  684.                 out.println(".br");
  685.                 out.println("          throws "+exc);
  686.             }
  687.             out.println(":lm margin=5.");
  688.             out.println(":p."+processCommentText(docs[i].commentText()));
  689.             Parameter[] params = docs[i].parameters();
  690.             int j;
  691.             if (params.length > 0) {
  692.                 out.println(":p.:hp2.Parameters&colon.:ehp2.");
  693.                 out.println(":lm margin=10.");
  694.                 ParamTag[] pTags = docs[i].paramTags();
  695.                 for (j=0;j<params.length;j++) {
  696.                     out.print(":p."+params[j].name()+" - ");
  697.                     for (int l=0;l<pTags.length;l++) {
  698.                         if (pTags[l].parameterName().equals(params[j].name())) {
  699.                             out.print(processCommentText(pTags[l].parameterComment()));
  700.                             break;
  701.                         }
  702.                     }
  703.                     out.println("");
  704.                 }
  705.                 out.println(":lm margin=1.");
  706.             }
  707. // Do the return type
  708.             Tag[] tags = docs[i].tags();
  709.             for (j=0;j<tags.length;j++) {
  710.                 if (tags[j].name().equalsIgnoreCase("@return")) {
  711.                     out.println(":lm margin=5.");
  712.                     out.println(":p.:hp2.Returns&colon.:ehp2.");
  713.                     out.println(":lm margin=10.:p."+processCommentText(tags[j].text()));
  714.                 }
  715.                 out.println(":lm margin=1.");
  716.             }
  717. // Exceptions
  718.             ThrowsTag[] throwsTags = docs[i].throwsTags();
  719.             if (throwsTags.length > 0) {
  720.                 out.println(":lm margin=5.");
  721.                 out.println(":p.:hp2.Throws&colon.:ehp2.");
  722.                 out.println(":lm margin=10.");
  723.                 for (j=0;j<throwsTags.length;j++) {
  724.                     out.println(":p."+throwsTags[j].exceptionName()+" - "+processCommentText(throwsTags[j].exceptionComment()));
  725.                 }
  726.                 out.println(":lm margin=1.");
  727.             }
  728. // See Tags
  729.             SeeTag[] seeTags = docs[i].seeTags();
  730.             if (seeTags.length > 0) {
  731.                 out.println(":lm margin=5.");
  732.                 out.println(":p.:hp2.See Also&colon.:ehp2.");
  733.                 out.print(":lm margin=10.:p.");
  734.                 for (j=0;j<seeTags.length;j++) {
  735.                     ClassDoc doc = seeTags[j].referencedClass();
  736.                     if (doc != null) {
  737.                         out.print(getLink(doc.qualifiedName(),doc.name()));
  738.                     }
  739.                     else {
  740.                         out.print(seeTags[j].referencedClassName());
  741.                     }
  742.                     if (j != seeTags.length-1) {
  743.                         out.println(", ");
  744.                     }
  745.                 }
  746.                 out.println(":lm margin=1.");
  747.             }
  748.             int prevRes = startRes - 2;
  749.             out.print(":p.");
  750.             if (prevRes >= 1) {
  751.                 out.print(":link reftype=hd res="+prevRes+".Previous:elink. ");
  752.             }
  753.             int nextRes = startRes;
  754.             out.print(":link reftype=hd res="+nextRes+".Next:elink. ");
  755.             out.println(getLink(docs[i].containingClass().qualifiedName(), "Return to Class "+docs[i].containingClass().name()));
  756.         }
  757.     }
  758.  
  759.  
  760. /**
  761.  * Write out a bunch of fields in individual windows.  This method is called
  762.  * by processClass to write out windows for each field for the class.  These are
  763.  * one heading level below the class pane.
  764.  * @param docs Array of field information
  765.  * @param startRes The window id to use for the first field.  Add one for second field, and so forth.
  766.  */
  767.     public static void processFields(FieldDoc[] docs, int startRes) {
  768.         out.println(":h3.Fields");
  769.  
  770.         out.println(":p.");
  771.         for (int i=0;i<docs.length;i++) {
  772.             out.println(":h4 x=right y=top width=80% height=100% res="+startRes+"."+docs[i].name());
  773.             startRes++;
  774.             out.println(":font facename='Tms Rmn' size=24x24.");
  775.             out.println(":hp4."+docs[i].name()+":ehp4.");
  776.             out.println(":font facename='default' size=0x0.");
  777.             out.println(":p.");
  778.             out.println(docs[i].modifiers()+" "+docs[i].type().typeName()+docs[i].type().dimension()+" :hp2."+docs[i].name()+":ehp2.");
  779.             out.println(":p."+processCommentText(docs[i].commentText()));
  780.  
  781.             int prevRes = startRes - 2;
  782.             out.print(":p.");
  783.             if (prevRes >= 1) {
  784.                 out.print(":link reftype=hd res="+prevRes+".Previous:elink. ");
  785.             }
  786.             int nextRes = startRes;
  787.             out.print(":link reftype=hd res="+nextRes+".Next:elink. ");
  788.             out.println(getLink(docs[i].containingClass().qualifiedName(), "Return to Class "+docs[i].containingClass().name()));
  789.         }
  790.     }
  791.  
  792. /**
  793.  * Outputs ipf tagging for superclass hierarchy for a class.  This is the little hierarchical
  794.  * chart shown in each class pane.  Wherever possible we use live links to each superclass.
  795.  * @param doc The class to print the chart for
  796.  */
  797.     public static void printClassTree(ClassDoc doc) {
  798.  
  799. // Build a list of all superclasses and then reverse it, since we want to show it in reverse order.
  800.         ArrayList superClasses = new ArrayList();
  801.         ClassDoc sClass = doc.superclass();
  802.         if (sClass == null) return;
  803.         while(sClass != null) {
  804.             superClasses.add(sClass);
  805.             sClass = sClass.superclass();
  806.         }
  807.         Collections.reverse(superClasses);
  808.  
  809. // Each level in indented 2 character positions more than the previous one
  810.         int indent=1;
  811.         for (int i=0;i<superClasses.size();i++) {
  812.             out.println(":lm margin="+indent+".");
  813.  
  814. // If not in the first indent level, we draw the little line that connects this level to the previous one.
  815.             if (indent > 1) {
  816.                 out.println("|");
  817.                 out.print("+--");
  818.             }
  819.             else {
  820.                 out.print(":p.:xmp.");
  821.             }
  822.             sClass = (ClassDoc) superClasses.get(i);
  823.             out.println(getLink(sClass.qualifiedName(),sClass.qualifiedName()));
  824.             indent += 2;
  825.         }
  826.         out.println(":lm margin=1.:exmp.");
  827.     }
  828.  
  829. /**
  830.  * Determines if the specified package is one being processed in this file.  If the package is not internal
  831.  * then references to it must be looked up in the mapping file.  We need to know this to decide how to
  832.  * link to a package:  with an internal or external link.
  833.  * @param pack The package name to check for
  834.  * @return true if the package is being documented in the ipf file being generated.
  835.  */
  836.     public static boolean isPackageInternal(String pack) {
  837.         for (int i=0;i<ourPackages.length;i++) {
  838.             if (ourPackages[i].equals(pack)) {
  839.                 return true;
  840.             }
  841.         }
  842.         return false;
  843.     }
  844.  
  845. /**
  846.  * Returns a string containing all the parameters for a method.  The parameters are separated by
  847.  * commas and surrounded by brackets.
  848.  * @param method The method to get the parameter list for
  849.  * @return String with the parameter list
  850.  */
  851.     public static String getParamString(ExecutableMemberDoc method) {
  852.         StringBuffer out = new StringBuffer("(");
  853.         Parameter[] p = method.parameters();
  854.         int i;
  855.         if (p.length > 0) {
  856.             for (i=0;i<p.length-1;i++) {
  857.                 out.append(p[i]+", ");
  858.             }
  859.             out.append(p[i]);
  860.         }
  861.         out.append(")");
  862.         return new String(out);
  863.     }
  864.  
  865. /**
  866.  * Returns a string with a comma-separate list of all the exceptions that may be thrown by a method.
  867.  * @param method The method to get the exception list for.
  868.  * @return The comma-separated list of exceptions.
  869.  */
  870.     public static String getExceptionsString(ExecutableMemberDoc method) {
  871.         StringBuffer out = new StringBuffer();
  872.         ClassDoc[] exc = method.thrownExceptions();
  873.         int i;
  874.         if (exc.length > 0) {
  875.             for (i=0;i<exc.length-1;i++) {
  876.                 out.append(exc[i].typeName()+", ");
  877.             }
  878.             out.append(exc[i].typeName());
  879.         }
  880.         return new String(out);
  881.     }
  882.  
  883.  
  884. /**
  885.  * This method is called to convert certain characters in a string to their symbolic representation in
  886.  * ipf.  For example, you cannot leave a colon or an ampersand in clear text, because it will be interpreted
  887.  * as the start of a tag or symbol.  So, they are replaced with their symbolic representation.
  888.  * Also, \n is replaced with \r\n, and the next character, which is always a space is removed.  This gets
  889.  * around a little difference in the way javadoc returns tag strings and the way IPFC wants to handle them.
  890.  * @param in The string to process
  891.  * @return Processed string.
  892.  */
  893.     public static String processSymbols(String in) {
  894.         StringBuffer out = new StringBuffer();
  895.         char c;
  896.         for (int i=0;i<in.length();i++) {
  897.             c = in.charAt(i);
  898.             if (c == ':') {
  899.                 out.append("&colon.");
  900.             }
  901.             else if (c == '&') {
  902.                 out.append("&.");
  903.             }
  904.             else if (c == '\n') {
  905.                 out.append("\r\n");
  906.                 // Remove the space that follows
  907.                 i++;
  908.             }
  909.             else {
  910.                 out.append(c);
  911.             }
  912.         }
  913.         return new String(out);
  914.     }
  915.  
  916. /**
  917.  * Process the comment or tag text to convert html tagging to ipf tagging.  Sun, in particular, uses a lot
  918.  * of html tags in its comments to format them nicely when converted to javadoc format.  Fortunately, many
  919.  * of these can easily be directly converted to ipf.  Tags which are not recognized are omitted.  This is not
  920.  * perfect, but usually what comes out is reasonably decent looking.  <code>processSymbols</code> is also
  921.  * called by this method to fix up any tricky symbols.
  922.  * @param in The text to process
  923.  * @return Cleaned up test.
  924.  */
  925.     public static String processCommentText(String in) {
  926.         in = processSymbols(in);
  927.         StringBuffer out = new StringBuffer();
  928.         StringTokenizer tok = new StringTokenizer(in, "<>", true    );
  929.         String token = null;
  930.         boolean inTag = false;
  931.         String flag = null;
  932.         while (tok.hasMoreTokens()) {
  933.             token = tok.nextToken();
  934.             if (token.equals("<")) {
  935.                 if (!inTag) {
  936.                     inTag = true;
  937.                     flag = null;
  938.                 }
  939.                 else {
  940.                     out.append(token);
  941.                 }
  942.             }
  943.             else if (token.equals(">")) {
  944.                 if (inTag) {
  945.                     inTag = false;
  946.                     if (flag != null) {
  947.                         if (flag.equalsIgnoreCase("b")) {
  948.                             out.append(":hp2.");
  949.                         }
  950.                         else if (flag.equalsIgnoreCase("/b")) {
  951.                             out.append(":ehp2.");
  952.                         }
  953.                         else if (flag.equalsIgnoreCase("p")) {
  954.                             out.append(":p.");
  955.                         }
  956.                         else if (flag.equalsIgnoreCase("i")) {
  957.                             out.append(":hp1.");
  958.                         }
  959.                         else if (flag.equalsIgnoreCase("/i")) {
  960.                             out.append(":ehp1.");
  961.                         }
  962.                         else if (flag.equalsIgnoreCase("code")) {
  963.                             out.append(":font facename='System Monospaced' size=12x12.");
  964.                         }
  965.                         else if (flag.equalsIgnoreCase("/code")) {
  966.                             out.append(":font facename='default'.");
  967.                         }
  968.                         else if (flag.equalsIgnoreCase("ul")) {
  969.                             out.append(":ul.");
  970.                         }
  971.                         else if (flag.equalsIgnoreCase("/ul")) {
  972.                             out.append(":eul.");
  973.                         }
  974.                         else if (flag.equalsIgnoreCase("ol")) {
  975.                             out.append(":ol.");
  976.                         }
  977.                         else if (flag.equalsIgnoreCase("/ol")) {
  978.                             out.append(":eol.");
  979.                         }
  980.                         else if (flag.equalsIgnoreCase("li")) {
  981.                             out.append(":li.");
  982.                         }
  983.                         else if (flag.equalsIgnoreCase("dl")) {
  984.                             out.append(":parml break=fit tsize=15 compact.");
  985.                         }
  986.                         else if (flag.equalsIgnoreCase("/dl")) {
  987.                             out.append(":eparml.");
  988.                         }
  989.                         else if (flag.equalsIgnoreCase("dt")) {
  990.                             out.append(":pt.");
  991.                         }
  992.                         else if (flag.equalsIgnoreCase("dd")) {
  993.                             out.append(":pd.");
  994.                         }
  995.                         else if (flag.equalsIgnoreCase("hr")) {
  996.                             out.append(":cgraphic.────────────────────────────────────────────────────────────────────────────────:ecgraphic.");
  997.                         }
  998.                         else if (flag.equalsIgnoreCase("pre")) {
  999.                             out.append(":fig.");
  1000.                         }
  1001.                         else if (flag.equalsIgnoreCase("/pre")) {
  1002.                             out.append(":efig.");
  1003.                         }
  1004.                         flag = null;
  1005.                     }
  1006.                 }
  1007.                 else {
  1008.                     out.append(token);
  1009.                 }
  1010.             }
  1011.             else {
  1012.                 if (inTag) {
  1013.                     flag = token;
  1014.                 }
  1015.                 else {
  1016.                     out.append(token);
  1017.                 }
  1018.             }
  1019.  
  1020.         }
  1021.         return (new String(out));
  1022.     }
  1023.  
  1024. /**
  1025.  * Returns how many option words specify a given tag.  This is part of the command-line option
  1026.  * processing for javadoc.  Any unregonized tags are passed in here, and it returns how
  1027.  * many symbols that tag should be.  For example, for -f which takes a filename as an argument,
  1028.  * it would return 2 (1 for the tag itself, and one for the filename).
  1029.  * @param option The option to get length for
  1030.  * @return The length for the option.
  1031.  */
  1032.     public static int optionLength(String option) {
  1033.         if (option.equals("-f")) {
  1034.             return 2;
  1035.         }
  1036.         else if (option.equals("-title")) {
  1037.             return 2;
  1038.         }
  1039.         else if (option.equals("-map")) {
  1040.             return 2;
  1041.         }
  1042.         else if (option.equals("-graphics")) {
  1043.             return 1;
  1044.         }
  1045.         return 0;
  1046.     }
  1047.  
  1048. /**
  1049.  * Processes our extra command line options.  There are several extra command line options supported
  1050.  * by this doclet, and this scans through the options presented, and handles them appropriately.
  1051.  * No error checking is performed at this time.
  1052.  * @param options Array of options
  1053.  */
  1054.     public static void readOptions(String[][] options) {
  1055.         for (int i=0;i<options.length;i++) {
  1056.             String[] opt = options[i];
  1057.             if (opt[0].equals("-f")) {
  1058.                 outputFileName = opt[1];
  1059.             }
  1060.             else if (opt[0].equals("-title")) {
  1061.                 title=opt[1];
  1062.             }
  1063.             else if (opt[0].equals("-map")) {
  1064.                 mappingFileName=opt[1];
  1065.             }
  1066.             else if (opt[0].equals("-graphics")) {
  1067.                 graphics = true;
  1068.             }
  1069.         }
  1070.     }
  1071.  
  1072.  
  1073. }
  1074.