Home | History | Annotate | Download | only in io
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.io;
     19 
     20 import java.util.Arrays;
     21 import libcore.io.Streams;
     22 
     23 /**
     24  * Wraps an existing {@link InputStream} and counts the line terminators
     25  * encountered while reading the data. Line numbering starts at 0. Recognized
     26  * line terminator sequences are {@code '\r'}, {@code '\n'} and {@code "\r\n"}.
     27  * When using {@code read}, line terminator sequences are always translated into
     28  * {@code '\n'}.
     29  *
     30  * @deprecated Use {@link LineNumberReader} instead.
     31  */
     32 @Deprecated
     33 public class LineNumberInputStream extends FilterInputStream {
     34 
     35     private int lineNumber;
     36 
     37     private int markedLineNumber = -1;
     38 
     39     private int lastChar = -1;
     40 
     41     private int markedLastChar;
     42 
     43     /**
     44      * Constructs a new {@code LineNumberInputStream} on the {@link InputStream}
     45      * {@code in}. Line numbers are counted for all data read from this stream.
     46      *
     47      * <p><strong>Warning:</strong> passing a null source creates an invalid
     48      * {@code LineNumberInputStream}. All operations on such a stream will fail.
     49      *
     50      * @param in
     51      *            The non-null input stream to count line numbers.
     52      */
     53     public LineNumberInputStream(InputStream in) {
     54         super(in);
     55     }
     56 
     57     /**
     58      * {@inheritDoc}
     59      *
     60      * <p>Note that the source stream may just be a sequence of {@code "\r\n"} bytes
     61      * which are converted into {@code '\n'} by this stream. Therefore,
     62      * {@code available} returns only {@code in.available() / 2} bytes as
     63      * result.
     64      */
     65     @Override
     66     public int available() throws IOException {
     67         return in.available() / 2 + (lastChar == -1 ? 0 : 1);
     68     }
     69 
     70     /**
     71      * Returns the current line number for this stream. Numbering starts at 0.
     72      *
     73      * @return the current line number.
     74      */
     75     public int getLineNumber() {
     76         return lineNumber;
     77     }
     78 
     79     /**
     80      * Sets a mark position in this stream. The parameter {@code readlimit}
     81      * indicates how many bytes can be read before the mark is invalidated.
     82      * Sending {@code reset()} will reposition this stream back to the marked
     83      * position, provided that {@code readlimit} has not been surpassed.
     84      * The line number count will also be reset to the last marked
     85      * line number count.
     86      * <p>
     87      * This implementation sets a mark in the filtered stream.
     88      *
     89      * @param readlimit
     90      *            the number of bytes that can be read from this stream before
     91      *            the mark is invalidated.
     92      * @see #markSupported()
     93      * @see #reset()
     94      */
     95     @Override
     96     public void mark(int readlimit) {
     97         in.mark(readlimit);
     98         markedLineNumber = lineNumber;
     99         markedLastChar = lastChar;
    100     }
    101 
    102     /**
    103      * Reads a single byte from the filtered stream and returns it as an integer
    104      * in the range from 0 to 255. Returns -1 if the end of this stream has been
    105      * reached.
    106      * <p>
    107      * The line number count is incremented if a line terminator is encountered.
    108      * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and
    109      * {@code "\r\n"}. Line terminator sequences are always translated into
    110      * {@code '\n'}.
    111      *
    112      * @return the byte read or -1 if the end of the filtered stream has been
    113      *         reached.
    114      * @throws IOException
    115      *             if the stream is closed or another IOException occurs.
    116      */
    117     @SuppressWarnings("fallthrough")
    118     @Override
    119     public int read() throws IOException {
    120         int currentChar = lastChar;
    121         if (currentChar == -1) {
    122             currentChar = in.read();
    123         } else {
    124             lastChar = -1;
    125         }
    126         switch (currentChar) {
    127             case '\r':
    128                 currentChar = '\n';
    129                 lastChar = in.read();
    130                 if (lastChar == '\n') {
    131                     lastChar = -1;
    132                 }
    133                 // fall through
    134             case '\n':
    135                 lineNumber++;
    136         }
    137         return currentChar;
    138     }
    139 
    140     /**
    141      * Reads up to {@code byteCount} bytes from the filtered stream and stores
    142      * them in the byte array {@code buffer} starting at {@code byteOffset}.
    143      * Returns the number of bytes actually read or -1 if no bytes have been
    144      * read and the end of this stream has been reached.
    145      *
    146      * <p>The line number count is incremented if a line terminator is encountered.
    147      * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and
    148      * {@code "\r\n"}. Line terminator sequences are always translated into
    149      * {@code '\n'}.
    150      *
    151      * @throws IndexOutOfBoundsException
    152      *     if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}.
    153      * @throws IOException
    154      *             if this stream is closed or another IOException occurs.
    155      * @throws NullPointerException
    156      *             if {@code buffer == null}.
    157      */
    158     @Override
    159     public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
    160         Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
    161         for (int i = 0; i < byteCount; ++i) {
    162             int currentChar;
    163             try {
    164                 currentChar = read();
    165             } catch (IOException e) {
    166                 if (i != 0) {
    167                     return i;
    168                 }
    169                 throw e;
    170             }
    171             if (currentChar == -1) {
    172                 return i == 0 ? -1 : i;
    173             }
    174             buffer[byteOffset + i] = (byte) currentChar;
    175         }
    176         return byteCount;
    177     }
    178 
    179     /**
    180      * Resets this stream to the last marked location. It also resets the line
    181      * count to what is was when this stream was marked.
    182      *
    183      * @throws IOException
    184      *             if this stream is already closed, no mark has been set or the
    185      *             mark is no longer valid because more than {@code readlimit}
    186      *             bytes have been read since setting the mark.
    187      * @see #mark(int)
    188      * @see #markSupported()
    189      */
    190     @Override
    191     public void reset() throws IOException {
    192         in.reset();
    193         lineNumber = markedLineNumber;
    194         lastChar = markedLastChar;
    195     }
    196 
    197     /**
    198      * Sets the line number of this stream to the specified
    199      * {@code lineNumber}. Note that this may have side effects on the
    200      * line number associated with the last marked position.
    201      *
    202      * @param lineNumber
    203      *            the new lineNumber value.
    204      * @see #mark(int)
    205      * @see #reset()
    206      */
    207     public void setLineNumber(int lineNumber) {
    208         this.lineNumber = lineNumber;
    209     }
    210 
    211     /**
    212      * Skips {@code count} number of bytes in this stream. Subsequent
    213      * calls to {@code read} will not return these bytes unless {@code reset} is
    214      * used. This implementation skips {@code byteCount} bytes in the
    215      * filtered stream and increments the line number count whenever line
    216      * terminator sequences are skipped.
    217      *
    218      * @param byteCount
    219      *            the number of bytes to skip.
    220      * @return the number of bytes actually skipped.
    221      * @throws IOException
    222      *             if this stream is closed or another IOException occurs.
    223      * @see #mark(int)
    224      * @see #read()
    225      * @see #reset()
    226      */
    227     @Override
    228     public long skip(long byteCount) throws IOException {
    229         return Streams.skipByReading(this, byteCount);
    230     }
    231 }
    232