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 org.apache.harmony.luni.util.Msg; 21 22/** 23 * Wraps an existing {@link Reader} and adds functionality to "push back" 24 * characters that have been read, so that they can be read again. Parsers may 25 * find this useful. The number of characters which may be pushed back can be 26 * specified during construction. If the buffer of pushed back bytes is empty, 27 * characters are read from the underlying reader. 28 */ 29public class PushbackReader extends FilterReader { 30 /** 31 * The {@code char} array containing the chars to read. 32 */ 33 char[] buf; 34 35 /** 36 * The current position within the char array {@code buf}. A value 37 * equal to buf.length indicates no chars available. A value of 0 indicates 38 * the buffer is full. 39 */ 40 int pos; 41 42 /** 43 * Constructs a new {@code PushbackReader} with the specified reader as 44 * source. The size of the pushback buffer is set to the default value of 1 45 * character. 46 * 47 * @param in 48 * the source reader. 49 */ 50 public PushbackReader(Reader in) { 51 super(in); 52 buf = new char[1]; 53 pos = 1; 54 } 55 56 /** 57 * Constructs a new {@code PushbackReader} with {@code in} as source reader. 58 * The size of the pushback buffer is set to {@code size}. 59 * 60 * @param in 61 * the source reader. 62 * @param size 63 * the size of the pushback buffer. 64 * @throws IllegalArgumentException 65 * if {@code size} is negative. 66 */ 67 public PushbackReader(Reader in, int size) { 68 super(in); 69 if (size <= 0) { 70 throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$ 71 } 72 buf = new char[size]; 73 pos = size; 74 } 75 76 /** 77 * Closes this reader. This implementation closes the source reader 78 * and releases the pushback buffer. 79 * 80 * @throws IOException 81 * if an error occurs while closing this reader. 82 */ 83 @Override 84 public void close() throws IOException { 85 synchronized (lock) { 86 buf = null; 87 in.close(); 88 } 89 } 90 91 /** 92 * Marks the current position in this stream. Setting a mark is not 93 * supported in this class; this implementation always throws an 94 * {@code IOException}. 95 * 96 * @param readAheadLimit 97 * the number of character that can be read from this reader 98 * before the mark is invalidated; this parameter is ignored. 99 * @throws IOException 100 * if this method is called. 101 */ 102 @Override 103 public void mark(int readAheadLimit) throws IOException { 104 throw new IOException(Msg.getString("K007f")); //$NON-NLS-1$ 105 } 106 107 /** 108 * Indicates whether this reader supports the {@code mark(int)} and 109 * {@code reset()} methods. {@code PushbackReader} does not support them, so 110 * it returns {@code false}. 111 * 112 * @return always {@code false}. 113 * @see #mark(int) 114 * @see #reset() 115 */ 116 @Override 117 public boolean markSupported() { 118 return false; 119 } 120 121 /** 122 * Reads a single character from this reader and returns it as an integer 123 * with the two higher-order bytes set to 0. Returns -1 if the end of the 124 * reader has been reached. If the pushback buffer does not contain any 125 * available characters then a character from the source reader is returned. 126 * Blocks until one character has been read, the end of the source reader is 127 * detected or an exception is thrown. 128 * 129 * @return the character read or -1 if the end of the source reader has been 130 * reached. 131 * @throws IOException 132 * if this reader is closed or an I/O error occurs while reading 133 * from this reader. 134 */ 135 @Override 136 public int read() throws IOException { 137 synchronized (lock) { 138 if (buf == null) { 139 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 140 } 141 /* Is there a pushback character available? */ 142 if (pos < buf.length) { 143 return buf[pos++]; 144 } 145 /** 146 * Assume read() in the InputStream will return 2 lowest-order bytes 147 * or -1 if end of stream. 148 */ 149 return in.read(); 150 } 151 } 152 153 /** 154 * Reads at most {@code length} bytes from this reader and stores them in 155 * byte array {@code buffer} starting at {@code offset}. Characters are 156 * read from the pushback buffer first, then from the source reader if more 157 * bytes are required. Blocks until {@code count} characters have been read, 158 * the end of the source reader is detected or an exception is thrown. 159 * 160 * @param buffer 161 * the array in which to store the characters read from this 162 * reader. 163 * @param offset 164 * the initial position in {@code buffer} to store the characters 165 * read from this reader. 166 * @param count 167 * the maximum number of bytes to store in {@code buffer}. 168 * @return the number of bytes read or -1 if the end of the source reader 169 * has been reached. 170 * @throws IndexOutOfBoundsException 171 * if {@code offset < 0} or {@code count < 0}, or if 172 * {@code offset + count} is greater than the length of 173 * {@code buffer}. 174 * @throws IOException 175 * if this reader is closed or another I/O error occurs while 176 * reading from this reader. 177 */ 178 @Override 179 public int read(char[] buffer, int offset, int count) throws IOException { 180 synchronized (lock) { 181 if (null == buf) { 182 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 183 } 184 // avoid int overflow 185 // BEGIN android-changed 186 // Exception priorities (in case of multiple errors) differ from 187 // RI, but are spec-compliant. 188 // made implicit null check explicit, used (offset | count) < 0 189 // instead of (offset < 0) || (count < 0) to safe one operation 190 if (buffer == null) { 191 throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$ 192 } 193 if ((offset | count) < 0 || offset > buffer.length - count) { 194 throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ 195 } 196 // END android-changed 197 198 int copiedChars = 0; 199 int copyLength = 0; 200 int newOffset = offset; 201 /* Are there pushback chars available? */ 202 if (pos < buf.length) { 203 copyLength = (buf.length - pos >= count) ? count : buf.length 204 - pos; 205 System.arraycopy(buf, pos, buffer, newOffset, copyLength); 206 newOffset += copyLength; 207 copiedChars += copyLength; 208 /* Use up the chars in the local buffer */ 209 pos += copyLength; 210 } 211 /* Have we copied enough? */ 212 if (copyLength == count) { 213 return count; 214 } 215 int inCopied = in.read(buffer, newOffset, count - copiedChars); 216 if (inCopied > 0) { 217 return inCopied + copiedChars; 218 } 219 if (copiedChars == 0) { 220 return inCopied; 221 } 222 return copiedChars; 223 } 224 } 225 226 /** 227 * Indicates whether this reader is ready to be read without blocking. 228 * Returns {@code true} if this reader will not block when {@code read} is 229 * called, {@code false} if unknown or blocking will occur. 230 * 231 * @return {@code true} if the receiver will not block when 232 * {@code read()} is called, {@code false} if unknown 233 * or blocking will occur. 234 * @throws IOException 235 * if this reader is closed or some other I/O error occurs. 236 * @see #read() 237 * @see #read(char[], int, int) 238 */ 239 @Override 240 public boolean ready() throws IOException { 241 synchronized (lock) { 242 if (buf == null) { 243 throw new IOException(Msg.getString("K0080")); //$NON-NLS-1$ 244 } 245 return (buf.length - pos > 0 || in.ready()); 246 } 247 } 248 249 /** 250 * Resets this reader to the last marked position. Resetting the reader is 251 * not supported in this class; this implementation always throws an 252 * {@code IOException}. 253 * 254 * @throws IOException 255 * if this method is called. 256 */ 257 @Override 258 public void reset() throws IOException { 259 throw new IOException(Msg.getString("K007f")); //$NON-NLS-1$ 260 } 261 262 /** 263 * Pushes all the characters in {@code buffer} back to this reader. The 264 * characters are pushed back in such a way that the next character read 265 * from this reader is buffer[0], then buffer[1] and so on. 266 * <p> 267 * If this reader's internal pushback buffer cannot store the entire 268 * contents of {@code buffer}, an {@code IOException} is thrown. Parts of 269 * {@code buffer} may have already been copied to the pushback buffer when 270 * the exception is thrown. 271 * 272 * @param buffer 273 * the buffer containing the characters to push back to this 274 * reader. 275 * @throws IOException 276 * if this reader is closed or the free space in the internal 277 * pushback buffer is not sufficient to store the contents of 278 * {@code buffer}. 279 */ 280 public void unread(char[] buffer) throws IOException { 281 unread(buffer, 0, buffer.length); 282 } 283 284 /** 285 * Pushes a subset of the characters in {@code buffer} back to this reader. 286 * The subset is defined by the start position {@code offset} within 287 * {@code buffer} and the number of characters specified by {@code length}. 288 * The bytes are pushed back in such a way that the next byte read from this 289 * stream is {@code buffer[offset]}, then {@code buffer[1]} and so on. 290 * <p> 291 * If this stream's internal pushback buffer cannot store the selected 292 * subset of {@code buffer}, an {@code IOException} is thrown. Parts of 293 * {@code buffer} may have already been copied to the pushback buffer when 294 * the exception is thrown. 295 * 296 * @param buffer 297 * the buffer containing the characters to push back to this 298 * reader. 299 * @param offset 300 * the index of the first byte in {@code buffer} to push back. 301 * @param length 302 * the number of bytes to push back. 303 * @throws IndexOutOfBoundsException 304 * if {@code offset < 0} or {@code count < 0}, or if 305 * {@code offset + count} is greater than the length of 306 * {@code buffer}. 307 * @throws IOException 308 * if this reader is closed or the free space in the internal 309 * pushback buffer is not sufficient to store the selected 310 * contents of {@code buffer}. 311 * @throws NullPointerException 312 * if {@code buffer} is {@code null}. 313 */ 314 public void unread(char[] buffer, int offset, int length) throws IOException { 315 synchronized (lock) { 316 if (buf == null) { 317 // K0059=Stream is closed 318 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 319 } 320 if (length > pos) { 321 // K007e=Pushback buffer full 322 throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$ 323 } 324 // Force buffer null check first! 325 if (offset > buffer.length - length || offset < 0) { 326 // K002e=Offset out of bounds \: {0} 327 throw new ArrayIndexOutOfBoundsException(Msg.getString("K002e", offset)); //$NON-NLS-1$ 328 } 329 if (length < 0) { 330 // K0031=Length out of bounds \: {0} 331 throw new ArrayIndexOutOfBoundsException(Msg.getString("K0031", length)); //$NON-NLS-1$ 332 } 333 334 for (int i = offset + length - 1; i >= offset; i--) { 335 unread(buffer[i]); 336 } 337 } 338 } 339 340 /** 341 * Pushes the specified character {@code oneChar} back to this reader. This 342 * is done in such a way that the next character read from this reader is 343 * {@code (char) oneChar}. 344 * <p> 345 * If this reader's internal pushback buffer cannot store the character, an 346 * {@code IOException} is thrown. 347 * 348 * @param oneChar 349 * the character to push back to this stream. 350 * @throws IOException 351 * if this reader is closed or the internal pushback buffer is 352 * full. 353 */ 354 public void unread(int oneChar) throws IOException { 355 synchronized (lock) { 356 if (buf == null) { 357 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 358 } 359 if (pos == 0) { 360 throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$ 361 } 362 buf[--pos] = (char) oneChar; 363 } 364 } 365 366 /** 367 * Skips {@code count} characters in this reader. This implementation skips 368 * characters in the pushback buffer first and then in the source reader if 369 * necessary. 370 * 371 * @param count 372 * the number of characters to skip. 373 * @return the number of characters actually skipped. 374 * @throws IllegalArgumentException if {@code count < 0}. 375 * @throws IOException 376 * if this reader is closed or another I/O error occurs. 377 */ 378 @Override 379 public long skip(long count) throws IOException { 380 if (count < 0) { 381 throw new IllegalArgumentException(); 382 } 383 synchronized (lock) { 384 if (buf == null) { 385 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 386 } 387 if (count == 0) { 388 return 0; 389 } 390 long inSkipped; 391 int availableFromBuffer = buf.length - pos; 392 if (availableFromBuffer > 0) { 393 long requiredFromIn = count - availableFromBuffer; 394 if (requiredFromIn <= 0) { 395 pos += count; 396 return count; 397 } 398 pos += availableFromBuffer; 399 inSkipped = in.skip(requiredFromIn); 400 } else { 401 inSkipped = in.skip(count); 402 } 403 return inSkipped + availableFromBuffer; 404 } 405 } 406} 407