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