Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/sun/tools/javadoc/MIFPrintStream.java

/*
 * @(#)MIFPrintStream.java	1.4 95/12/07 Frank Yellin
 *
 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */


package sun.tools.javadoc;

import java.util.*;
import java.io.*;
import sun.tools.java.*;


public class MIFPrintStream {
    PrintStream out;
    public final static int STATE_OUTER = 0;
    public final static int STATE_PARAGRAPH = 1;
    public final static int STATE_LINE = 2;
    public final static int STATE_STRING = 3;
  
    public final static char hardReturnChar =  (char)0x09;
    public final static char hardSpaceChar  =  (char)0x11;

    public final static int MIF_VERSION = 5;

    int state;
    
    Vector pendingMarks = null;
    int currentStringLength = 0; // characters in current string
    static Date today = new Date();

    public MIFPrintStream(OutputStream out) { 
        this(new PrintStream(out));
    } 

    public MIFPrintStream(PrintStream out) {
	this.out = out;
	out.println("<MIFFile " + MIF_VERSION + ".00>");
        out.println("<Comment Generated by javadoc on " + today + ">");
	include("DocTemplate.mif");
	out.println("<TextFlow <TFTag `A'> <TFAutoConnect Yes> <TextRectID 7>");
	state = STATE_OUTER;
    }
	
    public MIFPrintStream println(Object x) { 
	out.println(x.toString());
	return this;
    }

    public MIFPrintStream newParagraph(String name) { 
	topLevel();
        out.println("<Para");
	out.println("   <PgfTag `" + name + "'>");
	state = STATE_PARAGRAPH;
	return this;
    }

    public MIFPrintStream newParaLine() { 
	inParagraph();
	out.println("   <ParaLine");
	state = STATE_LINE;
	return this;
    }

    public MIFPrintStream newString(String tag) { 
	inParaLine();
	out.print("      <" + tag + " `");
	state = STATE_STRING;
	currentStringLength = 0;
	return this;
    }

    public MIFPrintStream topLevel() { 
	if (state > STATE_OUTER) {
	    inParagraph();
	    out.println("> # end paragraph");
	} 
	state = STATE_OUTER;
	return this;
    }

    public MIFPrintStream inParagraph() { 
	if (state < STATE_PARAGRAPH) 
	    throw new RuntimeException("Bad call to inParagraph");
	else if (state > STATE_PARAGRAPH) {
	    inParaLine();
	    out.println("   >");
	    state = STATE_PARAGRAPH;
	}
	return this;
    }

    public MIFPrintStream inParaLine() { 
	if (state < STATE_LINE) 
	    return newParaLine();
	else if (state > STATE_LINE) {
	    inString();
	    out.println("'>");
	    state = STATE_LINE;
	}
	return this;
    }
    
    public MIFPrintStream inString() { 
	if (state < STATE_STRING) 
	    return newString("String");
	return this;
    }


    public MIFPrintStream indent(double first, double left, double right) {
	inParagraph();
	out.println("   <PgfFIndent  " + first + "in>");
	out.println("   <PgfLIndent  " + left  + "in>");
	out.println("   <PgfRIndent  " + right + "in>");
	return this;
    }
    

    MIFPrintStream skip(int pts) {
	inParagraph();
	out.println("   <PgfSpBefore  " + pts + "pt>");
	return this;
    }

    MIFPrintStream bold(Object x) { 
	inParaLine();
	out.println("      <Font <FTag `'> <FWeight `Bold'> >");
	literal(x);
	inParaLine();
	out.println("      <Font <FTag `'> >");
	return this;
    }

    MIFPrintStream italic(Object x) { 
	inParaLine();
        out.println("      <Font <FTag `'> <FAngle `Italic'> >");
	literal(x);
	inParaLine();
	out.println("      <Font <FTag `'> >");
	return this;
    }

    MIFPrintStream tty(Object x) { 
	inParaLine();
	out.println("      <Font <FTag `'> <FFamily `Courier'> >");
	literal(x);
	inParaLine();
	out.println("      <Font <FTag `'> >");
	return this;
    }


    MIFPrintStream charTag(String tag, Object x) { 
	inParaLine();
	out.println("      <Font <FTag `" + tag + "'>  >");
	literal(x);
	inParaLine();
	out.println("      <Font <FTag `'> >");
	return this;
    }


    public MIFPrintStream literal(Object o) {
	String string = o.toString();
	int length = string.length();
	for (int i = 0; i < length; i++) 
	    literal(string.charAt(i));
	return this;
    }

    public MIFPrintStream literal(char ch) {
	inString();
	currentStringLength++;
	switch(ch) {
	    default:
	        if (ch >= ' ' && ch < 0x80) {
		    out.print(ch);
                } else {
		    currentStringLength += 4;
		    out.print("\\x" + 
			      Character.forDigit((ch >> 4) & 0xF, 16) +
			      Character.forDigit(ch & 0xF, 16) + ' ');
		}
		break;
	    case '\'':
		out.print("\\q"); currentStringLength++; break;
	    case '`':
		out.print("\\Q"); currentStringLength++; break;
	    case '>':
		out.print("\\>"); currentStringLength++; break;
	    case '\\':
		out.print("\\\\"); currentStringLength++; break;
	    case '\t':
		out.print("\\t"); currentStringLength++; break;
	    case ' ':
	        out.print(' ');	// normal
		if (currentStringLength >= 55) // break long lines
		    inParaLine();
		break;
	    case '\n':  // hard return
		out.print("\\n"); 
		inParagraph();
		break;

	}
	return this;
    }
    
    final static int MarkerHF1        = 0;
    final static int MarkerHF2        = 1;
    final static int MarkerIndex      = 2;
    final static int MarkerComment    = 3;
    final static int MarkerSubject    = 4;
    final static int MarkerAuthor     = 5;
    final static int MarkerGlossary   = 6;
    final static int MarkerEquation   = 7;
    final static int MarkerHypertext  = 8;
    final static int MarkerXRef       = 9;
    final static int MarkerCondText   = 10;

    private void markInternal(int mtype, Object text) {
	if (state == STATE_OUTER) { 
	    if (pendingMarks == null) 
		pendingMarks = new Vector();
	    pendingMarks.addElement(new Integer(mtype));
	    pendingMarks.addElement(text);
	} else { 
	    inParaLine();
	    out.println("      <Marker <MType " + mtype + "> <MText `" + 
			text + "'> >");
	}
    }

    public MIFPrintStream mark(int mtype, Object text) {
	markInternal(mtype, literalText(text.toString(), false));
	return this;
    }

    public MIFPrintStream markReference(Object text) { 
	markInternal(MarkerXRef, literalText(text.toString(), false));
	return this;
    }

    public MIFPrintStream index(Object text, String sort) { 
        markInternal(MarkerIndex, 
		     literalText(text.toString(), true) + '[' + 
		     literalText(sort.toString(), true) + ']');
	return this;
    }

    public MIFPrintStream index(Object text) { 
        markInternal(MarkerIndex, literalText(text.toString(), true));
	return this;
    }

    private String literalText(String text, boolean forIndex) { 
	int length = text.length();
	StringBuffer result = new StringBuffer();
	for (int i = 0; i < length; i++) {
	    char ch = text.charAt(i);
	    switch (ch) { 
	    default:
	        if (ch >= ' ' && ch < 0x80) {
		    result.append(ch);
                } else {
		    result.append("\\x" + 
				  Character.forDigit((ch >> 4) & 0xF, 16) +
				  Character.forDigit(ch & 0xF, 16) + ' ');
		}
		break;
	    case '\'':
		result.append("\\q"); break;
	    case '`':
		result.append("\\Q"); break;
	    case '>':
		result.append("\\>"); break;
	    case '\\':
		result.append("\\\\"); break;
	    case '[': case ']':
		if (forIndex) 
		    result.append("\\\\");
		result.append(ch);
		break;
	    }
	}
	return result.toString();
    }

    public MIFPrintStream emitPendingMarks() {
	if (pendingMarks != null) { 
	    for (int i = 0; i < pendingMarks.size(); i += 2) { 
		int mtype = ((Number)(pendingMarks.elementAt(i))).intValue();
		String text = (String)(pendingMarks.elementAt(i + 1));
		mark(mtype, text);
	    }
	}
	pendingMarks = null;
	return this;
    }

    public MIFPrintStream XRef(String type, String text, String source) {
	String ttext = literalText(text, false);
	String ref = (source == null) ? "" : literalText("<c>" + source, false);

	inParaLine();
	out.println("      <XRef <XRefName `" + type + "'>"); 
	out.println("            <XRefSrcText `" + ttext + "'>");
	out.println("            <XRefSrcFile `" + ref + "'>");
	out.println("      > #XRef");
	out.println("      <String `page\\x11 XX\u0015XX'>");	// default
	out.println("      <XRefEnd>");
	return this;
    }


    public MIFPrintStream close() { 
	topLevel();
	out.println("> #end TextFlow"); 
	out.println("# end of MIF");
	out.close();
	return null;
    }
	
    static final int BOLD = 1;
    static final int ITALIC = 2;
    static final int UNDERLINE = 4;
    static final int SMALLCAPS = 8;
    static final int TTY = 16;

    boolean isFormatted, lastSpace;
    int currentFont, desiredFont;
    String currentType;

    MIFPrintStream html(String type, String text) {
	int[] fontStack = new int[30];
	int fontStackHeight = 0;

        newParagraph(type);
	currentFont = desiredFont = 0;
	isFormatted = false; 
	lastSpace = true;
	currentType = type;

        for (int i = 0; i < text.length(); i++) { 
	    boolean seenSpace = false, seenHTML = false;
	    char ch = text.charAt(i);
	    String html = null;
	    switch(ch) { 
	        default:
		    htmlLiteral((char)ch);
		    break;
	        case '&':
		    if (text.startsWith("gt;", i + 1)) { 
			htmlLiteral('>'); i += 3; break;
		    } else if (text.startsWith("lt;", i + 1)) {
			htmlLiteral('<'); i += 3; break;
		    } else if (text.startsWith("amp;", i + 1)) {
			htmlLiteral('&'); i += 4; break;
		    } else if (text.startsWith("quot;", i + 1)) {
			htmlLiteral('"'); i += 5; break;
		    } else if (text.startsWith("reg;", i + 1)) { 
			htmlLiteral('\u00a8'); i  += 4; break; 
		    } else if (text.startsWith("copy;", i + 1)) { 
			htmlLiteral('\u00a9'); i += 5; break;
		    } else { 
			htmlLiteral('&'); break;
		    }

	        case '<': {
		    int j = text.indexOf('>', i + 1);
		    if (j <= i + 1) { 
			htmlLiteral('<');
			break;
		    } 
		    html = text.substring(i + 1, j); // html code sequence
		    char first = html.charAt(0);
		    if (first != '/' && Character.digit(first, 36) == -1) {
			htmlLiteral('<');
			html = null;
			break;
		    }
		    i = j; // point at the >
		    break;
		}

	        case '\t': case ' ': case '\n':
		    if (isFormatted) { 
			htmlLiteral(ch == ' ' ? hardSpaceChar : ch);
		    } else if (!lastSpace) { 
			htmlLiteral(' ');
		    }
		    break;
	    }
	    if (html == null) 
		continue;

	    if (html.equalsIgnoreCase("tt")||html.equalsIgnoreCase("code")) {
		fontStack[fontStackHeight++] = currentFont;
		desiredFont = currentFont | TTY;
	    } else if (html.equalsIgnoreCase("cite") || 
		       html.equalsIgnoreCase("em") ||
		       html.equalsIgnoreCase("i")) { 
		fontStack[++fontStackHeight] = currentFont;
		desiredFont = currentFont | ITALIC;
	    } else if (html.equalsIgnoreCase("b")) { 
		fontStack[fontStackHeight++] = currentFont;
		desiredFont = currentFont | BOLD;
	    } else if (html.equalsIgnoreCase("strong")) { 
		fontStack[fontStackHeight++] = currentFont;
		desiredFont = currentFont | BOLD | SMALLCAPS;
	    } else if (html.equalsIgnoreCase("/tt") || 
		       html.equalsIgnoreCase("/code") || 
		       html.equalsIgnoreCase("/cite") ||
		       html.equalsIgnoreCase("/em") ||
		       html.equalsIgnoreCase("/i") ||
		       html.equalsIgnoreCase("/b") ||
		       html.equalsIgnoreCase("/strong")) {
		if (fontStackHeight == 0) {
		    System.out.println("Font stack underflow");
		    desiredFont = 0;
		} else {
		    desiredFont = fontStack[--fontStackHeight];
		}
	    } else if (html.equalsIgnoreCase("pre")) {
		topLevel();
		fontStack[++fontStackHeight] = currentFont;
		topLevel();
		desiredFont = currentFont | TTY;
		isFormatted = true;
	    } else if (html.equalsIgnoreCase("/pre")) { 
		topLevel();
		desiredFont = fontStack[--fontStackHeight];
		isFormatted = false;
	    } else if (html.equalsIgnoreCase("p") || 
		       html.equalsIgnoreCase("par")) {
		topLevel();
	    } else if (html.startsWith("a ") || 
		       html.startsWith("A ") || 
		       html.equalsIgnoreCase("/a")) { 
		// donothing for anchors
	    } else if (html.indexOf("@") > 0 && html.indexOf(' ') == -1) { 
		// probably an internet address
		htmlLiteral('<'); 
		i -= html.length(); // point back at skipped text
	    } else {
		System.out.println("Unknown html <" + html + ">");
	    }
	} // end of for loop
	return this;
    }

    private void htmlLiteral(char ch) { 
	if (state < STATE_PARAGRAPH) { 
	    newParagraph(currentType);
	    out.println("   <PgfSpBefore  6pt>");
	    currentFont = 0;
	    lastSpace = true;
	}
	if (currentFont != desiredFont) { 
	    inParagraph();
	    out.println("   <Font <FTag `'> >");
	    out.print("   <Font ");
	    if ((desiredFont & ITALIC) != 0)
		out.print("<FAngle Italic> ");
	    if ((desiredFont & BOLD) != 0)
		out.print("<FBold Yes> ");
	    if ((desiredFont & UNDERLINE) != 0)
		out.print("<FUnderlining FSingle> ");
	    if ((desiredFont & SMALLCAPS) != 0)
		out.print("<FCase FSmallCaps> ");
	    if ((desiredFont & TTY) != 0)
		out.print("<FFamily Courier> ");
	    out.println(">");
	    currentFont = desiredFont;
	}
	literal(ch);
	lastSpace = (ch <= ' ');
    }

    public static void main(String argv[]) {
	MIFPrintStream x = new MIFPrintStream(System.out);
	x.html("Body", "<b>hello </b>");
	x.close();
    }
    
    private void include(String filename) { 
        if (MIF_VERSION >= 5) {
	    out.println("include(" + filename + ")");
	} else { 
	    try { 
		FileInputStream f = new FileInputStream(filename);
		BufferedInputStream b = new BufferedInputStream(f);
		byte buffer[] = new byte[1024];
		
		for (;;) { 
		    int i = b.read(buffer);
		    if (i < 0) break;
		    out.write(buffer, 0, i);
		}
	    } catch (Exception e) {
	    }
	}
    }
}