/* * $Id$ * * Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jct.utils.indexgen; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.FileScanner; import org.apache.tools.ant.taskdefs.MatchingTask; /** *

A utility to generate an index from a set of HTML files and directories. * Index entries are indicated inline by HTML of the form *

 *    <a name=XXX><!-- index: abc; pqr --></a>
 * 
* This will create entries in the index under "abc" and "pqr" that link back to "XXX". * The entries can have sub-entries, separated by ":", as in: *
 *    <a name=XXX><!-- index: abc:def; pqr:stu --></a>
 * This will create entries in the index under "abc"/"def" and "pqr"/"stu"
 * that link back to "XXX". The format is the same as for FrameMaker index marker
 * entries.
 *
 * 

The output can be in one or both of two forms. * *

The index can be output as a JavaHelp-compatible index.xml file, * with an associated map file. The map file can be merged with any other * maps with the "mapmerge" utility. * *

Or, the index can be output as a single index.html file, containing the * sorted set of index entries and their references. */ public class Main { /** * An exception to report bad command line arguments. */ public static class BadArgs extends Exception { BadArgs(String msg) { super(msg); } } /** * Command line entry point.
* @param args Command line arguments, per the usage as described. */ public static void main(String[] args) { try { if (args.length == 0) usage(System.err); else { Main m = new Main(args); m.run(); } } catch (BadArgs e) { System.err.println(e); usage(System.err); System.exit(1); } catch (Throwable t) { t.printStackTrace(); System.exit(2); } } /** * Write out short command line help. * @param out A stream to which to write the help. */ private static void usage(PrintStream out) { String program = System.getProperty("program", "java " + Main.class.getName()); out.println("Usage:"); out.println(" " + program + " options files..."); out.println(""); out.println("Arguments:"); out.println("-mapOut map.xml"); out.println(" Specify the location of the map.xml file to be written."); out.println("-htmlOut index.html"); out.println(" Specify the location of the index.html file."); out.println("-xmlOut index.xml"); out.println(" Specify the location of the index.xml file."); out.println("-srcpath dir;dir;..."); out.println(" Specify the a path in which to look for source files."); out.println("files..."); out.println(" HTML files and directories."); } public Main() { } /** * Create an object based on command line args. * It is an error if no input files or no output file is given. * @param args Command line args. * @see #main * @throws Main.BadArgs if problems are found in the given arguments. */ public Main(String[] args) throws BadArgs { for (int i = 0; i < args.length; i++) { if (args[i].equalsIgnoreCase("-htmlout") && i + 1 < args.length) { htmlOutFile = new File(args[++i]); } else if (args[i].equalsIgnoreCase("-xmlout") && i + 1 < args.length) { xmlOutFile = new File(args[++i]); } else if (args[i].equalsIgnoreCase("-mapout") && i + 1 < args.length) { mapOutFile = new File(args[++i]); } else if (args[i].equalsIgnoreCase("-mapdir") && i + 1 < args.length) { mapDir = new File(args[++i]); } else if (args[i].equalsIgnoreCase("-srcPath") && i + 1 < args.length) { path = splitPath(args[++i]); } else { inFiles = new File[args.length - i]; for (int j = 0; j < inFiles.length; j++) inFiles[j] = new File(args[i++]); } } } public static class Ant extends MatchingTask { private Main m = new Main(); public void setHtmlOutFile(File file) { m.htmlOutFile = file; } public void setXmlOutFile(File file) { m.xmlOutFile = file; } public void setMapOutFile(File file) { m.mapOutFile = file; } public void setMapDir(File file) { m.mapDir = file; } public void setDir(File dir) { getImplicitFileSet().setDir(dir); } public void execute() { FileScanner s = getImplicitFileSet().getDirectoryScanner(getProject()); m.path = new File[] { s.getBasedir() }; m.addFiles(s.getIncludedFiles()); try { m.run(); } catch (BadArgs e) { throw new BuildException(e.getMessage()); } catch (IOException e) { throw new BuildException(e); } } } public void addFiles(String[] paths) { if (paths == null) return; List files = new ArrayList(); if (inFiles != null) files.addAll(Arrays.asList(inFiles)); for (int i = 0; i < paths.length; i++) files.add(new File(paths[i])); inFiles = files.toArray(new File[files.size()]); } private void run() throws BadArgs, IOException { if (inFiles == null || inFiles.length == 0) throw new BadArgs("no input files specified"); if (htmlOutFile == null && mapOutFile == null && xmlOutFile == null) throw new BadArgs("no output files specified"); if (xmlOutFile != null && mapOutFile == null ) throw new BadArgs("no map output file specified"); if (mapOutFile != null && xmlOutFile == null) throw new BadArgs("no XML output file specified"); root = new Node(); read(inFiles); writeIndex(); } private void read(File[] files) throws IOException { for (int i = 0; i < files.length; i++) read(files[i]); } private void read(File file) throws IOException { if (path == null) read(file, file); else { for (int i = 0; i < path.length; i++) { File f = new File(path[i], file.getPath()); if (f.exists()) { read(f, file); return; } } throw new FileNotFoundException(file.getPath()); } } private void read(File absFile, File relFile) throws IOException { if (absFile.isDirectory()) { if (!absFile.getName().equals("SCCS")) { String[] files = absFile.list(); for (int i = 0; i < files.length; i++) { read(new File(absFile, files[i]), (relFile.getPath().equals(".") ? new File(files[i]) : new File(relFile, files[i]))); } } return; } if (!absFile.getName().endsWith(".html")) return; // ordinary file -- scan it looking for index entries in = new BufferedReader(new FileReader(absFile)); currFile = relFile; currName = null; line = 1; nextCh(); while (c >= 0) { if (c == '<') { nextCh(); skipSpace(); switch (c) { case '!': nextCh(); if (c == '-') { nextCh(); if (c == '-') { nextCh(); scanComment(); } } break; case '/': nextCh(); String endTag = scanIdentifier(); if (isLink(endTag)) { currName = ""; skipTag(); } else skipTag(); break; default: String startTag = scanIdentifier(); if (isLink(startTag)) scanLink(); else skipTag(); } } else nextCh(); } } private boolean isLink(String tag) { return tag.equals("a"); } /** * Process the contents of */ private void scanLink() throws IOException { skipSpace(); while (c != '>') { String att = scanIdentifier(); String value = scanValue(); if (att.equalsIgnoreCase("name")) currName = value; skipSpace(); } nextCh(); } /** * Read an identifier, and lowercase it */ private String scanIdentifier() throws IOException { StringBuffer buf = new StringBuffer(); while (true) { if ((c >= 'a') && (c <= 'z')) { buf.append((char)c); nextCh(); } else if ((c >= 'A') && (c <= 'Z')) { buf.append((char)('a' + (c - 'A'))); nextCh(); } else if ((c >= '0') && (c <= '9')) { buf.append((char)c); nextCh(); } else if (c == '-') { // needed for buf.append((char)c); nextCh(); } else if (buf.length() == 0) throw new IOException("Identifier expected (" + currFile + ":" + line + ")"); else return buf.toString(); } } /** * Read the value of an HTML attribute, which may be quoted. */ private String scanValue() throws IOException { skipSpace(); if (c != '=') return ""; int quote = -1; nextCh(); skipSpace(); if ((c == '\'') || (c == '\"')) { quote = c; nextCh(); skipSpace(); } StringBuffer buf = new StringBuffer(); while (((quote < 0) && (c != ' ') && (c != '\t') && (c != '\n') && (c != '\r') && (c != '>')) || ((quote >= 0) && (c != quote))) { if (c == -1 || c == '\n' || c == '\r') { throw new IOException("mismatched quotes (" + currFile + ":" + line + ")"); } buf.append((char)c); nextCh(); } if (c == quote) nextCh(); skipSpace(); return buf.toString(); } /** * Scan an HTML comment */ private void scanComment() throws IOException { // a comment sequence is "" // at the time this is called, "