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 18package java.io; 19 20import java.util.Arrays; 21import 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} 31 */ 32@Deprecated 33public 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 at most {@code length} bytes from the filtered stream and stores 142 * them in the byte array {@code buffer} starting at {@code offset}. 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 * <p> 146 * 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 * @param buffer 152 * the array in which to store the bytes read. 153 * @param offset 154 * the initial position in {@code buffer} to store the bytes read 155 * from this stream. 156 * @param length 157 * the maximum number of bytes to store in {@code buffer}. 158 * @return the number of bytes actually read or -1 if the end of the 159 * filtered stream has been reached while reading. 160 * @throws IndexOutOfBoundsException 161 * if {@code offset < 0} or {@code length < 0}, or if 162 * {@code offset + length} is greater than the length of 163 * {@code buffer}. 164 * @throws IOException 165 * if this stream is closed or another IOException occurs. 166 * @throws NullPointerException 167 * if {@code buffer} is {@code null}. 168 */ 169 @Override 170 public int read(byte[] buffer, int offset, int length) throws IOException { 171 Arrays.checkOffsetAndCount(buffer.length, offset, length); 172 for (int i = 0; i < length; i++) { 173 int currentChar; 174 try { 175 currentChar = read(); 176 } catch (IOException e) { 177 if (i != 0) { 178 return i; 179 } 180 throw e; 181 } 182 if (currentChar == -1) { 183 return i == 0 ? -1 : i; 184 } 185 buffer[offset + i] = (byte) currentChar; 186 } 187 return length; 188 } 189 190 /** 191 * Resets this stream to the last marked location. It also resets the line 192 * count to what is was when this stream was marked. 193 * 194 * @throws IOException 195 * if this stream is already closed, no mark has been set or the 196 * mark is no longer valid because more than {@code readlimit} 197 * bytes have been read since setting the mark. 198 * @see #mark(int) 199 * @see #markSupported() 200 */ 201 @Override 202 public void reset() throws IOException { 203 in.reset(); 204 lineNumber = markedLineNumber; 205 lastChar = markedLastChar; 206 } 207 208 /** 209 * Sets the line number of this stream to the specified 210 * {@code lineNumber}. Note that this may have side effects on the 211 * line number associated with the last marked position. 212 * 213 * @param lineNumber 214 * the new lineNumber value. 215 * @see #mark(int) 216 * @see #reset() 217 */ 218 public void setLineNumber(int lineNumber) { 219 this.lineNumber = lineNumber; 220 } 221 222 /** 223 * Skips {@code count} number of bytes in this stream. Subsequent 224 * calls to {@code read} will not return these bytes unless {@code reset} is 225 * used. This implementation skips {@code byteCount} bytes in the 226 * filtered stream and increments the line number count whenever line 227 * terminator sequences are skipped. 228 * 229 * @param byteCount 230 * the number of bytes to skip. 231 * @return the number of bytes actually skipped. 232 * @throws IOException 233 * if this stream is closed or another IOException occurs. 234 * @see #mark(int) 235 * @see #read() 236 * @see #reset() 237 */ 238 @Override 239 public long skip(long byteCount) throws IOException { 240 return Streams.skipByReading(this, byteCount); 241 } 242} 243