code: /**
   * Copyright (c) Rich Hickey. All rights reserved.
   * The use and distribution terms for this software are covered by the
   * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
   * which can be found in the file epl-v10.html at the root of this distribution.
   * By using this software in any fashion, you are agreeing to be bound by
   * the terms of this license.
   * You must not remove this notice, or any other, from this software.
   */
  
  package clojure.lang;
  
  import java.io.PushbackReader;
  import java.io.Reader;
  import java.io.LineNumberReader;
  import java.io.IOException;
  
  
  public class LineNumberingPushbackReader extends PushbackReader{
  
  // This class is a PushbackReader that wraps a LineNumberReader. The code
  // here to handle line terminators only mentions '\n' because
  // LineNumberReader collapses all occurrences of CR, LF, and CRLF into a
  // single '\n'.
  
  private static final int newline = (int) '\n';
  
  private boolean _atLineStart = true;
  private boolean _prev;
  private int _columnNumber = 1;
  private StringBuilder sb = null;
  
  public LineNumberingPushbackReader(Reader r){
  	super(new LineNumberReader(r));
  }
  
  public LineNumberingPushbackReader(Reader r, int size){
  	super(new LineNumberReader(r, size));
  }
  
  public int getLineNumber(){
  	return ((LineNumberReader) in).getLineNumber() + 1;
  }
  
  public void setLineNumber(int line) { ((LineNumberReader) in).setLineNumber(line - 1); }
  
  public void captureString(){
      this.sb = new StringBuilder();
  }
  
  public String getString(){
      if(sb != null)
          {
          String ret = sb.toString();
          sb = null;
          return ret;
          }
      return null;
  }
  
  public int getColumnNumber(){
  	return _columnNumber;
  }
  
  public int read() throws IOException{
      int c = super.read();
      _prev = _atLineStart;
      if((c == newline) || (c == -1))
          {
          _atLineStart = true;
          _columnNumber = 1;
          }
      else
          {
          _atLineStart = false;
          _columnNumber++;
          }
      if(sb != null && c != -1)
          sb.append((char)c);
      return c;
  }
  
  public void unread(int c) throws IOException{
      super.unread(c);
      _atLineStart = _prev;
      _columnNumber--;
      if(sb != null)
          sb.deleteCharAt(sb.length()-1);
  }
  
  public String readLine() throws IOException{
      int c = read();
      String line;
      switch (c) {
      case -1:
          line = null;
          break;
      case newline:
          line = "";
          break;
      default:
          String first = String.valueOf((char) c);
          String rest = ((LineNumberReader)in).readLine();
          if (sb != null)
            sb.append(rest+"\n");
          line = (rest == null) ? first : first + rest;
          _prev = false;
          _atLineStart = true;
          _columnNumber = 1;
          break;
      }
      return line;
  }
  
  public boolean atLineStart(){
      return _atLineStart;
  }
  }
  