KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > ppg > util > CodeWriter


1 /**
2  * CodeWriter -- Andrew C. Myers, April 2001
3  * For use in Cornell University Computer Science CS 412/413
4  */

5
6 package ppg.util;
7
8 import java.io.OutputStream JavaDoc;
9 import java.io.OutputStreamWriter JavaDoc;
10 import java.io.Writer JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.util.Vector JavaDoc;
13
14 /**
15  * A <code>CodeWriter</code> is a pretty-printing engine.
16  * It formats structured text onto an output stream <code>o</code> in the
17  * minimum number of lines, while keeping the width of the output
18  * within <code>width</code> characters if possible.
19  */

20 public class CodeWriter
21 {
22     /**
23      * Create a CodeWriter object with output stream <code>o</code>
24      * and width <code>width_</code>.
25      */

26
27     public CodeWriter(OutputStream JavaDoc o, int width_) {
28         output = new OutputStreamWriter JavaDoc(o);
29         width = width_;
30         current = input = new Block(null, 0);
31     }
32
33     /**
34      * Create a CodeWriter object with output <code>w</code> and
35      * width <code>width_</code>.
36      */

37     public CodeWriter(Writer JavaDoc w, int width_) {
38         output = w;
39         width = width_;
40         current = input = new Block(null, 0);
41     }
42         
43     /** Print the string <code>s</code> verbatim on the output stream. */
44     public void write(String JavaDoc s) {
45        if (s.length() > 0)
46           current.add(new StringItem(s));
47     }
48
49     /** Force a newline with no added indentation. */
50     public void newline()
51     {
52        newline(0);
53     }
54
55     /**
56      * Start a new block with a relative indentation of <code>n</code>
57      * characters.
58      * <br>
59      * A block is a formatting unit. The formatting algorithm will try
60      * to put the whole block in one line unless
61      * <ul>
62      * <li>there is a <code>newline</code> item in the block.</li>
63      * <li>the block cannot fit in one line.</li>
64      * </ul>
65      * If either of the two conditions is satisfied, the
66      * formatting algorithm will break the block into lines: every
67      * <code>allowBreak</code> will cause a line change, the first line
68      * is printed at the current cursor position <code>pos</code>,
69      * all the following lines are printed at the position
70      * <code>pos+n</code>.
71      *
72      * @param n the number of characters increased on indentation (relative
73      * to the current position) for all lines in the block.
74      */

75     public void begin(int n) {
76         Block b = new Block(current, n);
77         current.add(b);
78         current = b;
79     }
80         
81     /**
82      * Terminate the most recent outstanding <code>begin</code>.
83      */

84     public void end() {
85         current = current.parent;
86         if (current == null) throw new RuntimeException JavaDoc();
87     }
88
89     /**
90      * Allow a newline. Indentation will be preserved.
91      * If no newline is inserted, a single space character is output instead.
92      *
93      * @param n the amount of increase in indentation if
94      * the newline is inserted.
95      */

96     public void allowBreak(int n) {
97         current.add(new AllowBreak(n, " "));
98     }
99
100     /**
101      * Allow a newline. Indentation will be preserved.
102      *
103      * @param n the amount of increase in indentation if
104      * the newline is inserted.
105      * @param alt if no newline is inserted, the string <code>alt</code> is
106      * output instead.
107      */

108     public void allowBreak(int n, String JavaDoc alt) {
109         current.add(new AllowBreak(n, alt));
110     }
111
112     /**
113      * Force a newline. Indentation will be preserved. This method
114      * should be used sparingly; usually a call to <code>allowBreak</code> is
115      * preferable because forcing a newline also causes all breaks
116      * in containing blocks to be broken.
117      *
118      * @param n the amount of increase in indentation after the newline.
119      */

120     public void newline(int n) {
121         current.add(new Newline(n));
122     }
123
124     /**
125      * Send out the current batch of text to be formatted. All
126      * outstanding <code>begin</code>'s are closed and the current
127      * indentation level is reset to 0. Returns true if formatting
128      * was completely successful (the margins were obeyed).
129      */

130     public boolean flush() throws IOException JavaDoc {
131     boolean success = true;
132     try {
133         Item.format(input,0, 0, width, width, true, true);
134     } catch (Overrun o) { success = false; }
135         input.sendOutput(output, 0, 0);
136         output.flush();
137         input.free();
138         current = input = new Block(null, 0);
139     return success;
140     }
141     /**
142      * Return a readable representation of all the structured input
143      * given to the CodeWriter since the last flush.
144      */

145
146     Block input;
147     Block current;
148
149     Writer JavaDoc output;
150     int width;
151 }
152
153 /**
154  * An <code>Overrun</code> represents a formatting that failed because the right
155  * margin was exceeded by at least <code>amount</code> chars.
156  */

157 class Overrun extends Exception JavaDoc
158 {
159     int amount;
160     Overrun(int amount_) {
161     amount = amount_;
162     }
163 }
164
165 /**
166  * An <code>Item</code> is a piece of input handed to the formatter. It
167  * contains a reference to a possibly empty list of items that follow it.
168  */

169 abstract class Item
170 {
171     Item next;
172
173     protected Item() { next = null; }
174
175     /**
176      * Try to format this item and subsequent items. The current cursor
177      * position is <code>pos</code>, left and right margins are as
178      * specified. Returns the final position, which must be <code>&lt;
179      * fin</code>. If breaks may be broken, <code>can_break</code> is
180      * set. Return the new cursor position (which may overrun rmargin,
181      * fin, or both, and set any contained breaks accordingly. (It is
182      * important that formatN not necessarily convert overruns in its
183      * final position into exceptions. This allows the calling routine
184      * to distinguish between 'internal' overruns and ones that it can
185      * tack on a conservative estimate of how much formatting the rest
186      * of the list will make the overrun go up by. Also, it simplifies
187      * the coding of formatN.)
188      *
189      * Requires: rmargin &lt; lmargin, pos &lt;= rmargin.
190      */

191
192     abstract int formatN(int lmargin, int pos, int rmargin, int fin,
193                  boolean can_break, boolean nofail) throws Overrun;
194     /**
195      * Send the output associated with this item to <code>o</code>, using the
196      * current break settings.
197      */

198     abstract int sendOutput(Writer JavaDoc o, int lmargin, int pos)
199       throws IOException JavaDoc;
200
201     /** Make the garbage collector's job easy: free references to any
202         other items. */

203     void free() {
204         if( next != null) {
205             next.free();
206             next = null;
207         }
208     }
209
210     /**
211      * Try to format a whole sequence of items in the manner of formatN.
212      * The initial position may be an overrun (this is the only way
213      * that overruns are checked!) <code>it</code> may be also null,
214      * signifying an empty list.
215      */

216     static int format(Item it, int lmargin, int pos, int rmargin, int fin,
217                   boolean can_break, boolean nofail) throws Overrun {
218     if (!nofail && pos > rmargin) { // overrun
219
throw new Overrun(pos - rmargin);
220     }
221     if (it == null) { // no items to format. Check against final position.
222
if (!nofail && pos > fin) throw new Overrun(pos - fin);
223         return pos;
224     }
225     return it.formatN(lmargin, pos, rmargin, fin, can_break, nofail);
226     }
227 }
228
229 /**
230  * A Block is a formatting unit containing a list of other items
231  * to be formatted.
232  */

233 class Block extends Item {
234     Block parent;
235     Item first;
236     Item last;
237     int indent;
238         
239     Block(Block parent_, int indent_) {
240         parent = parent_;
241         first = last = null;
242         indent = indent_;
243     }
244         
245     /**
246      * Add a new item to the end of the block. Successive
247      * StringItems are concatenated together to limit recursion
248      * depth when formatting.
249      */

250     void add(Item it) {
251         if (first == null) {
252         first = it;
253     } else {
254         if (it instanceof StringItem && last instanceof StringItem) {
255         StringItem lasts = (StringItem)last;
256         lasts.appendString(((StringItem)it).s);
257         return;
258         } else {
259         last.next = it;
260         }
261     }
262         last = it;
263     }
264
265 int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
266             boolean nofail) throws Overrun {
267         // "this_fin" is a final-position bound for the formatting of
268
// the contained list, cranked in from the right margin
269
// when subsequent items overrun.
270
int this_fin = rmargin;
271         // Keep track of whether to send "nofail" down to contained
272
// list. Don't do this unless forced to.
273
boolean this_nofail = false;
274         // Keep track of whether to send "can_break" down to contained
275
// list. Don't do this unless forced to.
276
boolean this_break = false;
277     while (true) {
278         int next_pos;
279         try {
280           next_pos = format(first, pos + indent, pos, rmargin,
281                   this_fin, this_break, this_nofail && this_break);
282         } catch (Overrun o) {
283         if (!can_break) throw o;
284         if (!this_break) { this_break = true; continue; }
285         if (nofail) { this_nofail = true; continue; }
286         throw o;
287         }
288         try {
289         return format(next, lmargin, next_pos, rmargin, fin,
290                   can_break, nofail);
291         } catch (Overrun o) {
292         if (!can_break) throw o; // no way to fix it
293
if (next instanceof AllowBreak) throw o; // not our fault
294
this_break = true;
295         if (next_pos > this_fin) next_pos = this_fin;
296         this_fin = next_pos - o.amount;
297         }
298     }
299     }
300
301     int sendOutput(Writer JavaDoc o, int lmargin, int pos) throws IOException JavaDoc {
302         Item it = first;
303         lmargin = pos+indent;
304         while (it != null) {
305             pos = it.sendOutput(o, lmargin, pos);
306             it = it.next;
307         }
308         return pos;
309     }
310
311     void free() {
312         super.free();
313   
314         parent = null;
315         if( first != null) {
316             first.free();
317         }
318         last = null;
319     }
320 }
321
322 class StringItem extends Item {
323     String JavaDoc s;
324     StringItem(String JavaDoc s_) { s = s_; }
325         
326     int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
327         boolean nofail) throws Overrun {
328     return format(next, lmargin, pos + s.length(), rmargin, fin,
329               can_break, nofail);
330     }
331     int sendOutput(Writer JavaDoc o, int lm, int pos) throws IOException JavaDoc {
332         o.write(s);
333         return pos + s.length();
334     }
335     void appendString(String JavaDoc s) { this.s = this.s + s; }
336 }
337
338 class AllowBreak extends Item
339 {
340     int indent;
341     boolean broken = true;
342     String JavaDoc alt;
343         
344     AllowBreak(int n_, String JavaDoc alt_) { indent = n_; alt = alt_; }
345         
346     int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
347             boolean nofail) throws Overrun {
348         if (can_break) { pos = lmargin + indent; broken = true; }
349         else { pos += alt.length(); broken = false; }
350     return format(next, lmargin, pos, rmargin, fin, can_break, nofail);
351     }
352         
353     int sendOutput(Writer JavaDoc o, int lmargin, int pos)
354         throws IOException JavaDoc {
355         if (broken) {
356             o.write("\r\n");
357             for (int i = 0; i < lmargin + indent; i++) o.write(" ");
358             return lmargin + indent;
359         } else {
360         o.write(alt);
361             return pos + alt.length();
362         }
363     }
364 }
365
366 class Newline extends AllowBreak
367 {
368     Newline(int n_) { super(n_, ""); }
369         
370     int formatN(int lmargin, int pos, int rmargin, int fin, boolean can_break,
371         boolean nofail) throws Overrun {
372         broken = true;
373     if (!can_break) throw new Overrun(1);
374     return format(next, lmargin, lmargin + indent, rmargin, fin,
375             can_break, nofail);
376     }
377         
378     int sendOutput(Writer JavaDoc o, int lmargin, int pos)
379             throws IOException JavaDoc {
380         o.write("\r\n");
381         for (int i = 0; i < lmargin + indent; i++) o.write(" ");
382         return lmargin + indent;
383     }
384 }
385
Popular Tags