//============================================================================== // tribble/io/CharInputStreamDfl.java //------------------------------------------------------------------------------ package tribble.io; // System imports import java.io.IOException; import java.io.Reader; import java.lang.String; // Local imports import tribble.io.CharInputStreamI; import tribble.io.DiagnosticOutputI; import tribble.io.DiagnosticWriterI; /******************************************************************************* * Default generic character input stream. * *

* This is a default implementation of an input stream that is capable of * reading a single character at a time. Such a stream can be used as the * underlying character input stream for classes implementing the {@link LexerI} * interface. * *

* This character stream handles various combinations of newline termination * characters (LF, CR, and CR/LF pairs), returning a single newline character * ('\n') for any given combination. * *

* This also handles formfeed (FF) and null (NUL) characters, treating them as * spaces (SP). * *

* Tab (HT) characters are replaced with one or more spaces, to align on tabbed * columns. By default, tabs are replaced with the appropriate number of spaces * so as to align on 8-character columns, but this can be changed to a different * width or disabled altogether. * * @version $Revision: 1.2 $ $Date: 2003/01/25 18:40:45 $ * @since 2001-05-18 * @author * David R. Tribble, * david@tribble.com. *
* Copyright * ©2001-2003 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source code * provided that the original copyright and authorship notices remain * intact. * * @see LexerI */ public class CharInputStreamDfl implements CharInputStreamI, DiagnosticWriterI { // Identification /** Revision information. */ static final String REV = "@(#)tribble/io/CharInputStreamDfl.java $Revision: 1.2 $ $Date: 2003/01/25 18:40:45 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constants /** End of file character code. */ public static final int EOF = -1; /** Default tab width. */ public static final int DFL_TABSIZE = 8; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected variables /** Input stream. */ protected Reader m_in; /** Diagnostic output stream. */ protected DiagnosticOutputI m_out; /** Pushed-back (unread) character. */ protected int m_ungetc = EOF; /** Column position (of the last character read). */ protected int m_colNo = 0; /** Tab (HT) column width. */ protected int m_tabSz = DFL_TABSIZE; /** Pending tab (HT) character replacement. */ protected int m_tabSp; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constructor methods /*************************************************************************** * Default constructor. * * @since 1.1, 2001-05-18 */ public CharInputStreamDfl() { // Do nothing } /*************************************************************************** * Constructor. * * @param in * The input stream from which to read characters. * * @since 1.1, 2001-05-18 */ public CharInputStreamDfl(Reader in) { // Establish the input stream setInput(in); } /*************************************************************************** * Constructor. * * @param in * The input stream from which to read characters. * * @param out * The diagnostic output stream to write to. * * @since 1.1, 2001-05-18 */ public CharInputStreamDfl(Reader in, DiagnosticOutputI out) { // Establish the input and output streams setInput(in); setOutput(out); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*************************************************************************** * Establish the output stream to which diagnostic messages (warning and * error messages) are written during parsing. * * @param out * The diagnostic output stream. * * @since 1.1, 2001-05-18 */ public void setOutput(DiagnosticOutputI out) //implements tribble.io.DiagnosticWriterI { // Establish the output stream m_out = out; } /*************************************************************************** * Establish the input stream from which to read characters. * * @param in * The input stream from which to read characters. * * @since 1.1, 2001-05-18 */ public void setInput(Reader in) //implements tribble.io.CharInputStreamI { // Establish the input stream m_in = in; } /*************************************************************************** * Close the input stream. * * @since 1.1, 2001-05-18 */ public void close() //implements tribble.io.CharInputStreamI { try { // Close the input stream if (m_in != null) m_in.close(); } catch (IOException ex) { // Ignored } // Disassociate the input stream from this character stream m_in = null; } /*************************************************************************** * Read the next character from the input stream. * *

* This character stream handles various combinations of newline termination * characters (LF, CR, and CR/LF pairs), returning a single newline * character ('\n') for any given combination. * *

* This stream also handles formfeed (FF) and null (NUL) characters, * replacing them with spaces (SP). * *

* Tab (HT) characters are replaced by one or more space (SP) characters, so * that the character following the tab is aligned on a tab-sized column. * Tab character replacement can be disabled by specifying a tab size of * zero (see {@link #setTabSize}). * * @return * The next character code read from the input stream, or -1 if there are no * more characters to read (i.e., the end of the input stream was reached). * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2001-05-18 */ public int readChar() throws IOException //implements tribble.io.CharInputStreamI { int ch; // Check for an open input stream if (m_in == null) return (EOF); // End of file // Check for a previously pushed-back character if (m_ungetc != EOF) { // Use the pushed-back character ch = m_ungetc; m_ungetc = EOF; } else if (m_tabSp > 0) { // Continue replacing a tab (HT) with spaces (SP) m_tabSp--; ch = ' '; m_colNo++; } else { // Read the next character from the input stream ch = m_in.read(); // May throw m_colNo++; } // Handle control characters if (ch < ' ') { if (ch == '\n') { // End of line: LF m_colNo = 0; } else if (ch == '\r') { // End of line: CR, check for CR/LF pair ch = m_in.read(); // May throw if (ch != '\n') m_ungetc = ch; ch = '\n'; m_colNo = 0; } else if (ch == '\t' && m_tabSz > 0) { // Tab (HT), replace with spaces (SP) m_tabSp = 8 - m_colNo%8; ch = ' '; } else if (ch == '\f') { // Formfeed (FF), treat as a space (SP) ch = ' '; } else if (ch == '\0') { // Null (NUL) character, treat as a space (SP) ch = ' '; } // else leave character as is } // Done return (ch); } /*************************************************************************** * Establish the tab (HT) column width. * *

* Setting a tab width of zero disables the replacing of tab (HT) characters * with spaces, i.e., tab characters are read as is. The default tab width * is 8. * * @param n * The tab column width. * * @since 1.1, 2001-06-02 */ public void setTabSize(int n) { m_tabSz = n; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected methods /*************************************************************************** * Finalization. * Closes the underlying output stream, if any. * * @since 1.1, 2001-05-18 */ protected synchronized void finalize() { // Close and disassociate the input stream close(); // Disassociate (but do not close) the output stream m_out = null; } } // End CharInputStreamDfl.java