1 /* 2 * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.io; 27 28 import java.util.Objects; 29 30 /** 31 * A character stream whose source is a string. 32 * 33 * @author Mark Reinhold 34 * @since 1.1 35 */ 36 37 public class StringReader extends Reader { 38 39 private String str; 40 private int length; 41 private int next = 0; 42 private int mark = 0; 43 44 /** 45 * Creates a new string reader. 46 * 47 * @param s String providing the character stream. 48 */ StringReader(String s)49 public StringReader(String s) { 50 this.str = s; 51 this.length = s.length(); 52 } 53 54 /** Check to make sure that the stream has not been closed */ ensureOpen()55 private void ensureOpen() throws IOException { 56 if (str == null) 57 throw new IOException("Stream closed"); 58 } 59 60 /** 61 * Reads a single character. 62 * 63 * @return The character read, or -1 if the end of the stream has been 64 * reached 65 * 66 * @throws IOException If an I/O error occurs 67 */ read()68 public int read() throws IOException { 69 synchronized (lock) { 70 ensureOpen(); 71 if (next >= length) 72 return -1; 73 return str.charAt(next++); 74 } 75 } 76 77 /** 78 * Reads characters into a portion of an array. 79 * 80 * <p> If {@code len} is zero, then no characters are read and {@code 0} is 81 * returned; otherwise, there is an attempt to read at least one character. 82 * If no character is available because the stream is at its end, the value 83 * {@code -1} is returned; otherwise, at least one character is read and 84 * stored into {@code cbuf}. 85 * 86 * @param cbuf {@inheritDoc} 87 * @param off {@inheritDoc} 88 * @param len {@inheritDoc} 89 * 90 * @return {@inheritDoc} 91 * 92 * @throws IndexOutOfBoundsException {@inheritDoc} 93 * @throws IOException {@inheritDoc} 94 */ read(char[] cbuf, int off, int len)95 public int read(char[] cbuf, int off, int len) throws IOException { 96 synchronized (lock) { 97 ensureOpen(); 98 Objects.checkFromIndexSize(off, len, cbuf.length); 99 if (len == 0) { 100 return 0; 101 } 102 if (next >= length) 103 return -1; 104 int n = Math.min(length - next, len); 105 str.getChars(next, next + n, cbuf, off); 106 next += n; 107 return n; 108 } 109 } 110 111 /** 112 * Skips characters. If the stream is already at its end before this method 113 * is invoked, then no characters are skipped and zero is returned. 114 * 115 * <p>The {@code n} parameter may be negative, even though the 116 * {@code skip} method of the {@link Reader} superclass throws 117 * an exception in this case. Negative values of {@code n} cause the 118 * stream to skip backwards. Negative return values indicate a skip 119 * backwards. It is not possible to skip backwards past the beginning of 120 * the string. 121 * 122 * <p>If the entire string has been read or skipped, then this method has 123 * no effect and always returns {@code 0}. 124 * 125 * @param n {@inheritDoc} 126 * 127 * @return {@inheritDoc} 128 * 129 * @throws IOException {@inheritDoc} 130 */ skip(long n)131 public long skip(long n) throws IOException { 132 synchronized (lock) { 133 ensureOpen(); 134 if (next >= length) 135 return 0; 136 // Bound skip by beginning and end of the source 137 long r = Math.min(length - next, n); 138 r = Math.max(-next, r); 139 next += r; 140 return r; 141 } 142 } 143 144 /** 145 * Tells whether this stream is ready to be read. 146 * 147 * @return True if the next read() is guaranteed not to block for input 148 * 149 * @throws IOException If the stream is closed 150 */ ready()151 public boolean ready() throws IOException { 152 synchronized (lock) { 153 ensureOpen(); 154 return true; 155 } 156 } 157 158 /** 159 * Tells whether this stream supports the mark() operation, which it does. 160 */ markSupported()161 public boolean markSupported() { 162 return true; 163 } 164 165 /** 166 * Marks the present position in the stream. Subsequent calls to reset() 167 * will reposition the stream to this point. 168 * 169 * @param readAheadLimit Limit on the number of characters that may be 170 * read while still preserving the mark. Because 171 * the stream's input comes from a string, there 172 * is no actual limit, so this argument must not 173 * be negative, but is otherwise ignored. 174 * 175 * @throws IllegalArgumentException If {@code readAheadLimit < 0} 176 * @throws IOException If an I/O error occurs 177 */ mark(int readAheadLimit)178 public void mark(int readAheadLimit) throws IOException { 179 if (readAheadLimit < 0){ 180 throw new IllegalArgumentException("Read-ahead limit < 0"); 181 } 182 synchronized (lock) { 183 ensureOpen(); 184 mark = next; 185 } 186 } 187 188 /** 189 * Resets the stream to the most recent mark, or to the beginning of the 190 * string if it has never been marked. 191 * 192 * @throws IOException If an I/O error occurs 193 */ reset()194 public void reset() throws IOException { 195 synchronized (lock) { 196 ensureOpen(); 197 next = mark; 198 } 199 } 200 201 /** 202 * Closes the stream and releases any system resources associated with 203 * it. Once the stream has been closed, further read(), 204 * ready(), mark(), or reset() invocations will throw an IOException. 205 * Closing a previously closed stream has no effect. This method will block 206 * while there is another thread blocking on the reader. 207 */ close()208 public void close() { 209 synchronized (lock) { 210 str = null; 211 } 212 } 213 } 214