<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">/**
 * CodeWriter -- Andrew C. Myers, April 2001
 * For use in Cornell University Computer Science CS 412/413
 */

package Iota.util;

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.io.IOException;
import java.util.Vector;

/** 
 * A &lt;code&gt;CodeWriter&lt;/code&gt; is a pretty-printing engine.
 * It formats structured text onto an output stream &lt;code&gt;o&lt;/code&gt; in the
 * minimum number of lines, while keeping the width of the output
 * within &lt;code&gt;width&lt;/code&gt; characters if possible.
 */
public class CodeWriter
{
    /**
     * Create a CodeWriter object with output stream &lt;code&gt;o&lt;/code&gt;
     * and width &lt;code&gt;width_&lt;/code&gt;.
     */

    public CodeWriter(OutputStream o, int width_) {
        output = new OutputStreamWriter(o);
        width = width_;
        current = input = new Block(null, 0);
    }

    /**
     * Create a CodeWriter object with output &lt;code&gt;w&lt;/code&gt; and
     * width &lt;code&gt;width_&lt;/code&gt;.
     */
    public CodeWriter(Writer w, int width_) {
        output = w;
        width = width_;
        current = input = new Block(null, 0);
    }
        
    /** Print the string &lt;code&gt;s&lt;/code&gt; verbatim on the output stream. */
    public void write(String s) {
       if (s.length() &gt; 0)
          current.add(new StringItem(s));
    }

    /** Force a newline with no added indentation. */
    public void newline()
    {
       newline(0);
    }

    /**
     * Start a new block with a relative indentation of &lt;code&gt;n&lt;/code&gt;
     * characters.
     * &lt;br&gt;
     * A block is a formatting unit. The formatting algorithm will try
     * to put the whole block in one line unless
     * &lt;ul&gt;
     * &lt;li&gt;there is a &lt;code&gt;newline&lt;/code&gt; item in the block.&lt;/li&gt;
     * &lt;li&gt;the block cannot fit in one line.&lt;/li&gt;
     * &lt;/ul&gt;
     * If either of the two conditions is satisfied, the
     * formatting algorithm will break the block into lines: every
     * &lt;code&gt;allowBreak&lt;/code&gt; will cause a line change, the first line
     * is printed at the current cursor position &lt;code&gt;pos&lt;/code&gt;,
     * all the following lines are printed at the position
     * &lt;code&gt;pos+n&lt;/code&gt;.
     * 
     * @param n the number of characters increased on indentation (relative
     * to the current position) for all lines in the block.
     */         
    public void begin(int n) {
        Block b = new Block(current, n);
        current.add(b);
        current = b;
    }
        
    /** 
     * Terminate the most recent outstanding &lt;code&gt;begin&lt;/code&gt;. 
     */
    public void end() {
        current = current.parent;
        if (current == null) throw new RuntimeException();
    }

    /**
     * Allow a newline. Indentation will be preserved.
     * If no newline is inserted, a single space character is output instead.
     *
     * @param n the amount of increase in indentation if
     * the newline is inserted.
     */ 
    public void allowBreak(int n) {
        current.add(new AllowBreak(n, " "));
    }

    /**
     * Allow a newline. Indentation will be preserved.
     *
     * @param n the amount of increase in indentation if
     *  the newline is inserted.
     * @param alt if no newline is inserted, the string &lt;code&gt;alt&lt;/code&gt; is
     *  output instead.   
     */ 
    public void allowBreak(int n, String alt) {
        current.add(new AllowBreak(n, alt));
    }

    /**
     * Force a newline. Indentation will be preserved.  This method
     * should be used sparingly; usually a call to &lt;code&gt;allowBreak&lt;/code&gt; is
     * preferable because forcing a newline also causes all breaks
     * in containing blocks to be broken.
     *
     * @param n the amount of increase in indentation after the newline.
     */
    public void newline(int n) {
        current.add(new Newline(n));
    }

    /**
     * Send out the current batch of text to be formatted. All
     * outstanding &lt;code&gt;begin&lt;/code&gt;'s are closed and the current
     * indentation level is reset to 0. Returns true if formatting
     * was completely successful (the margins were obeyed).
     */
    public boolean flush() throws IOException {
	boolean success = true;
	try {
	    Item.format(input,0, 0, width, width, true, true);
	} catch (Overrun o) { success = false; }
        input.sendOutput(output, 0, 0);
        output.flush();
        input.free();
        current = input = new Block(null, 0);
	return success;
    }
    /**
     * Return a readable representation of all the structured input
     * given to the CodeWriter since the last flush.
     */

    Block input;
    Block current;

    Writer output;
    int width;
}

/**
 * An &lt;code&gt;Overrun&lt;/code&gt; represents a formatting that failed because the right
 * margin was exceeded by at least &lt;code&gt;amount&lt;/code&gt; chars.
 */
class Overrun extends Exception 
{
    int amount;
    Overrun(int amount_) {
	amount = amount_;
    }  
}

/**
 * An &lt;code&gt;Item&lt;/code&gt; is a piece of input handed to the formatter. It
 * contains a reference to a possibly empty list of items that follow it.
 */
abstract class Item 
{
    Item next;

    protected Item() { next = null; }

    /** 
     * Try to format this item and subsequent items. The current cursor
     * position is &lt;code&gt;pos&lt;/code&gt;, left and right margins are as
     * specified. Returns the final position, which must be &lt;code&gt;&amp;lt;
     * fin&lt;/code&gt;. If breaks may be broken, &lt;code&gt;can_break&lt;/code&gt; is
     * set. Return the new cursor position (which may overrun rmargin,
     * fin, or both, and set any contained breaks accordingly.  (It is
     * important that formatN not necessarily convert overruns in its
     * final position into exceptions. This allows the calling routine
     * to distinguish between 'internal' overruns and ones that it can
     * tack on a conservative estimate of how much formatting the rest
     * of the list will make the overrun go up by.  Also, it simplifies
     * the coding of formatN.)
     *
     * Requires: rmargin &amp;lt; lmargin, pos &amp;lt;= rmargin.
     */

    abstract int formatN(int lmargin, int pos, int rmargin, int fin,
			     boolean can_break, boolean nofail) throws Overrun;
    /**
     * Send the output associated with this item to &lt;code&gt;o&lt;/code&gt;, using the
     * current break settings.
     */
    abstract int sendOutput(Writer o, int lmargin, int pos)
      throws IOException;

    /** Make the garbage collector's job easy: free references to any
        other items. */
    void free() { 
        if( next != null) { 
            next.free(); 
            next = null; 
        }
    }

    /**
     * Try to format a whole sequence of items in the manner of formatN.
     * The initial position may be an overrun (this is the only way
     * that overruns are checked!) &lt;code&gt;it&lt;/code&gt; may be also null,
     * signifying an empty list.
     */
    static int format(Item it, int lmargin, int pos, int rmargin, int fin,
	  	          boolean can_break, boolean nofail) throws Overrun {
	if (!nofail &amp;&amp; pos &gt; rmargin) { // overrun
	    throw new Overrun(pos - rmargin);
	}
	if (it == null) { // no items to format. Check against final position.
	    if (!nofail &amp;&amp; pos &gt; fin) throw new Overrun(pos - fin);
	    return pos;
	}
	return it.formatN(lmargin, pos, rmargin, fin, can_break, nofail);
    }
}

/**
 * A Block is a formatting unit containing a list of other items
 * to be formatted.
 */
class Block extends Item {
    Block parent;
    Item first;
    Item last;
    int indent;
        
    Block(Block parent_, int indent_) {
        parent = parent_;
        first = last = null;
        indent = indent_;
    }
        
    /**
     * Add a new item to the end of the block. Successive
     * StringItems are concatenated together to limit recursion
     * depth when formatting.
     */
    void add(Item it) {
        if (first == null) {
	    first = it;
	} else {
	    if (it instanceof StringItem &amp;&amp; last instanceof StringItem) {
		StringItem lasts = (StringItem)last;
		lasts.appendString(((StringItem)it).s);
		return;
	    } else {
		last.next = it;
	    }
	}
        last = it;
    }

int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
    		boolean nofail) throws Overrun {
	    // "this_fin" is a final-position bound for the formatting of
	    // the contained list, cranked in from the right margin
	    // when subsequent items overrun.
	int this_fin = rmargin;
	    // Keep track of whether to send "nofail" down to contained
	    // list. Don't do this unless forced to.
	boolean this_nofail = false;
	    // Keep track of whether to send "can_break" down to contained
	    // list. Don't do this unless forced to.
	boolean this_break = false;
	while (true) {
	    int next_pos;
	    try {
	      next_pos = format(first, pos + indent, pos, rmargin,
				  this_fin, this_break, this_nofail &amp;&amp; this_break);
	    } catch (Overrun o) {
		if (!can_break) throw o;
		if (!this_break) { this_break = true; continue; }
		if (nofail) { this_nofail = true; continue; }
		throw o;
	    }
	    try {
		return format(next, lmargin, next_pos, rmargin, fin,
			      can_break, nofail);
	    } catch (Overrun o) {
		if (!can_break) throw o; // no way to fix it
		if (next instanceof AllowBreak) throw o; // not our fault
		this_break = true;
		if (next_pos &gt; this_fin) next_pos = this_fin;
		this_fin = next_pos - o.amount;
	    }
	}
    }

    int sendOutput(Writer o, int lmargin, int pos) throws IOException {
        Item it = first;
        lmargin = pos+indent;
        while (it != null) {
            pos = it.sendOutput(o, lmargin, pos);
            it = it.next;
        }
        return pos;
    }

    void free() {
        super.free();
  
        parent = null;
        if( first != null) {
            first.free();
        }
        last = null;
    }
}

class StringItem extends Item {
    String s;
    StringItem(String s_) { s = s_; }
        
    int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
		boolean nofail) throws Overrun {
	return format(next, lmargin, pos + s.length(), rmargin, fin,
		      can_break, nofail);
    }
    int sendOutput(Writer o, int lm, int pos) throws IOException {
        o.write(s);
        return pos + s.length();
    }
    void appendString(String s) { this.s = this.s + s; }
}

class AllowBreak extends Item 
{
    int indent;
    boolean broken = true;
    String alt;
        
    AllowBreak(int n_, String alt_) { indent = n_; alt = alt_; }
        
    int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
    		boolean nofail) throws Overrun {
        if (can_break) { pos = lmargin + indent; broken = true; }
        else { pos += alt.length(); broken = false; }
	return format(next, lmargin, pos, rmargin, fin, can_break, nofail);
    }
        
    int sendOutput(Writer o, int lmargin, int pos)
        throws IOException {
        if (broken) {
            o.write("\r\n");
            for (int i = 0; i &lt; lmargin + indent; i++) o.write(" ");
            return lmargin + indent;
        } else {
	    o.write(alt);
            return pos + alt.length();
        }
    }
}

class Newline extends AllowBreak 
{
    Newline(int n_) { super(n_, ""); }
        
    int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
		boolean nofail) throws Overrun {
        broken = true;
	if (!can_break) throw new Overrun(1);
	return format(next, lmargin, lmargin + indent, rmargin, fin,
			can_break, nofail);
    }
        
    int sendOutput(Writer o, int lmargin, int pos)
            throws IOException {
        o.write("\r\n");
        for (int i = 0; i &lt; lmargin + indent; i++) o.write(" ");
        return lmargin + indent;
    }
}
</pre></body></html>