From: adam Date: Fri, 20 Apr 2007 02:58:19 +0000 (-0400) Subject: add IndentingReader X-Git-Url: http://git.megacz.com/?p=sbp.git;a=commitdiff_plain;h=83bcf9559e9338c906b3c262ce7a1ecca7ffe3ed add IndentingReader darcs-hash:20070420025819-5007d-7325ca7dfce9eb76f0bdcf219a129f33989a7e70.gz --- diff --git a/src/edu/berkeley/sbp/misc/IndentingReader.java b/src/edu/berkeley/sbp/misc/IndentingReader.java new file mode 100644 index 0000000..bd80aad --- /dev/null +++ b/src/edu/berkeley/sbp/misc/IndentingReader.java @@ -0,0 +1,158 @@ +// Copyright 2007 all rights reserved; see LICENSE file for BSD-style license + +package edu.berkeley.sbp.misc; + +import java.util.*; +import java.io.*; + +/** + * This Reader inserts special characters into the stream to indicate + * when the indentation level has increased or decreased.

+ * + * Lines are separated by LF characters (ASCII/Unicode 0x0A). The + * indentation of a line is defined to be the number of contiguous + * spaces (ASCII/Unicode 0x20). A blank line is a line consisting + * only of zero or more spaces.

+ * + * If the indentation of the current line is greater than the + * indentation of the most recent non-blank line, the last space + * character of the current line's indentation will be replaced + * by an updent character. (FIXME not quite right)

+ * + * If the indentation of the next non-blank line is less than + * the indentation of the current line, one or more downdent + * characters will be inserted immediately after the last + * non-whitespace character on the current line.

+ * + * These rules have two goals: + * + *

+ */ +public class IndentingReader extends Reader { + + private final Reader r; + private final char updent; + private final char downdent; + + public IndentingReader(Reader r, char updent, char downdent) { + this.r = r; + this.updent = updent; + this.downdent = downdent; + istack.add(0); + } + + private boolean indenting = true; + private int indentation = 0; + private ArrayList istack = new ArrayList(); + private ArrayList blanks = new ArrayList(); + private int queuedIndentation = 0; + private int blankIndentation = 0; + + private int _peek = -2; + public int _peek() throws IOException { + if (_peek == -2) _peek = r.read(); + return _peek; + } + public int _read() throws IOException { + int ret = _peek(); + _peek = -2; + return ret; + } + + public int read() throws IOException { + while(true) { + int i = _peek(); + if (i==-1 && istack.size() > 1) { + istack.remove(istack.size()-1); + return downdent; + } + if (i==-1) return -1; + + char c = (char)i; + + if (c=='\n') { + if (indenting) { + blanks.add(indentation); + } else { + blanks.add(0); + } + indenting=true; + indentation = 0; + _read(); + continue; + } + + if (indenting) { + + // more indentation to consume + if (c==' ') { indentation++; _read(); continue; } + + // reached end of whitespace; line has non-whitespace material + int last = istack.size()==0 ? -1 : istack.get(istack.size()-1); + + // emit any required close-braces + if (indentation < last) { + istack.remove(istack.size()-1); + return downdent; + } + + // emit \n and any blank lines + if (blankIndentation > 0) { + blankIndentation--; + return ' '; + } + if (blanks.size() > 0) { + blankIndentation = blanks.remove(blanks.size()-1); + return '\n'; + } + + if (queuedIndentation < indentation) { + queuedIndentation++; + + // omit the last space of indentation if we're planning on emitting an open-brace + if (queuedIndentation < indentation || !(indentation > last)) + return ' '; + } + + // emit open-brace + if (indentation > last) { + istack.add(indentation); + return updent; + } + + // done with indentation + indenting = false; + queuedIndentation = 0; + } + return _read(); + } + } + + public int read(char[] buf, int off, int len) throws IOException { + int numread = 0; + while(len>0) { + int ret = read(); + if (ret==-1) break; + buf[off] = (char)ret; + off++; + len--; + numread++; + } + return (len>0 && numread==0) ? -1 : numread; + } + public void close() throws IOException { r.close(); } +} +