/*
* This file is part of a syntax highlighting package
* Copyright (C) 1999-2002 Stephen Ostermiller
* http://ostermiller.org/contact.pl?regarding=Syntax+Highlighting
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* See COPYING.TXT for details.
*/
package com.Ostermiller.Syntax;
import java.io.*;
import com.Ostermiller.Syntax.Lexer.*;
import com.Ostermiller.bte.*;
import gnu.getopt.*;
import java.util.*;
import java.text.*;
import com.Ostermiller.util.*;
import java.lang.reflect.*;
import java.net.MalformedURLException;
/**
* ToHTML will take source and convert it into an html document with
* syntax highlighting. All the special characters in the html will be
* replaced by their escape sequences.
* <p>
* A typical use of this class might be:
* <pre>
* try {
* ToHTML toHTML = new ToHTML();
* toHTML.setInput(new FileReader("Source.java"));
* toHTML.setOutput(new FileWriter("Source.java.html"));
* toHTML.setMimeType("text/x-java");
* toHTML.setFileExt("java")
* toHTML.writeFullHTML();
* } catch (InvocationTargetException x){
* // can only happen if setLexerType(java.lang.String) method is used.
* } catch (CompileException x){
* // can only happen if setTemplate(java.lang.String) method is used.
* } catch (IOException x){
* System.err.println(x.getMessage());
* }
* </pre>
* <p>
* A ToHTML instance may be reused, however it is not thread safe and should only be
* used by one thread at a time without external synchronization.
*/
public class ToHTML {
private static ToHTML defaultToHTML = new ToHTML();
private PrintWriter out;
private Reader in;
private String fileExt;
private String mimeType;
private Lexer lexer;
private String lexerType;
private String styleSheet;
private HashSet ignoreStyles = new HashSet();
private HashMap translateStyles = new HashMap();
private String bteSuper;
private String title;
private String docName;
private HashMap lexers = new HashMap();
private static HashMap registeredMimeTypes = new HashMap();
private static HashMap registeredFileExtensions = new HashMap();
static {
register(
"com.Ostermiller.Syntax.Lexer.HTMLLexer1",
new String[] {
"text/html",
},
new String[] {
"htm",
"html",
}
);
register(
"com.Ostermiller.Syntax.Lexer.JavaLexer",
new String[] {
"text/x-java",
"text/java", // for backwards compatibility, but not correct
},
new String[] {
"jav",
"java",
}
);
register(
"com.Ostermiller.Syntax.Lexer.SQLLexer",
new String[] {
"text/x-sql",
"application/x-sql",
},
new String[] {
"sql",
}
);
register(
"com.Ostermiller.Syntax.Lexer.CLexer",
new String[] {
"text/x-c++hdr",
"text/x-csrc",
"text/x-chdr",
"text/x-csrc",
"text/c", // for backwards compatibility, but not correct
},
new String[] {
"c",
"h",
"cc",
"cpp",
"cxx",
"c++",
"hpp",
"hxx",
"hh",
}
);
register(
"com.Ostermiller.Syntax.Lexer.PropertiesLexer",
new String[] {
"text/x-properties",
},
new String[] {
"props",
"properties",
}
);
register(
"com.Ostermiller.Syntax.Lexer.LatexLexer",
new String[] {
"application/x-latex",
"text/x-latex",
"application/x-tex",
"text/x-tex",
},
new String[] {
"tex",
"sty",
"cls",
"dtx",
"ins",
"latex",
}
);
register(
"com.Ostermiller.Syntax.Lexer.PlainLexer",
new String[] {
"text/plain",
"text",
},
new String[] {
"txt",
"text",
}
);
}
/**
* Register a lexer to handle the given mime types and fileExtensions.
* <p>
* If a document has a type "text/plain" it will first match a registered
* mime type of "text/plain" and then a registered mime type of "text".
*
* @param lexer String representing the fully qualified java name of the lexer.
* @param mimeTypes array of mime types that the lexer can handle.
* @param fileExtensions array of fileExtensions the lexer can handle. (case insensitive)
*/
public static void register(String lexer, String[] mimeTypes, String[] fileExtensions){
for (int i=0; i<mimeTypes.length; i++){
registeredMimeTypes.put(mimeTypes[i], lexer);
}
for (int i=0; i<fileExtensions.length; i++){
registeredFileExtensions.put(fileExtensions[i].toLowerCase(), lexer);
}
}
/**
* Open a span if needed for the given style.
*
* @param description style description.
* @param out place to write output.
*/
private void openSpan(String description, PrintWriter out) throws IOException {
if (translateStyles.containsKey(description)){
description = (String)translateStyles.get(description);
}
if (!ignoreStyles.contains(description)){
out.print("<span class=" + description + ">");
}
}
/**
* Close a span if needed for the given style.
*
* @param description style description.
* @param out place to write output.
*/
private void closeSpan(String description, PrintWriter out) throws IOException {
if (translateStyles.containsKey(description)){
description = (String)translateStyles.get(description);
}
if (!ignoreStyles.contains(description)){
out.print("</span>");
}
}
/**
* Write a highlighted document html fragment.
*
* @param lexer Lexer from which to get input.
* @param out place to write output.
* @throws IOException if an I/O error occurs.
*/
private void writeHTMLFragment(Lexer lexer, PrintWriter out) throws IOException {
String currentDescription = null;
Token token;
out.println();
out.print("<pre>");
while ((token = lexer.getNextToken()) != null){
// optimization implemented here:
// ignored white space can be put in the same span as the stuff
// around it. This saves space because spans don't have to be
// opened and closed.
if ((token.isWhiteSpace() && ignoreStyles.contains("whitespace")) ||
(currentDescription != null && token.getDescription().equals(currentDescription))){
writeEscapedHTML(token.getContents(), out);
} else {
if (currentDescription != null) closeSpan(currentDescription, out);
currentDescription = token.getDescription();
openSpan(currentDescription, out);
writeEscapedHTML(token.getContents(), out);
}
}
if (currentDescription != null) closeSpan(currentDescription, out);
out.println("</pre>");
}
/**
* Write a HTML fragment to the output location that has been set for this class.
* <p>
* The type of syntax highlighting done will be determined first by the class name for the lexer,
* then by the mime type, then by the file extension.
* <p>
* To save space in the resulting output, it is often a good idea to ignore whitespace. If
* whitespace is ignored, it will be merged with surrounding tags.
*
* @throws IOException if an I/O error occurs.
* @throws InvocationTargetException if a lexer class is specified which can't be instantiated.
*/
public void writeHTMLFragment() throws IOException, InvocationTargetException {
Lexer lexer = this.lexer;
if (lexer == null) lexer = getLexerFromClass(lexerType);
if (lexer == null) lexer = getLexerFromMime();
if (lexer == null) lexer = getLexerFromExt();
writeHTMLFragment(lexer, out);
out.flush();
}
/**
* Format for date.
*/
private static SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM dd yyyy 'at' HH:mm");
/**
* Cache of a potentially large buffer
*/
private CircularCharBuffer circularBuffer= null;
/**
* Cache of BTE files.
*/
private com.Ostermiller.bte.Compiler compiler = null;
/**
* Write a full HTML document to the output location that has been set for this class.
* If a template URL is specified, and <a href="http://ostermiller.org/bte/">template libraries</a>
* are installed, templates will be used.
* <p>
* The type of syntax highlighting done will be determined first by the class name for the lexer,
* then by the mime type, then by the file extension.
* <p>
* To save space in the resulting output, it is often a good idea to ignore whitespace. If
* whitespace is ignored, it will be merged with surrounding tags.
* <p>
* If templates are used, the following sections are available to the template:<br>
* date<br>
* highlightedDocument<br>
* fileExtension<br>
* fileName (minus the extension)<br>
* styleSheet (if specified)<br>
* title (if specified)<br>
* The <a href="http://ostermiller.org/syntax/page.bte">default template</a> may be used as a guide.
*
* @throws IOException if an I/O error occurs.
* @throws InvocationTargetException if a lexer class is specified which can't be instantiated.
* @throws CompileException if a bte template is specified which can't be compiled.
*/
public void writeFullHTML() throws IOException, InvocationTargetException, CompileException {
Lexer lexer = this.lexer;
if (lexer == null) lexer = getLexerFromClass(lexerType);
if (lexer == null) lexer = getLexerFromMime();
if (lexer == null) lexer = getLexerFromExt();
boolean noBTE = false;
try {
if (bteSuper == null){
noBTE = true;
} else {
if (compiler == null){
compiler = new com.Ostermiller.bte.Compiler();
}
if (circularBuffer == null){
circularBuffer = new CircularCharBuffer(CircularCharBuffer.INFINITE_SIZE);
} else {
circularBuffer.clear();
}
PrintWriter bteOut = new PrintWriter(circularBuffer.getWriter());
bteOut.print("<%bte.doc super='" + bteSuper + "' %>\n");
bteOut.print("<%bte.tpl name=date %>" + dateFormat.format(new Date()) + "<%/bte.tpl%>\n");
if (title != null){
bteOut.print("<%bte.tpl name=title %>" + title + "<%/bte.tpl%>\n");
}
if (fileExt != null){
bteOut.print("<%bte.tpl name=fileExtension %>" + fileExt + "<%/bte.tpl%>\n");
}
if (docName != null){
bteOut.print("<%bte.tpl name=fileName %>" + docName + "<%/bte.tpl%>\n");
}
if (styleSheet != null){
bteOut.print("<%bte.tpl name=styleSheet %>" + styleSheet + "<%/bte.tpl%>\n");
}
bteOut.print("<%bte.tpl name=highlightedDocument %>");
writeHTMLFragment(lexer, bteOut);
bteOut.print("<%/bte.tpl%>\n");
bteOut.print("<%/bte.doc%>\n");
bteOut.close();
Reader bteIn = circularBuffer.getReader();
compiler.compile(bteIn, out);
}
} catch (NoClassDefFoundError x){
noBTE = true;
}
if (noBTE){
PrintWriter out = this.out;
out.println("<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>");
out.println("<html>");
out.println("<head>");
if (title != null){
out.println("<title>" + title + "</title>");
}
out.println("<meta http-equiv='content-type' content='text/html;charset=ISO-8859-1'>");
if (styleSheet != null){
out.println("<link rel='stylesheet' title='Syntax Highlighting' href='" + styleSheet + "' type='text/css'>");
}
out.println("</head>");
out.println("<body>");
writeHTMLFragment(lexer, out);
out.println("<div style='border: thin black ridge;padding:1cm;'>");
out.println("<!--");
out.println("To customize the appearance of the html that is emitted you must install");
out.println("the BTE template system from:");
out.println("http://ostermiller.org/bte/");
out.println("-->");
out.println("Syntax Highlighting created using the ");
out.println("<a href='http://ostermiller.org/syntax/'>com.Ostermiller.Syntax</a> package.<br>");
out.println(dateFormat.format(new Date()));
out.println("</div>");
out.println("</body>");
out.println("</html>");
}
out.flush();
}
/**
* Set the URL of the BTE template file which should used.
* For more information on BTE templates please
* see the <a href="http://ostermiller.org/bte/">BTE website</a>.
*
* @param bteSuper url of the bte template file.
*/
public void setTemplate(String bteSuper){
this.bteSuper = bteSuper;
}
/**
* Set the file extension to be used for the document.
* <p>
* The type of syntax highlighting to use will depend on
* the lexerType that is set, the mime type that is given,
* and the file extension that is given.
* If a lexer is explicitly given, it is used, otherwise
* if the mime type is recognized, an appropriate style
* for that mime type is used, otherwise if the file extension
* is recognized, an appropriate style for that extension is
* used, otherwise, no style is given.
*
* @param mimeType the mimeType of the document or null to clear.
*/
public void setFileExt(String fileExt){
this.fileExt = fileExt;
}
/**
* Set the name of this document (minus any file extension).
* <p>
* This name will be reported to html template.
*
* @param docName Name of the document.
*/
public void setDocName(String docName){
this.docName = docName;
}
/**
* Set the lexer to be used for the document.
* <p>
* The type of syntax highlighting to use will depend on
* the lexerType that is set, the mime type that is given,
* and the file extension that is given.
* If a lexer is explicitly given, it is used, otherwise
* if the mime type is recognized, an appropriate style
* for that mime type is used, otherwise if the file extension
* is recognized, an appropriate style for that extension is
* used, otherwise, no style is given.
*
* @param full java name of the lexer to use.
*/
public void setLexerType(String lexerType){
this.lexerType = lexerType;
}
/**
* Set the lexer to be used for the document.
*/
private void setLexer(Lexer lexer){
saveLexer(lexer);
this.lexer = lexer;
}
/**
* Set the mime type of the document.
* <p>
* The type of syntax highlighting to use will depend on
* the lexerType that is set, the mime type that is given,
* and the file extension that is given.
* If a lexer is explicitly given, it is used, otherwise
* if the mime type is recognized, an appropriate style
* for that mime type is used, otherwise if the file extension
* is recognized, an appropriate style for that extension is
* used, otherwise, no style is given.
*
* @param mimeType the mimeType of the document or null to clear.
*/
public void setMimeType(String mimeType){
this.mimeType = mimeType;
}
/**
* Get the mime type of the document.
*
* @return the mimeType of the document or null if none.
*/
public String getMimeType(){
return mimeType;
}
/**
* Set the output stream.
*
* @param out new output stream.
*/
public void setOutput(Writer out){
this.out = new PrintWriter(out);
}
/**
* Set the output stream.
*
* @param out new output stream.
*/
public void setOutput(PrintWriter out){
this.out = out;
}
/**
* Set the output to the given file.
*
* @param f file to which to write.
* @throws IOException if the file cannot be opened.
*/
public void setOutput(File f) throws IOException {
setOutput(new FileWriter(f));
}
/**
* Set the input to the given file.
*
* @param f file from which to read.
* @throws IOException if the file cannot be opened.
*/
public void setInput(File f) throws IOException {
setInput(new FileReader(f));
}
/**
* Set the input stream.
*
* @param in new input stream.
*/
public void setInput(Reader in){
this.in = in;
}
/**
* Link the html document to the style sheet at the
* given URL.
*
* @param link stylesheet URL to use, or null to none.
*/
public void setStyleSheet(String styleSheet){
this.styleSheet = styleSheet;
}
/**
* Use the given title for the html document.
*
* @param title document title, or null to use none.
*/
public void setTitle(String title){
this.title = title;
}
/**
* Get the URL of the linked style sheet.
*
* @return stylesheet URL to used, or null if none.
*/
public String getStyleSheet(){
return styleSheet;
}
/**
* Get the title used for the html document.
*
* @return document title, or null if none.
*/
public String getTitle(){
return title;
}
/**
* Adds the specified style to be ignored if it
* is not already present.
* Ignored styles will not have a span associated with
* them in the output. This can be used to make the
* resulting files smaller. It is suggested to
* ignore "whitespace" as it probably won't look any different
* with an associated style.
* <p>
* If styles have been translated, then the translated style
* should be the one to be ignored.
*
* @param style the name of the style to be ignored.
* @return true if the style was not already being ignored.
*/
public boolean addIgnoreStyle(String style){
return ignoreStyles.add(style);
}
/**
* No longer ignore the given style.
*
* @param style the name of the style to no longer be ignored.
* @return true if the style was being ignored.
*/
public boolean removeIgnoreStyle(String style){
return ignoreStyles.remove(style);
}
/**
* Rename a style. Useful for working with style sheets that
* expect certain names or for saving space by providing shorter
* names. The name of the style will only be changed in the html
* document. You must edit the CSS stylesheet or provide a custom
* CSS stylesheet with this option as the default stylesheet will
* use canonical style names.
*
* @param canonicalStyle the canonical (default) name of the style.
* @param style the new name of the style.
* @return previous translation of the canonicalStyle.
*/
public String translateStyle(String canonicalStyle, String style){
if (canonicalStyle == null || style == null) throw new NullPointerException();
String value = (String)translateStyles.put(canonicalStyle, style);
if (value == null) return canonicalStyle;
return value;
}
/**
* No longer ignore the given style.
*
* @param canonicalStyle the canonical (default) name of the style.
* @return previous translation of the canonicalStyle.
*/
public String removeTranslation(String canonicalStyle){
String value = (String)translateStyles.remove(canonicalStyle);
if (value == null) return canonicalStyle;
return value;
}
private static String version = "1.1.1";
/**
* Locale specific strings displayed to the user.
*/
protected static ResourceBundle labels = ResourceBundle.getBundle("com.Ostermiller.Syntax.ToHTML", Locale.getDefault());
/**
* Program to add syntax highlighting to source files.
* Execute <b>java com.Ostermiller.Syntax.ToHTML --help</b>
* for more details.
*/
public static void main(String[] args){
// create the command line options that we are looking for
LongOpt[] longopts = {
new LongOpt(labels.getString("help.option"), LongOpt.NO_ARGUMENT, null, 1),
new LongOpt(labels.getString("version.option"), LongOpt.NO_ARGUMENT, null, 2),
new LongOpt(labels.getString("about.option"), LongOpt.NO_ARGUMENT, null, 3),
new LongOpt(labels.getString("m.option"), LongOpt.REQUIRED_ARGUMENT, null, 'm'),
new LongOpt(labels.getString("l.option"), LongOpt.REQUIRED_ARGUMENT, null, 'l'),
new LongOpt(labels.getString("T.option"), LongOpt.REQUIRED_ARGUMENT, null, 'T'),
new LongOpt(labels.getString("i.option"), LongOpt.REQUIRED_ARGUMENT, null, 'i'),
new LongOpt(labels.getString("t.option"), LongOpt.REQUIRED_ARGUMENT, null, 't'),
new LongOpt(labels.getString("s.option"), LongOpt.REQUIRED_ARGUMENT, null, 's'),
new LongOpt(labels.getString("o.option"), LongOpt.REQUIRED_ARGUMENT, null, 'o'),
new LongOpt(labels.getString("f.option"), LongOpt.NO_ARGUMENT, null, 'f'),
new LongOpt(labels.getString("r.option"), LongOpt.REQUIRED_ARGUMENT, null, 'r'),
};
String oneLetterOptions = "m:l:T:i:t:s:o:fr:";
Getopt opts = new Getopt(labels.getString("tohtml"), args, oneLetterOptions, longopts);
ToHTML toHTML = new ToHTML();
toHTML.setTemplate(ClassLoader.getSystemResource("com/Ostermiller/Syntax/page.bte").toString());
toHTML.setStyleSheet("syntax.css");
String output = null;
String title = null;
boolean force = false;
int c;
while ((c = opts.getopt()) != -1){
switch(c){
case 1:{
// print out the help message
String[] helpFlags = new String[]{
"--" + labels.getString("help.option"),
"--" + labels.getString("version.option"),
"--" + labels.getString("about.option"),
"-m --" + labels.getString("m.option") + " <" + labels.getString("type") + ">",
"-l --" + labels.getString("l.option") + " <" + labels.getString("class") + ">",
"-T --" + labels.getString("T.option") + " <" + labels.getString("title") + ">",
"-i --" + labels.getString("i.option") + " <" + labels.getString("class") + ">",
"-t --" + labels.getString("t.option") + " <" + labels.getString("url") + "|" + labels.getString("file") + ">",
"-s --" + labels.getString("s.option") + " <" + labels.getString("url") + ">",
"-o --" + labels.getString("o.option") + " <" + labels.getString("file") + ">",
"-f --" + labels.getString("f.option"),
"-r --" + labels.getString("r.option") + " <" + labels.getString("translate") + ">",
};
int maxLength = 0;
for (int i=0; i<helpFlags.length; i++){
maxLength = Math.max(maxLength, helpFlags[i].length());
}
maxLength += 1;
System.out.println(
labels.getString("tohtml") + " [-" + StringHelper.replace(oneLetterOptions, ":", "") + "] <" + labels.getString("files") + ">\n" +
labels.getString("purpose.message") + "\n" +
labels.getString("stdin.message") + "\n" +
" " + StringHelper.postpad(helpFlags[0] ,maxLength, ' ') + labels.getString("help.message") + "\n" +
" " + StringHelper.postpad(helpFlags[1] ,maxLength, ' ') + labels.getString("version.message") + "\n" +
" " + StringHelper.postpad(helpFlags[2] ,maxLength, ' ') + labels.getString("about.message") + "\n" +
" " + StringHelper.postpad(helpFlags[3] ,maxLength, ' ') + labels.getString("m.message") + "\n" +
" " + StringHelper.postpad(helpFlags[4] ,maxLength, ' ') + labels.getString("l.message") + "\n" +
" " + StringHelper.postpad(helpFlags[5] ,maxLength, ' ') + labels.getString("T.message") + "\n" +
" " + StringHelper.postpad(helpFlags[6] ,maxLength, ' ') + labels.getString("i.message") + "\n" +
" " + StringHelper.postpad(helpFlags[7] ,maxLength, ' ') + labels.getString("t.message") + "\n" +
" " + StringHelper.postpad(helpFlags[8] ,maxLength, ' ') + labels.getString("s.message") + "\n" +
" " + StringHelper.postpad(helpFlags[9] ,maxLength, ' ') + labels.getString("o.message") + "\n" +
" " + StringHelper.postpad(helpFlags[10] ,maxLength, ' ') + labels.getString("f.message") + "\n" +
" " + StringHelper.postpad(helpFlags[11] ,maxLength, ' ') + labels.getString("r.message") + "\n"
);
System.exit(0);
} break;
case 2:{
// print out the version message
System.out.println(MessageFormat.format(labels.getString("version"), new String[] {version}));
System.exit(0);
} break;
case 3:{
System.out.println(
labels.getString("tohtml") + " -- " + labels.getString("purpose.message") + "\n" +
MessageFormat.format(labels.getString("copyright"), new String[] {"1999-2002", "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Syntax+Highlighting)"}) + "\n\n" +
labels.getString("license")
);
System.exit(0);
} break;
case 'm':{
toHTML.setMimeType(opts.getOptarg());
} break;
case 'l':{
toHTML.setLexerType(opts.getOptarg());
} break;
case 'T':{
title = opts.getOptarg();
} break;
case 'i':{
toHTML.addIgnoreStyle(opts.getOptarg());
} break;
case 't':{
String template = opts.getOptarg();
File f = new File(opts.getOptarg());
if (f.exists()){
try {
toHTML.setTemplate(f.toURL().toString());
} catch (MalformedURLException mfue){
toHTML.setTemplate(template);
}
} else {
toHTML.setTemplate(template);
}
} break;
case 's':{
toHTML.setStyleSheet(opts.getOptarg());
} break;
case 'o':{
output = opts.getOptarg();
} break;
case 'f':{
force = true;
} break;
case 'r':{
java.util.StringTokenizer st = new java.util.StringTokenizer(opts.getOptarg(), "=", false);
if (st.countTokens() != 2){
System.err.println(labels.getString("unexpectedTranslation"));
System.exit(1);
}
toHTML.translateStyle(st.nextToken().trim(), st.nextToken().trim());
} break;
default:{
System.err.println(labels.getString("unexpectedArgument"));
System.exit(1);
}
}
}
try {
if (args.length <= opts.getOptind()){
try {
if (output == null) output = "--";
toHTML.setOutput(output, "out.html", force);
toHTML.setInput(new InputStreamReader(System.in));
toHTML.setTitle((title!=null)?(title):("HTML of System.in" + ((toHTML.getMimeType()==null)?"":(" ("+toHTML.getMimeType()+")"))));
toHTML.writeFullHTML();
} catch (IOException e){
System.err.println(e.getMessage());
}
} else {
for (int i=opts.getOptind(); i<args.length; i++){
try {
String defaultName = args[i] + ".html";
String outputFileName = null;
outputFileName = (output == null)?defaultName:output;
toHTML.setOutput(outputFileName, defaultName, force);
toHTML.setExtFromFileName(args[i]);
toHTML.setDocNameFromFileName(args[i]);
toHTML.setInput(new FileReader(args[i]));
toHTML.setTitle((title!=null)?(title):("HTML of " +
getDocName(args[i]) + ((toHTML.getMimeType()==null)?"":(" ("+toHTML.getMimeType()+")"))));
toHTML.writeFullHTML();
} catch (IOException e){
System.err.println(e.getMessage());
}
}
}
} catch (InvocationTargetException x){
System.err.println(x.getMessage());
} catch (CompileException x){
System.err.println(x.getMessage());
}
}
private static String getDocName(String fileName){
String docName = null;
if (fileName != null) {
docName = "";
int dotIndex = fileName.lastIndexOf(".");
int sepIndex = fileName.lastIndexOf(System.getProperty("file.separator"));
int start = sepIndex + 1;
if (sepIndex == -1){
start = 0;
}
int end = dotIndex;
if (dotIndex == -1 || dotIndex < sepIndex){
end = fileName.length();
}
docName = fileName.substring(start, end);
}
return docName;
}
/**
* Sets the document name minus the path and extension
*
* @param inputFileName file name from which to extract the document name.
*/
public void setDocNameFromFileName(String inputFileName){
setDocName(getDocName(inputFileName));
}
/**
* Sets the file extension to be anything after the last '.' in the
* given file name.
*
* @param inputFileName file name from which to extract the extension.
*/
public void setExtFromFileName(String inputFileName){
String extension = null;
if (inputFileName != null) {
extension = "";
int dotIndex = inputFileName.lastIndexOf(".");
int sepIndex = inputFileName.lastIndexOf(System.getProperty("file.separator"));
if (dotIndex != -1 && dotIndex > sepIndex) {
extension = inputFileName.substring(dotIndex+1, inputFileName.length());
}
}
setFileExt(extension);
}
private void setOutput(String output, String defaultName, boolean force) throws IOException {
if (output.equals("--")){
setOutput(new PrintWriter(System.out, true));
} else {
File outputFile = new File(defaultName);
if (output != null){
outputFile = new File(output);
if (outputFile.isDirectory()){
int dirIndex = defaultName.lastIndexOf(System.getProperty("file.separator"));
if (dirIndex != -1){
defaultName = defaultName.substring(dirIndex+1, defaultName.length());
}
outputFile = new File (outputFile, defaultName);
}
}
if (!force && outputFile.exists()){
throw new IOException(
MessageFormat.format(
labels.getString("fileExists"),
new Object[] {
outputFile.toString()
}
)
);
}
setOutput(outputFile);
if (getStyleSheet().equals("syntax.css")){
putSyntaxCSS(outputFile.getParentFile());
}
}
}
private static boolean putSyntaxCSS(File directory) throws IOException {
File f;
if (directory==null) {
f = new File("syntax.css");
} else {
f = new File(directory, "syntax.css");
}
if (f.exists()) return false;
FileOutputStream out = new FileOutputStream(f);
InputStream in = ClassLoader.getSystemResourceAsStream("com/Ostermiller/Syntax/doc/syntax.css");
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer,0,read);
}
in.close();
out.close();
return true;
}
/**
* Write the html encoded version of a document
* using syntax highlighting for Java.
* <p>
* The document produced is not a full html document but rather
* an html fragment that may be inserted into a full document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html.
* @param out html document gets written here.
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlifyJava(Reader in, PrintWriter out) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(new JavaLexer(in));
toHTML.setOutput(out);
toHTML.writeHTMLFragment();
} catch (InvocationTargetException x){
// can't happen
}
}
/**
* Write the html encoded version of a document
* using syntax highlighting for C.
* <p>
* The document produced is not a full html document but rather
* an html fragment that may be inserted into a full document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html.
* @param out html document gets written here.
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlifyC(Reader in, PrintWriter out) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(new CLexer(in));
toHTML.setOutput(out);
toHTML.writeHTMLFragment();
} catch (InvocationTargetException x){
// can't happen
}
}
/**
* Write the html encoded version of a document
* using syntax highlighting for HTML using a simple coloring
* algorithm.
* <p>
* The document produced is not a full html document but rather
* an html fragment that may be inserted into a full document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html.
* @param out html document gets written here.
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlifySimpleHTML(Reader in, PrintWriter out) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(new HTMLLexer(in));
toHTML.setOutput(out);
toHTML.writeHTMLFragment();
} catch (InvocationTargetException x){
// can't happen
}
}
/**
* Write the html encoded version of a document
* using syntax highlighting for HTML using a complex coloring
* algorithm.
* <p>
* The document produced is not a full html document but rather
* an html fragment that may be inserted into a full document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html.
* @param out html document gets written here.
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlifyComplexHTML(Reader in, PrintWriter out) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(new HTMLLexer1(in));
toHTML.setOutput(out);
toHTML.writeHTMLFragment();
} catch (InvocationTargetException x){
// can't happen
}
}
/**
* Write the html encoded version of a document
* using no syntax highlighting.
* <p>
* The document produced is not a full html document but rather
* an html fragment that may be inserted into a full document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html.
* @param out html document gets written here.
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlifyPlain(Reader in, PrintWriter out) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(new PlainLexer(in));
toHTML.setOutput(out);
toHTML.writeHTMLFragment();
} catch (InvocationTargetException x){
// can't happen
}
}
/**
* Write the html encoded version of a document
* using syntax highlighting for the given mime type.
* <p>
* Recognized mime-types include:
* <ul>
* <li>text/html
* <li>text/x-java
* <li>text/x-csrc
* <li>text/x-chdr
* <li>text/x-csrc
* <li>text/x-c++hdr
* </ul>
* If a mime type is not recognized, the html is written
* without syntax highlighting.
* <p>
* The document produced is not a full html document but rather
* an html fragment that may be inserted into a full document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html
* @param out html document gets written here
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(Reader in, PrintWriter out, String mimeType) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setInput(in);
toHTML.setOutput(out);
toHTML.setMimeType(mimeType);
toHTML.writeFullHTML();
} catch (InvocationTargetException x){
// can't happen
} catch (CompileException x){
// can't happen
}
}
private void saveLexer(Lexer lexer){
if (!lexers.containsKey(lexerType)){
lexers.put(lexer.getClass().getName(), lexer);
}
}
/**
* Return a lexer that is an instance of the given
* class name.
* If no appropriate lexer is found, return null.
*
* @return lexer for the class name.
* @throws InvocationTargetException if the class could not be instantiated.
*/
private Lexer getLexerFromClass(String lexerType) throws IOException, InvocationTargetException {
Lexer lexer = null;
if (lexerType != null){
if (lexers.containsKey(lexerType)){
lexer = (Lexer)lexers.get(lexerType);
lexer.reset(in, 0, 0, 0);
} else {
try {
lexer = (Lexer)(
Class.forName(lexerType)
.getDeclaredConstructor(new Class[] {Class.forName("java.io.Reader")})
.newInstance(new Object[] {in})
);
saveLexer(lexer);
} catch (ClassNotFoundException x){
throw new InvocationTargetException(x);
} catch (NoSuchMethodException x){
throw new InvocationTargetException(x);
} catch (InstantiationException x){
throw new InvocationTargetException(x);
} catch (IllegalAccessException x){
throw new InvocationTargetException(x);
}
}
}
return lexer;
}
/**
* Return an appropriate lexer for the current mime type.
* If no appropriate lexer is found, return null.
*
* @return lexer for the current mime type.
*/
private Lexer getLexerFromMime() throws IOException, InvocationTargetException {
String lexerType = null;
if (mimeType != null){
if (registeredMimeTypes.containsKey(mimeType)){
lexerType = (String)registeredMimeTypes.get(mimeType);
} else {
int slashIndex = mimeType.indexOf("/");
if (slashIndex > 0){
String mainMime = mimeType.substring(0, slashIndex);
if (registeredMimeTypes.containsKey(mainMime)){
lexerType = (String)registeredMimeTypes.get(mainMime);
}
}
}
}
return getLexerFromClass(lexerType);
}
/**
* Return an appropriate lexer for the current file extension.
* If no appropriate lexer is found, return null.
*
* @return lexer for the current file extension.
*/
private Lexer getLexerFromExt() throws IOException, InvocationTargetException {
String lexerType = null;
if (fileExt != null){
if (registeredFileExtensions.containsKey(fileExt.toLowerCase())){
lexerType = (String)registeredFileExtensions.get(fileExt.toLowerCase());
}
}
if (lexerType == null) lexerType = "com.Ostermiller.Syntax.Lexer.PlainLexer";
return getLexerFromClass(lexerType);
}
/**
* Write the html version of the output from the given lexer as a
* stand alone html document with the given title. The html document
* will use the style sheet syntax.css.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param lexer contains the tokens that need to be html escaped.
* @param out html gets written here.
* @param title the title to be put on the html document.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(Reader in, PrintWriter out, String title, String mimeType) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setInput(in);
toHTML.setOutput(out);
toHTML.setTitle(title);
toHTML.setMimeType(mimeType);
toHTML.writeFullHTML();
} catch (InvocationTargetException x){
// can't happen
} catch (CompileException x){
// can't happen
}
}
/**
* Write the html version of a document
* using syntax highlighting for the given mime type.
* <P>
* Conversions between characters and bytes will be done
* using the default character set for the system.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param in Data stream that needs to be formatted in html.
* @param out html document gets written here.
* @param mimeType the mime type of the document to be used for syntax highlighting purposes.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(InputStream in, PrintStream out, String mimeType) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setInput(new InputStreamReader(in));
toHTML.setOutput(new PrintWriter(out));
toHTML.setMimeType(mimeType);
toHTML.writeFullHTML();
} catch (InvocationTargetException x){
// can't happen
} catch (CompileException x){
// can't happen
}
}
/**
* Write the html version of the output from the given lexer. The
* document is written without an html head or footer so that it
* can be used as part of a larger html document.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param lexer contains the tokens that need to be html escaped.
* @param out html gets written here.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(Lexer lexer, PrintWriter out) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(lexer);
toHTML.setOutput(new PrintWriter(out));
toHTML.writeHTMLFragment();
} catch (InvocationTargetException x){
// can't happen
}
}
/**
* Write the html version of the output from the given lexer. The
* document is written without an html head or footer so that it
* can be used as part of a larger html document.
* <P>
* Conversions between characters and bytes will be done
* using the default character set for the system.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param lexer contains the tokens that need to be html escaped.
* @param out html gets written here.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(Lexer lexer, PrintStream out) throws IOException {
htmlify(lexer, new PrintWriter(out));
}
/**
* Write the html version of the output from the given lexer as a
* stand alone html document with the given title. The html document
* will use the style sheet syntax.css.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param lexer contains the tokens that need to be html escaped.
* @param out html gets written here.
* @param title the title to be put on the html document.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(Lexer lexer, PrintWriter out, String title) throws IOException {
try {
ToHTML toHTML = new ToHTML();
toHTML.setLexer(lexer);
toHTML.setOutput(out);
toHTML.setTitle(title);
toHTML.writeFullHTML();
} catch (InvocationTargetException x){
// can't happen
} catch (CompileException x){
// can't happen
}
}
/**
* Write the html version of the output from the given lexer as a
* stand alone html document with the given title. The html document
* will use the style sheet syntax.css.
* <P>
* Conversions between characters and bytes will be done
* using the default character set for the system.
* <p>
* This is a convenience method that creates an new ToHTML object.
*
* @param lexer contains the tokens that need to be html escaped.
* @param out html gets written here.
* @param title the title to be put on the html document.
* @throws IOException if an I/O error occurs.
*/
public static void htmlify(Lexer lexer, PrintStream out, String title) throws IOException {
htmlify(lexer, new PrintWriter(out), title);
}
/**
* Write the string after escaping characters that would hinder
* it from rendering in html.
*
* @param text The string to be escaped and written
* @param out output gets written here
*/
public static void writeEscapedHTML(String text, PrintWriter out){
boolean lastSpace = false;
for (int i=0; i < text.length(); i++){
char ch = text.charAt(i);
switch(ch){
case '<': {
out.print("<");
lastSpace = false;
break;
}
case '>': {
out.print(">");
lastSpace = false;
break;
}
case '&': {
out.print("&");
lastSpace = false;
break;
}
case '"': {
out.print(""");
lastSpace = false;
break;
}
default: {
out.print(ch);
lastSpace = false;
break;
}
}
}
}
/**
* Write the string after escaping characters that would hinder
* it from rendering in html.
* <P>
* Conversions between characters and bytes will be done
* using the default character set for the system.
*
* @param text The string to be escaped and written
* @param out output gets written here
*/
public static void writeEscapedHTML(String text, PrintStream out){
writeEscapedHTML(text, new PrintWriter(out));
}
}