| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.io; |
| |
| import java.util.Arrays; |
| |
| /** |
| * A specialized {@link Reader} that reads characters from a {@code String} in |
| * a sequential manner. |
| * |
| * @see StringWriter |
| */ |
| public class StringReader extends Reader { |
| private String str; |
| |
| private int markpos = -1; |
| |
| private int pos; |
| |
| private int count; |
| |
| /** |
| * Construct a new {@code StringReader} with {@code str} as source. The size |
| * of the reader is set to the {@code length()} of the string and the Object |
| * to synchronize access through is set to {@code str}. |
| * |
| * @param str |
| * the source string for this reader. |
| */ |
| public StringReader(String str) { |
| this.str = str; |
| this.count = str.length(); |
| } |
| |
| /** |
| * Closes this reader. Once it is closed, read operations on this reader |
| * will throw an {@code IOException}. Only the first invocation of this |
| * method has any effect. |
| */ |
| @Override |
| public void close() { |
| str = null; |
| } |
| |
| /** |
| * Returns a boolean indicating whether this reader is closed. |
| * |
| * @return {@code true} if closed, otherwise {@code false}. |
| */ |
| private boolean isClosed() { |
| return str == null; |
| } |
| |
| /** |
| * Sets a mark position in this reader. The parameter {@code readLimit} is |
| * ignored for this class. Calling {@code reset()} will reposition the |
| * reader back to the marked position. |
| * |
| * @param readLimit |
| * ignored for {@code StringReader} instances. |
| * @throws IllegalArgumentException |
| * if {@code readLimit < 0}. |
| * @throws IOException |
| * if this reader is closed. |
| * @see #markSupported() |
| * @see #reset() |
| */ |
| @Override |
| public void mark(int readLimit) throws IOException { |
| if (readLimit < 0) { |
| throw new IllegalArgumentException(); |
| } |
| |
| synchronized (lock) { |
| checkNotClosed(); |
| markpos = pos; |
| } |
| } |
| |
| private void checkNotClosed() throws IOException { |
| if (isClosed()) { |
| throw new IOException("StringReader is closed"); |
| } |
| } |
| |
| /** |
| * Indicates whether this reader supports the {@code mark()} and {@code |
| * reset()} methods. This implementation returns {@code true}. |
| * |
| * @return always {@code true}. |
| */ |
| @Override |
| public boolean markSupported() { |
| return true; |
| } |
| |
| /** |
| * Reads a single character from the source string and returns it as an |
| * integer with the two higher-order bytes set to 0. Returns -1 if the end |
| * of the source string has been reached. |
| * |
| * @return the character read or -1 if the end of the source string has been |
| * reached. |
| * @throws IOException |
| * if this reader is closed. |
| */ |
| @Override |
| public int read() throws IOException { |
| synchronized (lock) { |
| checkNotClosed(); |
| if (pos != count) { |
| return str.charAt(pos++); |
| } |
| return -1; |
| } |
| } |
| |
| /** |
| * Reads at most {@code len} characters from the source string and stores |
| * them at {@code offset} in the character array {@code buf}. Returns the |
| * number of characters actually read or -1 if the end of the source string |
| * has been reached. |
| * |
| * @param buf |
| * the character array to store the characters read. |
| * @param offset |
| * the initial position in {@code buffer} to store the characters |
| * read from this reader. |
| * @param len |
| * the maximum number of characters to read. |
| * @return the number of characters read or -1 if the end of the reader has |
| * been reached. |
| * @throws IndexOutOfBoundsException |
| * if {@code offset < 0} or {@code len < 0}, or if |
| * {@code offset + len} is greater than the size of {@code buf}. |
| * @throws IOException |
| * if this reader is closed. |
| */ |
| @Override |
| public int read(char[] buf, int offset, int len) throws IOException { |
| synchronized (lock) { |
| checkNotClosed(); |
| Arrays.checkOffsetAndCount(buf.length, offset, len); |
| if (len == 0) { |
| return 0; |
| } |
| if (pos == this.count) { |
| return -1; |
| } |
| int end = pos + len > this.count ? this.count : pos + len; |
| str.getChars(pos, end, buf, offset); |
| int read = end - pos; |
| pos = end; |
| return read; |
| } |
| } |
| |
| /** |
| * Indicates whether this reader is ready to be read without blocking. This |
| * implementation always returns {@code true}. |
| * |
| * @return always {@code true}. |
| * @throws IOException |
| * if this reader is closed. |
| * @see #read() |
| * @see #read(char[], int, int) |
| */ |
| @Override |
| public boolean ready() throws IOException { |
| synchronized (lock) { |
| checkNotClosed(); |
| return true; |
| } |
| } |
| |
| /** |
| * Resets this reader's position to the last {@code mark()} location. |
| * Invocations of {@code read()} and {@code skip()} will occur from this new |
| * location. If this reader has not been marked, it is reset to the |
| * beginning of the source string. |
| * |
| * @throws IOException |
| * if this reader is closed. |
| * @see #mark(int) |
| * @see #markSupported() |
| */ |
| @Override |
| public void reset() throws IOException { |
| synchronized (lock) { |
| checkNotClosed(); |
| pos = markpos != -1 ? markpos : 0; |
| } |
| } |
| |
| /** |
| * Moves {@code charCount} characters in the source string. Unlike the {@link |
| * Reader#skip(long) overridden method}, this method may skip negative skip |
| * distances: this rewinds the input so that characters may be read again. |
| * When the end of the source string has been reached, the input cannot be |
| * rewound. |
| * |
| * @param charCount |
| * the maximum number of characters to skip. Positive values skip |
| * forward; negative values skip backward. |
| * @return the number of characters actually skipped. This is bounded below |
| * by the number of characters already read and above by the |
| * number of characters remaining:<br> {@code -(num chars already |
| * read) <= distance skipped <= num chars remaining}. |
| * @throws IOException |
| * if this reader is closed. |
| * @see #mark(int) |
| * @see #markSupported() |
| * @see #reset() |
| */ |
| @Override |
| public long skip(long charCount) throws IOException { |
| synchronized (lock) { |
| checkNotClosed(); |
| |
| int minSkip = -pos; |
| int maxSkip = count - pos; |
| |
| if (maxSkip == 0 || charCount > maxSkip) { |
| charCount = maxSkip; // no rewinding if we're at the end |
| } else if (charCount < minSkip) { |
| charCount = minSkip; |
| } |
| |
| pos += charCount; |
| return charCount; |
| } |
| } |
| } |