Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package java.io;
     27 
     28 
     29 /**
     30  * A buffered character-input stream that keeps track of line numbers.  This
     31  * class defines methods {@link #setLineNumber(int)} and {@link
     32  * #getLineNumber()} for setting and getting the current line number
     33  * respectively.
     34  *
     35  * <p> By default, line numbering begins at 0. This number increments at every
     36  * <a href="#lt">line terminator</a> as the data is read, and can be changed
     37  * with a call to <tt>setLineNumber(int)</tt>.  Note however, that
     38  * <tt>setLineNumber(int)</tt> does not actually change the current position in
     39  * the stream; it only changes the value that will be returned by
     40  * <tt>getLineNumber()</tt>.
     41  *
     42  * <p> A line is considered to be <a name="lt">terminated</a> by any one of a
     43  * line feed ('\n'), a carriage return ('\r'), or a carriage return followed
     44  * immediately by a linefeed.
     45  *
     46  * @author      Mark Reinhold
     47  * @since       JDK1.1
     48  */
     49 
     50 public class LineNumberReader extends BufferedReader {
     51 
     52     /** The current line number */
     53     private int lineNumber = 0;
     54 
     55     /** The line number of the mark, if any */
     56     private int markedLineNumber; // Defaults to 0
     57 
     58     /** If the next character is a line feed, skip it */
     59     private boolean skipLF;
     60 
     61     /** The skipLF flag when the mark was set */
     62     private boolean markedSkipLF;
     63 
     64     /**
     65      * Create a new line-numbering reader, using the default input-buffer
     66      * size.
     67      *
     68      * @param  in
     69      *         A Reader object to provide the underlying stream
     70      */
     71     public LineNumberReader(Reader in) {
     72         super(in);
     73     }
     74 
     75     /**
     76      * Create a new line-numbering reader, reading characters into a buffer of
     77      * the given size.
     78      *
     79      * @param  in
     80      *         A Reader object to provide the underlying stream
     81      *
     82      * @param  sz
     83      *         An int specifying the size of the buffer
     84      */
     85     public LineNumberReader(Reader in, int sz) {
     86         super(in, sz);
     87     }
     88 
     89     /**
     90      * Set the current line number.
     91      *
     92      * @param  lineNumber
     93      *         An int specifying the line number
     94      *
     95      * @see #getLineNumber
     96      */
     97     public void setLineNumber(int lineNumber) {
     98         this.lineNumber = lineNumber;
     99     }
    100 
    101     /**
    102      * Get the current line number.
    103      *
    104      * @return  The current line number
    105      *
    106      * @see #setLineNumber
    107      */
    108     public int getLineNumber() {
    109         return lineNumber;
    110     }
    111 
    112     /**
    113      * Read a single character.  <a href="#lt">Line terminators</a> are
    114      * compressed into single newline ('\n') characters.  Whenever a line
    115      * terminator is read the current line number is incremented.
    116      *
    117      * @return  The character read, or -1 if the end of the stream has been
    118      *          reached
    119      *
    120      * @throws  IOException
    121      *          If an I/O error occurs
    122      */
    123     public int read() throws IOException {
    124         synchronized (lock) {
    125             int c = super.read();
    126             if (skipLF) {
    127                 if (c == '\n')
    128                     c = super.read();
    129                 skipLF = false;
    130             }
    131             switch (c) {
    132             case '\r':
    133                 skipLF = true;
    134             case '\n':          /* Fall through */
    135                 lineNumber++;
    136                 return '\n';
    137             }
    138             return c;
    139         }
    140     }
    141 
    142     /**
    143      * Read characters into a portion of an array.  Whenever a <a
    144      * href="#lt">line terminator</a> is read the current line number is
    145      * incremented.
    146      *
    147      * @param  cbuf
    148      *         Destination buffer
    149      *
    150      * @param  off
    151      *         Offset at which to start storing characters
    152      *
    153      * @param  len
    154      *         Maximum number of characters to read
    155      *
    156      * @return  The number of bytes read, or -1 if the end of the stream has
    157      *          already been reached
    158      *
    159      * @throws  IOException
    160      *          If an I/O error occurs
    161      */
    162     public int read(char cbuf[], int off, int len) throws IOException {
    163         synchronized (lock) {
    164             int n = super.read(cbuf, off, len);
    165 
    166             for (int i = off; i < off + n; i++) {
    167                 int c = cbuf[i];
    168                 if (skipLF) {
    169                     skipLF = false;
    170                     if (c == '\n')
    171                         continue;
    172                 }
    173                 switch (c) {
    174                 case '\r':
    175                     skipLF = true;
    176                 case '\n':      /* Fall through */
    177                     lineNumber++;
    178                     break;
    179                 }
    180             }
    181 
    182             return n;
    183         }
    184     }
    185 
    186     /**
    187      * Read a line of text.  Whenever a <a href="#lt">line terminator</a> is
    188      * read the current line number is incremented.
    189      *
    190      * @return  A String containing the contents of the line, not including
    191      *          any <a href="#lt">line termination characters</a>, or
    192      *          <tt>null</tt> if the end of the stream has been reached
    193      *
    194      * @throws  IOException
    195      *          If an I/O error occurs
    196      */
    197     public String readLine() throws IOException {
    198         synchronized (lock) {
    199             String l = super.readLine(skipLF);
    200             skipLF = false;
    201             if (l != null)
    202                 lineNumber++;
    203             return l;
    204         }
    205     }
    206 
    207     /** Maximum skip-buffer size */
    208     private static final int maxSkipBufferSize = 8192;
    209 
    210     /** Skip buffer, null until allocated */
    211     private char skipBuffer[] = null;
    212 
    213     /**
    214      * Skip characters.
    215      *
    216      * @param  n
    217      *         The number of characters to skip
    218      *
    219      * @return  The number of characters actually skipped
    220      *
    221      * @throws  IOException
    222      *          If an I/O error occurs
    223      *
    224      * @throws  IllegalArgumentException
    225      *          If <tt>n</tt> is negative
    226      */
    227     public long skip(long n) throws IOException {
    228         if (n < 0)
    229             throw new IllegalArgumentException("skip() value is negative");
    230         int nn = (int) Math.min(n, maxSkipBufferSize);
    231         synchronized (lock) {
    232             if ((skipBuffer == null) || (skipBuffer.length < nn))
    233                 skipBuffer = new char[nn];
    234             long r = n;
    235             while (r > 0) {
    236                 int nc = read(skipBuffer, 0, (int) Math.min(r, nn));
    237                 if (nc == -1)
    238                     break;
    239                 r -= nc;
    240             }
    241             return n - r;
    242         }
    243     }
    244 
    245     /**
    246      * Mark the present position in the stream.  Subsequent calls to reset()
    247      * will attempt to reposition the stream to this point, and will also reset
    248      * the line number appropriately.
    249      *
    250      * @param  readAheadLimit
    251      *         Limit on the number of characters that may be read while still
    252      *         preserving the mark.  After reading this many characters,
    253      *         attempting to reset the stream may fail.
    254      *
    255      * @throws  IOException
    256      *          If an I/O error occurs
    257      */
    258     public void mark(int readAheadLimit) throws IOException {
    259         synchronized (lock) {
    260             super.mark(readAheadLimit);
    261             markedLineNumber = lineNumber;
    262             markedSkipLF     = skipLF;
    263         }
    264     }
    265 
    266     /**
    267      * Reset the stream to the most recent mark.
    268      *
    269      * @throws  IOException
    270      *          If the stream has not been marked, or if the mark has been
    271      *          invalidated
    272      */
    273     public void reset() throws IOException {
    274         synchronized (lock) {
    275             super.reset();
    276             lineNumber = markedLineNumber;
    277             skipLF     = markedSkipLF;
    278         }
    279     }
    280 
    281 }
    282