steveclaflin.com

FixJavadocHtml.java


FixJavadocHtml.java – repairs the class documentation files in JDK 1.5

The javadoc produced by jdk 1.5 produces malformed HTML. Among other problems, there is a mismatched <DL> tag that may cause keyFinder's JavaScript to throw an "Access is denied" error in some Internet Explorer 6 setups. This program traverses the doc directories to fix that specific problem in every class documentation file (it doesn't fix anything else). If keyFinder works for you already, then i wouldn't bother running this.

The following code can be compiled in jdk 1.4 or 1.5. Compile it to a package structure rooted in your jdk's docs/api folder. When it runs, it lists only files that it was not successful in changing. There may be one or two that aren't class pages; that is OK. If you see any failures that are java classes, then that file may not work in keyFinder. (I'm providing source code rather than a compiled file so you can convince yourself that it doesn't do anything malicious – I'm assuming that, as a Java programmer, you will know how to compile and run it.)

It took about 4 minutes on my system.


package com.steveclaflin.javadoc.tools;

import java.io.*;
import java.util.regex.*;
import java.util.*;

/**
  * Fixes malformed HTML in Javadoc files that prevents keyFinder from
  * working properly in Windows/IE5/6 with SP2. Corrects &lt;DL&gt;, 
  * &lt;DT&gt; and &lt;PRE&gt; tags around the class declaration section.
  * Should be run from the docs/api directory of your jdk home.
  */
public class FixJavadocHtml {
  
  /**
  * Variables for keeping statistics.
  */
  private static int successCount = 0, skipCount = 0, failCount = 0, maxFail = 6;
  private static FileFilter isDocFileFilter = new ClassDocFileFilter();
  private static Vector vSkip = new Vector();
  private static Vector vFail = new Vector();
  
  /**
   * Runs the FixJavadocHtml program to process the package roots java, javax
   * and org.
   */
  public static void main(String[] args) {
    System.out.println("FixJavadocHtml " + new File(".").getAbsolutePath());
    File[] dirs = new File[] { 
      new File("java"), new File("javax"), new File("org")
    };
    for (int i = 0; i < dirs.length; i++) {
      fix(dirs[i]);
    }
    report();
  }
  
  /**
  * Processes a directory by either fixing entries that are files or 
  * recursively processing entries that are directories, except directories
  * named <i>class-use</i>.
  * Replaces &lt;HR&gt;\n&lt;DL&gt;\n&lt;DT&gt;&lt;PRE&gt; with
  * &lt;HR&gt;\n&lt;PRE&gt;\n&lt;DL&gt;\n&lt;DT&gt;
  *
  * @param f the directory to process.
  */
  public static void fix(File f) {
    Pattern searchPattern = Pattern.compile("<DL>\r?\n<DT><PRE>", 
                                            Pattern.MULTILINE);
    String replaceString = "<PRE>\n<DL>\n<DT>";
    if (f.isDirectory()) {
      File [] dirs = f.listFiles(isDocFileFilter);
      for (int i = 0; i < dirs.length; i++) {
        if (!f.getName().equals("class-use")) fix(dirs[i]);
      }
    } 
    else if (f.isFile() && f.canRead() && f.canWrite()) {
      String name = "";
      try {
        name = f.getAbsolutePath();
        FileInputStream fis = new FileInputStream(f);
        int size = fis.available()/2;
        InputStreamReader isr = new InputStreamReader(fis);
        StringWriter sw = new StringWriter(size);
        int c = 0;
        while ((c = isr.read()) >= 0) {
          sw.write(c);
        }
        isr.close();
        fis.close();
        String s = sw.toString();
        Matcher m = searchPattern.matcher(s);
        if (m.find()) {
          s = m.replaceFirst(replaceString);
          File temp = new File(name + "X");
          f.renameTo(temp);
          FileOutputStream fos = new FileOutputStream(name);
          OutputStreamWriter osw = new OutputStreamWriter(fos);
          osw.write(s);
          osw.close();
          fos.close();
          temp.delete();
          successCount++;
        }
        else {
          vSkip.add(name);
          skipCount++;
        }
      } catch (IOException e) {
        System.out.println(": FAILED: " + e.getMessage());
        e.printStackTrace();
        vFail.add(name + e.getMessage());
        failCount++;
        if (failCount > maxFail) {
          report();
          System.exit(-1);
        }
      }
    }
  }
  
  /**
   * Prints a count of files successfully changed, and counts and lists of files skipped and failures.
   */
  public static void report() {
    System.out.println(successCount + " files changed");
    System.out.println();
    System.out.println(skipCount + " skipped");
    Iterator i = vSkip.iterator();
    while (i.hasNext()) {
      System.out.println(" " + i.next());
    }
    System.out.println();
    System.out.println(failCount + " failed");
    i = vFail.iterator();
    while (i.hasNext()) {
      System.out.println(" " + i.next());
    } 
  }
  
  /**
  * Utility class to allow only files that begin with an uppercase letter and
  * end with .html to be processed.
  */
  private static class ClassDocFileFilter implements FileFilter {
  
  /**
  * Accepts only files that begin with an uppercase letter and
  * end with .html.
  *
  * @return true if the pathname is accepted
  */
  public boolean accept(File pathname) {
    if (pathname.isDirectory()) return true;
    String name = pathname.getName();
    return Character.isUpperCase(name.charAt(0)) && name.endsWith(".html");
    }
  
  }
  
}

Return to home page.