1 // Copyright 2007 all rights reserved; see LICENSE file for BSD-style license
3 package edu.berkeley.sbp.misc;
9 * This Reader inserts special characters into the stream to indicate
10 * when the indentation level has increased or decreased.<p>
12 * Lines are separated by LF characters (ASCII/Unicode 0x0A). The
13 * indentation of a line is defined to be the number of contiguous
14 * spaces (ASCII/Unicode 0x20). A blank line is a line consisting
15 * only of zero or more spaces. <p>
17 * If the indentation of the current line is <i>greater than</i> the
18 * indentation of the most recent non-blank line, the last space
19 * character of the current line's indentation will be <i>replaced
20 * by</i> an <tt>updent</tt> character. (FIXME not quite right)<p>
22 * If the indentation of the next non-blank line is <i>less than</i>
23 * the indentation of the current line, one or more <tt>downdent</tt>
24 * characters will be <i>inserted</i> immediately after the last
25 * non-whitespace character on the current line. <p>
27 * These rules have two goals:
29 * <ul> <li> Whitespace never appears immediately after an
30 * <tt>updent</tt> or immediately before a
31 * <tt>downdent</tt>. This simplifies grammars which are
32 * based on these characters.
34 * <li> Blank lines have no effect on the placement of
35 * <tt>updent</tt> and <tt>downdent</tt>.
37 * <li> Every non-space character from the original stream
38 * appears in the modified stream. Furthermore, these
39 * characters appear at exactly the same row and column as
40 * they did in the original stream. This simplifies
41 * debugging considerably.
44 public class IndentingReader extends Reader {
46 private final Reader r;
47 private final char updent;
48 private final char downdent;
50 public IndentingReader(Reader r, char updent, char downdent) {
53 this.downdent = downdent;
57 private boolean indenting = true;
58 private int indentation = 0;
59 private ArrayList<Integer> istack = new ArrayList<Integer>();
60 private ArrayList<Integer> blanks = new ArrayList<Integer>();
61 private int queuedIndentation = 0;
62 private int blankIndentation = 0;
64 private int _peek = -2;
65 public int _peek() throws IOException {
66 if (_peek == -2) _peek = r.read();
69 public int _read() throws IOException {
75 public int read() throws IOException {
78 if (i==-1 && istack.size() > 1) {
79 istack.remove(istack.size()-1);
88 blanks.add(indentation);
100 // more indentation to consume
101 if (c==' ') { indentation++; _read(); continue; }
103 // reached end of whitespace; line has non-whitespace material
104 int last = istack.size()==0 ? -1 : istack.get(istack.size()-1);
106 // emit any required close-braces
107 if (indentation < last) {
108 istack.remove(istack.size()-1);
112 // emit \n and any blank lines
113 if (blankIndentation > 0) {
117 if (blanks.size() > 0) {
118 blankIndentation = blanks.remove(blanks.size()-1);
122 if (queuedIndentation < indentation) {
125 // omit the last space of indentation if we're planning on emitting an open-brace
126 if (queuedIndentation < indentation || !(indentation > last))
131 if (indentation > last) {
132 istack.add(indentation);
136 // done with indentation
138 queuedIndentation = 0;
144 public int read(char[] buf, int off, int len) throws IOException {
149 buf[off] = (char)ret;
154 return (len>0 && numread==0) ? -1 : numread;
156 public void close() throws IOException { r.close(); }