| /* | |
| * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package java.io; | |
| import java.nio.CharBuffer; | |
| import java.nio.ReadOnlyBufferException; | |
| import java.util.List; | |
| import java.util.Objects; | |
| /** | |
| * Abstract class for reading character streams. The only methods that a | |
| * subclass must implement are read(char[], int, int) and close(). Most | |
| * subclasses, however, will override some of the methods defined here in order | |
| * to provide higher efficiency, additional functionality, or both. | |
| * | |
| * | |
| * @see BufferedReader | |
| * @see LineNumberReader | |
| * @see CharArrayReader | |
| * @see InputStreamReader | |
| * @see FileReader | |
| * @see FilterReader | |
| * @see PushbackReader | |
| * @see PipedReader | |
| * @see StringReader | |
| * @see Writer | |
| * | |
| * @author Mark Reinhold | |
| * @since 1.1 | |
| */ | |
| public abstract class Reader implements Readable, Closeable { | |
| private static final int TRANSFER_BUFFER_SIZE = 8192; | |
| /** | |
| * Returns a new {@code Reader} that reads no characters. The returned | |
| * stream is initially open. The stream is closed by calling the | |
| * {@code close()} method. Subsequent calls to {@code close()} have no | |
| * effect. | |
| * | |
| * <p> While the stream is open, the {@code read()}, {@code read(char[])}, | |
| * {@code read(char[], int, int)}, {@code read(CharBuffer)}, {@code | |
| * ready()}, {@code skip(long)}, and {@code transferTo()} methods all | |
| * behave as if end of stream has been reached. After the stream has been | |
| * closed, these methods all throw {@code IOException}. | |
| * | |
| * <p> The {@code markSupported()} method returns {@code false}. The | |
| * {@code mark()} and {@code reset()} methods throw an {@code IOException}. | |
| * | |
| * <p> The {@link #lock object} used to synchronize operations on the | |
| * returned {@code Reader} is not specified. | |
| * | |
| * @return a {@code Reader} which reads no characters | |
| * | |
| * @since 11 | |
| */ | |
| public static Reader nullReader() { | |
| return new Reader() { | |
| private volatile boolean closed; | |
| private void ensureOpen() throws IOException { | |
| if (closed) { | |
| throw new IOException("Stream closed"); | |
| } | |
| } | |
| @Override | |
| public int read() throws IOException { | |
| ensureOpen(); | |
| return -1; | |
| } | |
| @Override | |
| public int read(char[] cbuf, int off, int len) throws IOException { | |
| Objects.checkFromIndexSize(off, len, cbuf.length); | |
| ensureOpen(); | |
| if (len == 0) { | |
| return 0; | |
| } | |
| return -1; | |
| } | |
| @Override | |
| public int read(CharBuffer target) throws IOException { | |
| Objects.requireNonNull(target); | |
| ensureOpen(); | |
| if (target.hasRemaining()) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| @Override | |
| public boolean ready() throws IOException { | |
| ensureOpen(); | |
| return false; | |
| } | |
| @Override | |
| public long skip(long n) throws IOException { | |
| ensureOpen(); | |
| return 0L; | |
| } | |
| @Override | |
| public long transferTo(Writer out) throws IOException { | |
| Objects.requireNonNull(out); | |
| ensureOpen(); | |
| return 0L; | |
| } | |
| @Override | |
| public void close() { | |
| closed = true; | |
| } | |
| }; | |
| } | |
| /** | |
| * Returns a {@code Reader} that reads characters from a | |
| * {@code CharSequence}. The reader is initially open and reading starts at | |
| * the first character in the sequence. | |
| * | |
| * <p> The returned reader supports the {@link #mark mark()} and | |
| * {@link #reset reset()} operations. | |
| * | |
| * <p> The resulting reader is not safe for use by multiple | |
| * concurrent threads. If the reader is to be used by more than one | |
| * thread it should be controlled by appropriate synchronization. | |
| * | |
| * <p> If the sequence changes while the reader is open, e.g. the length | |
| * changes, the behavior is undefined. | |
| * | |
| * @param cs {@code CharSequence} providing the character stream. | |
| * @return a {@code Reader} which reads characters from {@code cs} | |
| * @throws NullPointerException if {@code cs} is {@code null} | |
| * | |
| * @since 24 | |
| */ | |
| public static Reader of(final CharSequence cs) { | |
| Objects.requireNonNull(cs); | |
| return new Reader() { | |
| private boolean isClosed; | |
| private int next = 0; | |
| private int mark = 0; | |
| /** Check to make sure that the stream has not been closed */ | |
| private void ensureOpen() throws IOException { | |
| if (isClosed) | |
| throw new IOException("Stream closed"); | |
| } | |
| @Override | |
| public int read() throws IOException { | |
| ensureOpen(); | |
| if (next >= cs.length()) | |
| return -1; | |
| return cs.charAt(next++); | |
| } | |
| @Override | |
| public int read(char[] cbuf, int off, int len) throws IOException { | |
| ensureOpen(); | |
| Objects.checkFromIndexSize(off, len, cbuf.length); | |
| if (len == 0) { | |
| return 0; | |
| } | |
| int length = cs.length(); | |
| if (next >= length) | |
| return -1; | |
| int n = Math.min(length - next, len); | |
| cs.getChars(next, next + n, cbuf, off); | |
| next += n; | |
| return n; | |
| } | |
| @Override | |
| public String readAllAsString() throws IOException { | |
| ensureOpen(); | |
| int len = cs.length(); | |
| String result = cs.subSequence(next, len).toString(); | |
| next += result.length(); | |
| return result; | |
| } | |
| @Override | |
| public List<String> readAllLines() throws IOException { | |
| return readAllAsString().lines().toList(); | |
| } | |
| @Override | |
| public long skip(long n) throws IOException { | |
| ensureOpen(); | |
| if (next >= cs.length()) | |
| return 0; | |
| // Bound skip by beginning and end of the source | |
| long r = Math.min(cs.length() - next, n); | |
| r = Math.max(-next, r); | |
| next += (int)r; | |
| return r; | |
| } | |
| @Override | |
| public boolean ready() throws IOException { | |
| ensureOpen(); | |
| return true; | |
| } | |
| @Override | |
| public boolean markSupported() { | |
| return true; | |
| } | |
| @Override | |
| public void mark(int readAheadLimit) throws IOException { | |
| if (readAheadLimit < 0){ | |
| throw new IllegalArgumentException("Read-ahead limit < 0"); | |
| } | |
| ensureOpen(); | |
| mark = next; | |
| } | |
| @Override | |
| public void reset() throws IOException { | |
| ensureOpen(); | |
| next = mark; | |
| } | |
| @Override | |
| public void close() { | |
| isClosed = true; | |
| } | |
| }; | |
| } | |
| /** | |
| * The object used to synchronize operations on this stream. For | |
| * efficiency, a character-stream object may use an object other than | |
| * itself to protect critical sections. A subclass should therefore use | |
| * the object in this field rather than {@code this} or a synchronized | |
| * method. | |
| */ | |
| protected Object lock; | |
| /** | |
| * Creates a new character-stream reader whose critical sections will | |
| * synchronize on the reader itself. | |
| */ | |
| protected Reader() { | |
| this.lock = this; | |
| } | |
| /** | |
| * Creates a new character-stream reader whose critical sections will | |
| * synchronize on the given object. | |
| * | |
| * @param lock The Object to synchronize on. | |
| */ | |
| protected Reader(Object lock) { | |
| if (lock == null) { | |
| throw new NullPointerException(); | |
| } | |
| this.lock = lock; | |
| } | |
| /** | |
| * Attempts to read characters into the specified character buffer. | |
| * The buffer is used as a repository of characters as-is: the only | |
| * changes made are the results of a put operation. No flipping or | |
| * rewinding of the buffer is performed. If the {@linkplain | |
| * java.nio.CharBuffer#length length} of the specified character | |
| * buffer is zero, then no characters will be read and zero will be | |
| * returned. | |
| * | |
| * @param target the buffer to read characters into | |
| * @return The number of characters added to the buffer, | |
| * possibly zero, or -1 if this source of characters is at its end | |
| * @throws IOException if an I/O error occurs | |
| * @throws NullPointerException if target is null | |
| * @throws java.nio.ReadOnlyBufferException if target is a read only buffer, | |
| * even if its length is zero | |
| * @since 1.5 | |
| */ | |
| public int read(CharBuffer target) throws IOException { | |
| if (target.isReadOnly()) | |
| throw new ReadOnlyBufferException(); | |
| int nread; | |
| if (target.hasArray()) { | |
| char[] cbuf = target.array(); | |
| int pos = target.position(); | |
| int rem = Math.max(target.limit() - pos, 0); | |
| int off = target.arrayOffset() + pos; | |
| nread = this.read(cbuf, off, rem); | |
| if (nread > 0) | |
| target.position(pos + nread); | |
| } else { | |
| int len = target.remaining(); | |
| char[] cbuf = new char[len]; | |
| nread = read(cbuf, 0, len); | |
| if (nread > 0) | |
| target.put(cbuf, 0, nread); | |
| } | |
| return nread; | |
| } | |
| /** | |
| * Reads a single character. This method will block until a character is | |
| * available, an I/O error occurs, or the end of the stream is reached. | |
| * | |
| * <p> Subclasses that intend to support efficient single-character input | |
| * should override this method. | |
| * | |
| * @return The character read, as an integer in the range 0 to 65535 | |
| * ({@code 0x00-0xffff}), or -1 if the end of the stream has | |
| * been reached | |
| * | |
| * @throws IOException If an I/O error occurs | |
| */ | |
| public int read() throws IOException { | |
| char[] cb = new char[1]; | |
| if (read(cb, 0, 1) == -1) | |
| return -1; | |
| else | |
| return cb[0]; | |
| } | |
| /** | |
| * Reads characters into an array. This method will block until some input | |
| * is available, an I/O error occurs, or the end of the stream is reached. | |
| * | |
| * <p> If the length of {@code cbuf} is zero, then no characters are read | |
| * and {@code 0} is returned; otherwise, there is an attempt to read at | |
| * least one character. If no character is available because the stream is | |
| * at its end, the value {@code -1} is returned; otherwise, at least one | |
| * character is read and stored into {@code cbuf}. | |
| * | |
| * @param cbuf Destination buffer | |
| * | |
| * @return The number of characters read, or -1 | |
| * if the end of the stream | |
| * has been reached | |
| * | |
| * @throws IOException If an I/O error occurs | |
| */ | |
| public int read(char[] cbuf) throws IOException { | |
| return read(cbuf, 0, cbuf.length); | |
| } | |
| /** | |
| * Reads characters into a portion of an array. This method will block | |
| * until some input is available, an I/O error occurs, or the end of the | |
| * stream is reached. | |
| * | |
| * <p> If {@code len} is zero, then no characters are read and {@code 0} is | |
| * returned; otherwise, there is an attempt to read at least one character. | |
| * If no character is available because the stream is at its end, the value | |
| * {@code -1} is returned; otherwise, at least one character is read and | |
| * stored into {@code cbuf}. | |
| * | |
| * @param cbuf Destination buffer | |
| * @param off Offset at which to start storing characters | |
| * @param len Maximum number of characters to read | |
| * | |
| * @return The number of characters read, or -1 if the end of the | |
| * stream has been reached | |
| * | |
| * @throws IndexOutOfBoundsException | |
| * If {@code off} is negative, or {@code len} is negative, | |
| * or {@code len} is greater than {@code cbuf.length - off} | |
| * @throws IOException If an I/O error occurs | |
| */ | |
| public abstract int read(char[] cbuf, int off, int len) throws IOException; | |
| private String readAllCharsAsString() throws IOException { | |
| StringBuilder result = new StringBuilder(); | |
| char[] cbuf = new char[TRANSFER_BUFFER_SIZE]; | |
| int nread; | |
| while ((nread = read(cbuf, 0, cbuf.length)) != -1) { | |
| result.append(cbuf, 0, nread); | |
| } | |
| return result.toString(); | |
| } | |
| /** | |
| * Reads all remaining characters as lines of text. This method blocks until | |
| * all remaining characters have been read and end of stream is detected, | |
| * or an exception is thrown. This method does not close the reader. | |
| * | |
| * <p> When this reader reaches the end of the stream, further | |
| * invocations of this method will return an empty list. | |
| * | |
| * <p> A <i>line</i> is either a sequence of zero or more characters | |
| * followed by a line terminator, or it is a sequence of one or | |
| * more characters followed by the end of the stream. | |
| * A line does not include the line terminator. | |
| * | |
| * <p> A <i>line terminator</i> is one of the following: | |
| * a line feed character {@code "\n"} (U+000A), | |
| * a carriage return character {@code "\r"} (U+000D), | |
| * or a carriage return followed immediately by a line feed | |
| * {@code "\r\n"} (U+000D U+000A). | |
| * | |
| * <p> The behavior for the case where the reader is | |
| * <i>asynchronously closed</i>, or the thread interrupted during the | |
| * read, is highly reader specific, and therefore not specified. | |
| * | |
| * <p> If an I/O error occurs reading from the stream then it | |
| * may do so after some, but not all, characters have been read. | |
| * Consequently the stream may not be at end of stream and may | |
| * be in an inconsistent state. It is strongly recommended that the reader | |
| * be promptly closed if an I/O error occurs. | |
| * | |
| * @apiNote | |
| * This method is intended for simple cases where it is appropriate and | |
| * convenient to read the entire input into a list of lines. It is not | |
| * suitable for reading input from an unknown origin, as this may result | |
| * in the allocation of an arbitrary amount of memory. | |
| * | |
| * @return the remaining characters as lines of text stored in an | |
| * unmodifiable {@code List} of {@code String}s in the order | |
| * they are read | |
| * | |
| * @throws IOException If an I/O error occurs | |
| * @throws OutOfMemoryError If the number of remaining characters | |
| * exceeds the implementation limit for {@code String}. | |
| * | |
| * @see String#lines | |
| * @see #readAllAsString | |
| * @see java.nio.file.Files#readAllLines | |
| * | |
| * @since 25 | |
| */ | |
| public List<String> readAllLines() throws IOException { | |
| return readAllCharsAsString().lines().toList(); | |
| } | |
| /** | |
| * Reads all remaining characters into a string. This method blocks until | |
| * all remaining characters including all line separators have been read | |
| * and end of stream is detected, or an exception is thrown. The resulting | |
| * string will contain line separators as they appear in the stream. This | |
| * method does not close the reader. | |
| * | |
| * <p> When this reader reaches the end of the stream, further | |
| * invocations of this method will return an empty string. | |
| * | |
| * <p> The behavior for the case where the reader | |
| * is <i>asynchronously closed</i>, or the thread interrupted during the | |
| * read, is highly reader specific, and therefore not specified. | |
| * | |
| * <p> If an I/O error occurs reading from the stream then it | |
| * may do so after some, but not all, characters have been read. | |
| * Consequently the stream may not be at end of stream and may | |
| * be in an inconsistent state. It is strongly recommended that the reader | |
| * be promptly closed if an I/O error occurs. | |
| * | |
| * @apiNote | |
| * This method is intended for simple cases where it is appropriate and | |
| * convenient to read the entire input into a {@code String}. It is not | |
| * suitable for reading input from an unknown origin, as this may result | |
| * in the allocation of an arbitrary amount of memory. | |
| * | |
| * @return a {@code String} containing all remaining characters | |
| * | |
| * @throws IOException If an I/O error occurs | |
| * @throws OutOfMemoryError If the number of remaining characters | |
| * exceeds the implementation limit for | |
| * {@code String}. | |
| * | |
| * @see #readAllLines | |
| * @see java.nio.file.Files#readString | |
| * | |
| * @since 25 | |
| */ | |
| public String readAllAsString() throws IOException { | |
| return readAllCharsAsString(); | |
| } | |
| /** Maximum skip-buffer size */ | |
| private static final int maxSkipBufferSize = 8192; | |
| /** Skip buffer, null until allocated */ | |
| private char[] skipBuffer = null; | |
| /** | |
| * Skips characters. This method will block until some characters are | |
| * available, an I/O error occurs, or the end of the stream is reached. | |
| * If the stream is already at its end before this method is invoked, | |
| * then no characters are skipped and zero is returned. | |
| * | |
| * @param n The number of characters to skip | |
| * | |
| * @return The number of characters actually skipped | |
| * | |
| * @throws IllegalArgumentException If {@code n} is negative. | |
| * @throws IOException If an I/O error occurs | |
| */ | |
| public long skip(long n) throws IOException { | |
| if (n < 0L) | |
| throw new IllegalArgumentException("skip value is negative"); | |
| synchronized (lock) { | |
| int nn = (int) Math.min(n, maxSkipBufferSize); | |
| if ((skipBuffer == null) || (skipBuffer.length < nn)) | |
| skipBuffer = new char[nn]; | |
| long r = n; | |
| while (r > 0) { | |
| int nc = read(skipBuffer, 0, (int)Math.min(r, nn)); | |
| if (nc == -1) | |
| break; | |
| r -= nc; | |
| } | |
| return n - r; | |
| } | |
| } | |
| /** | |
| * Tells whether this stream is ready to be read. | |
| * | |
| * @return True if the next read() is guaranteed not to block for input, | |
| * false otherwise. Note that returning false does not guarantee that the | |
| * next read will block. | |
| * | |
| * @throws IOException If an I/O error occurs | |
| */ | |
| public boolean ready() throws IOException { | |
| return false; | |
| } | |
| /** | |
| * Tells whether this stream supports the mark() operation. The default | |
| * implementation always returns false. Subclasses should override this | |
| * method. | |
| * | |
| * @return true if and only if this stream supports the mark operation. | |
| */ | |
| public boolean markSupported() { | |
| return false; | |
| } | |
| /** | |
| * Marks the present position in the stream. Subsequent calls to reset() | |
| * will attempt to reposition the stream to this point. Not all | |
| * character-input streams support the mark() operation. | |
| * | |
| * @param readAheadLimit Limit on the number of characters that may be | |
| * read while still preserving the mark. After | |
| * reading this many characters, attempting to | |
| * reset the stream may fail. | |
| * | |
| * @throws IOException If the stream does not support mark(), | |
| * or if some other I/O error occurs | |
| */ | |
| public void mark(int readAheadLimit) throws IOException { | |
| throw new IOException("mark() not supported"); | |
| } | |
| /** | |
| * Resets the stream. If the stream has been marked, then attempt to | |
| * reposition it at the mark. If the stream has not been marked, then | |
| * attempt to reset it in some way appropriate to the particular stream, | |
| * for example by repositioning it to its starting point. Not all | |
| * character-input streams support the reset() operation, and some support | |
| * reset() without supporting mark(). | |
| * | |
| * @throws IOException If the stream has not been marked, | |
| * or if the mark has been invalidated, | |
| * or if the stream does not support reset(), | |
| * or if some other I/O error occurs | |
| */ | |
| public void reset() throws IOException { | |
| throw new IOException("reset() not supported"); | |
| } | |
| /** | |
| * Closes the stream and releases any system resources associated with | |
| * it. Once the stream has been closed, further read(), ready(), | |
| * mark(), reset(), or skip() invocations will throw an IOException. | |
| * Closing a previously closed stream has no effect. | |
| * | |
| * @throws IOException If an I/O error occurs | |
| */ | |
| public abstract void close() throws IOException; | |
| /** | |
| * Reads all characters from this reader and writes the characters to the | |
| * given writer in the order that they are read. On return, this reader | |
| * will be at end of the stream. This method does not close either reader | |
| * or writer. | |
| * <p> | |
| * This method may block indefinitely reading from the reader, or | |
| * writing to the writer. The behavior for the case where the reader | |
| * and/or writer is <i>asynchronously closed</i>, or the thread | |
| * interrupted during the transfer, is highly reader and writer | |
| * specific, and therefore not specified. | |
| * <p> | |
| * If the total number of characters transferred is greater than {@linkplain | |
| * Long#MAX_VALUE}, then {@code Long.MAX_VALUE} will be returned. | |
| * <p> | |
| * If an I/O error occurs reading from the reader or writing to the | |
| * writer, then it may do so after some characters have been read or | |
| * written. Consequently the reader may not be at end of the stream and | |
| * one, or both, streams may be in an inconsistent state. It is strongly | |
| * recommended that both streams be promptly closed if an I/O error occurs. | |
| * | |
| * @param out the writer, non-null | |
| * @return the number of characters transferred | |
| * @throws IOException if an I/O error occurs when reading or writing | |
| * @throws NullPointerException if {@code out} is {@code null} | |
| * | |
| * @since 10 | |
| */ | |
| public long transferTo(Writer out) throws IOException { | |
| Objects.requireNonNull(out, "out"); | |
| long transferred = 0; | |
| char[] buffer = new char[TRANSFER_BUFFER_SIZE]; | |
| int nRead; | |
| while ((nRead = read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) { | |
| out.write(buffer, 0, nRead); | |
| if (transferred < Long.MAX_VALUE) { | |
| try { | |
| transferred = Math.addExact(transferred, nRead); | |
| } catch (ArithmeticException ignore) { | |
| transferred = Long.MAX_VALUE; | |
| } | |
| } | |
| } | |
| return transferred; | |
| } | |
| } |