1 /* 2 * Copyright (c) 1996, 2015, 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 29 /** 30 * A character stream whose source is a string. 31 * 32 * @author Mark Reinhold 33 * @since 1.1 34 */ 35 36 public class StringReader extends Reader { 37 38 private String str; 39 private int length; 40 private int next = 0; 41 private int mark = 0; 42 43 /** 44 * Creates a new string reader. 45 * 46 * @param s String providing the character stream. 47 */ StringReader(String s)48 public StringReader(String s) { 49 this.str = s; 50 this.length = s.length(); 51 } 52 53 /** Check to make sure that the stream has not been closed */ ensureOpen()54 private void ensureOpen() throws IOException { 55 if (str == null) 56 throw new IOException("Stream closed"); 57 } 58 59 /** 60 * Reads a single character. 61 * 62 * @return The character read, or -1 if the end of the stream has been 63 * reached 64 * 65 * @exception IOException If an I/O error occurs 66 */ read()67 public int read() throws IOException { 68 synchronized (lock) { 69 ensureOpen(); 70 if (next >= length) 71 return -1; 72 return str.charAt(next++); 73 } 74 } 75 76 /** 77 * Reads characters into a portion of an array. 78 * 79 * @param cbuf Destination buffer 80 * @param off Offset at which to start writing characters 81 * @param len Maximum number of characters to read 82 * 83 * @return The number of characters read, or -1 if the end of the 84 * stream has been reached 85 * 86 * @exception IOException If an I/O error occurs 87 * @exception IndexOutOfBoundsException {@inheritDoc} 88 */ read(char cbuf[], int off, int len)89 public int read(char cbuf[], int off, int len) throws IOException { 90 synchronized (lock) { 91 ensureOpen(); 92 if ((off < 0) || (off > cbuf.length) || (len < 0) || 93 ((off + len) > cbuf.length) || ((off + len) < 0)) { 94 throw new IndexOutOfBoundsException(); 95 } else if (len == 0) { 96 return 0; 97 } 98 if (next >= length) 99 return -1; 100 int n = Math.min(length - next, len); 101 str.getChars(next, next + n, cbuf, off); 102 next += n; 103 return n; 104 } 105 } 106 107 /** 108 * Skips the specified number of characters in the stream. Returns 109 * the number of characters that were skipped. 110 * 111 * <p>The <code>ns</code> parameter may be negative, even though the 112 * <code>skip</code> method of the {@link Reader} superclass throws 113 * an exception in this case. Negative values of <code>ns</code> cause the 114 * stream to skip backwards. Negative return values indicate a skip 115 * backwards. It is not possible to skip backwards past the beginning of 116 * the string. 117 * 118 * <p>If the entire string has been read or skipped, then this method has 119 * no effect and always returns 0. 120 * 121 * @exception IOException If an I/O error occurs 122 */ skip(long ns)123 public long skip(long ns) throws IOException { 124 synchronized (lock) { 125 ensureOpen(); 126 if (next >= length) 127 return 0; 128 // Bound skip by beginning and end of the source 129 long n = Math.min(length - next, ns); 130 n = Math.max(-next, n); 131 next += n; 132 return n; 133 } 134 } 135 136 /** 137 * Tells whether this stream is ready to be read. 138 * 139 * @return True if the next read() is guaranteed not to block for input 140 * 141 * @exception IOException If the stream is closed 142 */ ready()143 public boolean ready() throws IOException { 144 synchronized (lock) { 145 ensureOpen(); 146 return true; 147 } 148 } 149 150 /** 151 * Tells whether this stream supports the mark() operation, which it does. 152 */ markSupported()153 public boolean markSupported() { 154 return true; 155 } 156 157 /** 158 * Marks the present position in the stream. Subsequent calls to reset() 159 * will reposition the stream to this point. 160 * 161 * @param readAheadLimit Limit on the number of characters that may be 162 * read while still preserving the mark. Because 163 * the stream's input comes from a string, there 164 * is no actual limit, so this argument must not 165 * be negative, but is otherwise ignored. 166 * 167 * @exception IllegalArgumentException If {@code readAheadLimit < 0} 168 * @exception IOException If an I/O error occurs 169 */ mark(int readAheadLimit)170 public void mark(int readAheadLimit) throws IOException { 171 if (readAheadLimit < 0){ 172 throw new IllegalArgumentException("Read-ahead limit < 0"); 173 } 174 synchronized (lock) { 175 ensureOpen(); 176 mark = next; 177 } 178 } 179 180 /** 181 * Resets the stream to the most recent mark, or to the beginning of the 182 * string if it has never been marked. 183 * 184 * @exception IOException If an I/O error occurs 185 */ reset()186 public void reset() throws IOException { 187 synchronized (lock) { 188 ensureOpen(); 189 next = mark; 190 } 191 } 192 193 /** 194 * Closes the stream and releases any system resources associated with 195 * it. Once the stream has been closed, further read(), 196 * ready(), mark(), or reset() invocations will throw an IOException. 197 * Closing a previously closed stream has no effect. This method will block 198 * while there is another thread blocking on the reader. 199 */ close()200 public void close() { 201 synchronized (lock) { 202 str = null; 203 } 204 } 205 } 206