genhtml.beh.html

Program{
	(@
  // various HTML tags
  final static String HTML = "<HTML>";
  final static String _HTML = "</HTML>";
  final static String HEAD = "<HEAD>";
  final static String _HEAD = "</HEAD>";
  final static String TITLE = "<TITLE>";
  final static String _TITLE = "</TITLE>";
  final static String PRE = "<PRE>";
  final static String _PRE = "</PRE>";
  // Background White, Visited Links HotPink, Links Blue
  final static String BODY = "<BODY LINK=\"#0000FF\" VLINK=\"#FF00FF\" BGCOLOR=\"#FFFFFF\">";
  final static String _BODY = "</BODY>";
  final static String BASE = "<BASE TARGET=\"frame0\">";

  // html files and directories
  public final static String htmlext = ".html"; // html file extension
  static File htmldir = new File("html/"); // dir for html files
  static File htmlgendir; // dir for java html files
  static File gen; // gendir formatted for our purposes
  private static File indexfile = new File("index" + htmlext);
  private static File referfile = new File("referfile" + htmlext);
  private static Vector file_exts = new Vector(4); // holds filename extensions
  private static Vector files = new Vector(4); // 2D Vector of filenames
  private static Vector inputfiles = new Vector(); // *.input files
  private static Vector origbehfiles = new Vector(); // *.beh files
  private static Program cdprog;

  // tables
  private static Hashtable htmlclassnames = new Hashtable();  
  // visitor to create cd class table
  private static HtmlClassNameVisitor cdvis = new HtmlClassNameVisitor();
  private static HtmlClassNameVisitor behvis = new HtmlClassNameVisitor();

  // html frames
  public final static int cdframeoffset = 1; // frame number for cd files
  // flexibility option to allow frame format to be specified on command line
  // for 2nd column such as *,2*,2*,* or 15%,35%,35%,15%
  private static String frame_fmt; 

  // methods
  /** Get the reference filename */
  public static File getReferFile() { return referfile; }
  /** Get the 2D array of filenames */
  public static Vector getFiles() { return files; }
  /** Get the filename extensions */
  public static Vector getFileExts() { return file_exts; }
  /** Get the list of java files */
  public static Vector getJavaFiles() { return javaFiles; }
  /** Get the table of cd class names */
  public static Hashtable getHtmlCdClassNames() { return cdvis.getTable(); }
  /** Get the table of beh class names */
  public static Hashtable getHtmlBehClassNames() { return behvis.getTable(); }
  /** Get the table of class names */
  public static Hashtable getHtmlClassNames() { return htmlclassnames; }
  public static void putHtmlClassNames(Object k, Object e) { htmlclassnames.put(k, e); }
  /** return the index for a file extension */
  public static int findExtIndex(String s) { return file_exts.indexOf(s); }

  /** Generate the HTML files */
  private static void generateHtml() throws ParseError, IOException {
    if(html) {
      // HTML generation is desired
      InputStream in;
log.println("javaFiles = "+javaFiles.toString());
      // format and ensure html directory
      String dir = htmldir.toString();
      if(!dir.endsWith("/"))
	dir += "/";
      htmldir = new File(dir);
      ensureDirectory(htmldir);
      // format and ensure html java directory
      dir = gendir.toString();
      if(dir.startsWith("./"))
	dir = dir.substring(2);
      if(!dir.endsWith("/"))
	dir += "/";
      gen = new File(dir);
      htmlgendir = new File(htmldir.toString() + dir);
      ensureDirectory(htmlgendir);

      // get filename lists
      // NOTE: not handling multiple cd files due to beh being parsed
      // and written first and assuming already know which cdfile to
      // cross-reference
      // get cdfile list
      addFiles(".cd", cdfile);
      // get behfile list
      for (Enumeration e = origbehfiles.elements(); e.hasMoreElements();) {
	File efile = (File)e.nextElement();
	addFiles(efile.toString().substring(efile.toString().lastIndexOf(".")), efile);
      }
      // add java file extension
      file_exts.addElement(".java");
      files.addElement(new Vector());
      // get inputfile list
      for (Enumeration e = inputfiles.elements(); e.hasMoreElements();) {
	File efile = (File)e.nextElement();
	addFiles(efile.toString().substring(efile.toString().lastIndexOf(".")), efile);
      }

      // parse cd file
      in = makeInputStream(cdfile);
      cdprog = parse(in);
      cdprog.toAll(cdvis); // create hashtable of classes in cd
      // parse beh files
      int index;
      if((index = file_exts.indexOf(".beh")) >= 0) {
	for (Enumeration e = ((Vector)(files.elementAt(index))).elements(); e.hasMoreElements();) {
	  File behfile = (File)e.nextElement();
	  in = makeInputStream(behfile);
	  ProgramBehavior beh = ProgramBehavior.parse(in);
	  beh.toAll(behvis);
	  writeHtmlBehaviorFile(behfile, beh);
	}
      }
      // now java files know which cd and beh files to reference
      // generate all the java html files and get list of java files
      prog.generateCode(false, true);
      // now know which beh files to reference.
      writeHtmlClassDictionary();
      // add any java-only classes to hash table
      prog.addHtmlJavaClasses(); 
      // generate frame index and reference file
      writeHtmlFrameIndex();
      writeHtmlReferFile();
      // generate input file
      writeHtmlInputFile();
    }
  }

  /** Generate HTML behavior file */
  private static void writeHtmlBehaviorFile(File behfile, ProgramBehavior pb)
    throws ParseError, IOException {
    File behfilehtml = new File(htmldir.toString(), 
				behfile.getName() + htmlext);
    log.println("Creating " + behfilehtml.toString() + "...");
    openOutputFile(behfilehtml);
    htmlHeader(out, behfilehtml.getName());
    out.println(BODY);
    out.println("<H2>" + behfilehtml.getName() + "</H2>");
    out.println(BASE);
    out.println(PRE);
    pb.toAll(new HtmlVisitor(out, behfile, htmlclassnames));
    out.println(_PRE);
    out.println(_BODY);
    htmlFooter(out);
  }

  /** Generate HTML Class Dictionary */
  private static void writeHtmlClassDictionary() throws IOException {
    int index; // file extension index
    // Check if class dictionary exists
    if((index = file_exts.indexOf(".cd")) >= 0) {
      int i = 0;
      // future: allowing for multiple cd files
      for (Enumeration e = ((Vector)(files.elementAt(index))).elements(); e.hasMoreElements(); i++) {
	File cdfile = (File)e.nextElement();
	File cdfilehtml = new File(htmldir, cdfile.getName() + htmlext);
	log.println("Creating " + cdfilehtml.toString() + "...");
	openOutputFile(cdfilehtml);
	htmlHeader(out, cdfilehtml.getName());
	out.println(BODY);
	out.println("<H2>" + cdfilehtml.getName() + "</H2>");
	out.println(BASE);
	out.println(PRE);
	cdprog.toAll(new HtmlVisitor(out, cdfile, htmlclassnames));
	out.println(_PRE);
	out.println(_BODY);
	htmlFooter(out);
      }
    }
    else {
      System.err.println("No cd file specified!");
    }
  }
  /** Write an index.html file that sets up the frames */
  private static void writeHtmlFrameIndex()
    throws IOException {
    int i;
    File f = new File(htmldir.toString(), indexfile.toString());
    log.println("Creating " + f.toString() + "...");
    openOutputFile(f);
    htmlHeader(out, "Demeter/Java Hypertext Documentation Tool");
    // we want the left column frame to contain the reference file
    out.println("<FRAMESET COLS=\"*,5*\">");
    out.println("  <FRAME SRC=\"" + referfile + "\" NAME=frame0>");
    if(!file_exts.isEmpty()) {
      String rows = "  <FRAMESET ROWS=\"";
      if(frame_fmt != null)
	rows += frame_fmt; // specified on command line
      else {
	i = 0;
	// 2nd column frame gets a row for every file type
	for (Enumeration e = file_exts.elements(); e.hasMoreElements();) {
	  String curext = (String)e.nextElement();
	  if(i++ != 0) rows += ","; // prepend comma only after first
	  // give procedural files more space
	  if (curext.equals(".cd") || curext.equals(".input")) {
	    rows += "*"; // less space (class dict and input files)
	  }
	  else {
	    rows += "2*"; // more space
	  }
	}
      }
      out.println(rows + "\">");
      i = cdframeoffset;
      // initially point to the first file in each list
      for (Enumeration e = files.elements(); e.hasMoreElements();) {
	Vector curext = (Vector)e.nextElement();
	out.println("    <FRAME SRC=\"" + curext.firstElement()
+ htmlext + "\" NAME=frame" + i++ + ">");
      }
      out.println("  </FRAMESET>");
    }
    out.println("</FRAMESET>");
    out.println("<NOFRAMES>");
    out.println("You must use a browser that can display frames " + 
		"to see this page.");
    out.println("</NOFRAMES>");
    htmlFooter(out);
  }

  /** Generate a reference file that contains a list of files
      relevant to the program and a class list that
      lists all references for a particular class
  */
  private static void writeHtmlReferFile()
    throws IOException {
    int i, j;
    File f = new File(htmldir.toString(),referfile.toString());
    log.println("Creating " + f.toString() + "...");
    openOutputFile(f);
    htmlHeader(out, "Reference File");
    out.println(BODY);
    out.println("<NOBR><H2>Reference File</H2></NOBR>");
    // create list of files
    out.println("<NOBR><H3>File List</H3></NOBR>");
    i = cdframeoffset;
    for (Enumeration e = files.elements(); e.hasMoreElements();) {
      Vector ext = (Vector)e.nextElement();
      for (Enumeration el = ext.elements(); el.hasMoreElements();) {
	File ff = (File)el.nextElement();
	out.println("<A HREF=\"" + ff
+ htmlext + "\" TARGET=\"frame" + i + "\">" + ff + "</A><BR>");
      }
      ++i;
    }
    // write list of class references
    out.println("<NOBR><H3>Class List</H3></NOBR>");
    out.println("<NOBR>c prefix = .cd file reference</NOBR><BR>");
    out.println("<NOBR>b prefix = .beh file reference</NOBR><BR>");
    out.println("<NOBR>j prefix = .java file reference</NOBR><BR><BR>");
    Enumeration e, k, ev;
    String file = "";
    int cindex = Program.findExtIndex(".cd");
    int bindex = Program.findExtIndex(".beh");
    int jindex = Program.findExtIndex(".java");
    // sort the classes
    Collator col = Collator.getInstance();
    CollationKey[] keys = new CollationKey[htmlclassnames.size()];
    for (k = htmlclassnames.keys(), i = 0; k.hasMoreElements(); i++) {
      keys[i] = col.getCollationKey((String)k.nextElement());
    }
    sort(keys);
    // iterate through all classnames
    for (j = 0; j < keys.length; j++) {
      String key = keys[j].getSourceString(); // classname
      HtmlClassIndex hci = (HtmlClassIndex)htmlclassnames.get(key);
      // place a name tag for other files to reference & classname
      out.print("<NOBR><A NAME=\"" + key + "\"><B>" + key + "</B> "); // class name tag
      // list cd file references
      int frame = cindex + cdframeoffset;
      for (ev = hci.getCd().elements(), i = 0; ev.hasMoreElements(); i++) {
	file = (String)ev.nextElement();
	out.print("c<A HREF=\"" + file + htmlext + "#" + key + i +
		  "\" TARGET=\"frame" + frame + "\">" + i + "</A>");
      }
      // list beh file references
      frame = bindex + cdframeoffset;
      for (ev = hci.getBeh().elements(), i = 0; ev.hasMoreElements(); i++) {
	file = (String)ev.nextElement();
	out.print("b<A HREF=\"" + file + htmlext + "#" + key + i +
		  "\" TARGET=\"frame" + frame + "\">" + i + "</A>");
      }
      // list java file references
      File javfile = new File(gen + key + ".java");
      if(((Vector)(files.elementAt(jindex))).contains(javfile)) {
	// this file was generated in HTML
	frame = jindex + cdframeoffset;
	out.print("j<A HREF=\"" + javfile.toString() + htmlext + "#" + key + 
		  0 + "\" TARGET=\"frame" + frame + "\">" + 0 + "</A>");
      }
      out.println("</NOBR><BR>");
    }
    out.println(_BODY);
    htmlFooter(out);
  }

  /** Write out the input files in HTML */
  private static void writeHtmlInputFile() throws IOException {
    int index;

    if((index = file_exts.indexOf(".input")) >= 0) {
      int i = 0;
      for (Enumeration e = ((Vector)(files.elementAt(index))).elements(); e.hasMoreElements(); i++) {
	File inputfile = (File)e.nextElement();
	File inputfilehtml = new File(htmldir, inputfile.getName() + htmlext);
	log.println("Creating " + inputfilehtml.toString() + "...");
	openOutputFile(inputfilehtml);
	htmlHeader(out, inputfilehtml.getName());
	out.println(BODY);
	out.println("<H2>" + inputfilehtml.getName() + "</H2>");
	// for now, just copy the text.
	out.println(PRE);
	InputStream in = makeInputStream(inputfile);
	copy(in, out);
	out.println(_PRE);
	out.println(_BODY);
	htmlFooter(out);
      }
    }
  }

  /** store filename by extension */
  public static void addFiles(String ext, File file) {
    int index;
    if((index = file_exts.indexOf(ext)) < 0) {
      // need to add filetype first
      file_exts.addElement(ext);
      files.addElement(new Vector());
      index = files.size() - 1;
    }
    // make sure path starts as expected (no ./)
    if(file.toString().startsWith("./"))
      file = new File(file.toString().substring(2));
    // Add pathname to end of appropriate list
    ((Vector)(files.elementAt(index))).addElement(file);
  }

  /** Make sure the directory exists and is writable. */
  private static void ensureDirectory(File dir) throws IOException {
    if (!dir.exists()) {
      log.println("Directory " + dir.toString() +
		  " does not exist, will create.");
      if (!dir.mkdirs()) {
	System.err.println("Error: could not create output directory.");
	throw new IOException();
      }
    }
    if (dir.exists()) {
      if (!dir.isDirectory()) {
	System.err.println("Error: " + dir +
			   " exists but is not a directory.");
      } else if (!dir.canWrite()) {
	System.err.println("Error: " + dir +
			   " exists but is not writable.");
      } else return;
      throw new IOException();
    }
  }

  /** Every HTML file has this header */
  public static void htmlHeader(PrintWriter out, String title) {
    out.println(HTML);
    out.println(HEAD);
    out.println(TITLE + title + _TITLE);
    out.println("<!-- Automatically generated by Demeter/Java " +
		"Hypertext Documentation Tool -->");
    out.println(_HEAD);
  }

  /** Every HTML file has this footer */
  public static void htmlFooter(PrintWriter out) {
    out.println(_HTML);
    closeOutputFile();
  }

  /** copy from in to out */
  private static void copy(InputStream in, PrintWriter out) 
    throws IOException {
    int ch;
    while((ch  = in.read()) >= 0)
      out.write(ch);
  }

  /** bubble sort the keys */
  private static void sort(CollationKey[] keys) {
    // for each location in the array
    for(int i = 0; i < keys.length - 1; i++) {

      for(int j = 0; j < keys.length - i - 1; j++) {
	// for the rest of the array compare the keys
	if( keys[j].compareTo( keys[j+1] ) > 0 ) {
	  // swap keys[i] and keys[j]
	  CollationKey temp = keys[j + 1];
	  keys[j + 1] = keys[j];
	  keys[j] = temp;
	}
      }
    }
  }
  @)
	private void addHtmlJavaClasses()=allClassDefs{
		before ClassDef (@
      String c = host.get_classname().toString();
      if(!Program.getHtmlClassNames().containsKey(c))
        Program.putHtmlClassNames(c, new HtmlClassIndex());
    @)
	}
}

ProgramBehavior{
	traversal toAll(UniversalVisitor v) {
		bypassing{ Hashtable }to*;
	}
}

HtmlClassNameVisitor{
	(@ private Hashtable hash; @)
	init (@ hash = new Hashtable(); @)
	before-> ClassName, name, Name (@
    hash.put(source.get_name().toString(), source.get_name().toString());
   @)
	Hashtable getTable()(@
    return (hash);
  @)
}

HtmlClassIndex{
	(@  private Vector cd = new Vector();
      private Vector beh = new Vector();
      public void addCd(String s) { cd.addElement(s); }
      public void addBeh(String s) { beh.addElement(s); }
      public Vector getCd() { return cd; }
      public Vector getBeh() { return beh; }
  @)
}