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 18 package java.io; 19 20 import java.util.Arrays; 21 22 /** 23 * A specialized {@link Reader} that reads characters from a {@code String} in 24 * a sequential manner. 25 * 26 * @see StringWriter 27 */ 28 public class StringReader extends Reader { 29 private String str; 30 31 private int markpos = -1; 32 33 private int pos; 34 35 private int count; 36 37 /** 38 * Construct a new {@code StringReader} with {@code str} as source. The size 39 * of the reader is set to the {@code length()} of the string and the Object 40 * to synchronize access through is set to {@code str}. 41 * 42 * @param str 43 * the source string for this reader. 44 */ 45 public StringReader(String str) { 46 this.str = str; 47 this.count = str.length(); 48 } 49 50 /** 51 * Closes this reader. Once it is closed, read operations on this reader 52 * will throw an {@code IOException}. Only the first invocation of this 53 * method has any effect. 54 */ 55 @Override 56 public void close() { 57 str = null; 58 } 59 60 /** 61 * Returns a boolean indicating whether this reader is closed. 62 * 63 * @return {@code true} if closed, otherwise {@code false}. 64 */ 65 private boolean isClosed() { 66 return str == null; 67 } 68 69 /** 70 * Sets a mark position in this reader. The parameter {@code readLimit} is 71 * ignored for this class. Calling {@code reset()} will reposition the 72 * reader back to the marked position. 73 * 74 * @param readLimit 75 * ignored for {@code StringReader} instances. 76 * @throws IllegalArgumentException 77 * if {@code readLimit < 0}. 78 * @throws IOException 79 * if this reader is closed. 80 * @see #markSupported() 81 * @see #reset() 82 */ 83 @Override 84 public void mark(int readLimit) throws IOException { 85 if (readLimit < 0) { 86 throw new IllegalArgumentException(); 87 } 88 89 synchronized (lock) { 90 checkNotClosed(); 91 markpos = pos; 92 } 93 } 94 95 private void checkNotClosed() throws IOException { 96 if (isClosed()) { 97 throw new IOException("StringReader is closed"); 98 } 99 } 100 101 /** 102 * Indicates whether this reader supports the {@code mark()} and {@code 103 * reset()} methods. This implementation returns {@code true}. 104 * 105 * @return always {@code true}. 106 */ 107 @Override 108 public boolean markSupported() { 109 return true; 110 } 111 112 /** 113 * Reads a single character from the source string and returns it as an 114 * integer with the two higher-order bytes set to 0. Returns -1 if the end 115 * of the source string has been reached. 116 * 117 * @return the character read or -1 if the end of the source string has been 118 * reached. 119 * @throws IOException 120 * if this reader is closed. 121 */ 122 @Override 123 public int read() throws IOException { 124 synchronized (lock) { 125 checkNotClosed(); 126 if (pos != count) { 127 return str.charAt(pos++); 128 } 129 return -1; 130 } 131 } 132 133 /** 134 * Reads at most {@code len} characters from the source string and stores 135 * them at {@code offset} in the character array {@code buf}. Returns the 136 * number of characters actually read or -1 if the end of the source string 137 * has been reached. 138 * 139 * @param buf 140 * the character array to store the characters read. 141 * @param offset 142 * the initial position in {@code buffer} to store the characters 143 * read from this reader. 144 * @param len 145 * the maximum number of characters to read. 146 * @return the number of characters read or -1 if the end of the reader has 147 * been reached. 148 * @throws IndexOutOfBoundsException 149 * if {@code offset < 0} or {@code len < 0}, or if 150 * {@code offset + len} is greater than the size of {@code buf}. 151 * @throws IOException 152 * if this reader is closed. 153 */ 154 @Override 155 public int read(char[] buf, int offset, int len) throws IOException { 156 synchronized (lock) { 157 checkNotClosed(); 158 Arrays.checkOffsetAndCount(buf.length, offset, len); 159 if (len == 0) { 160 return 0; 161 } 162 if (pos == this.count) { 163 return -1; 164 } 165 int end = pos + len > this.count ? this.count : pos + len; 166 str.getChars(pos, end, buf, offset); 167 int read = end - pos; 168 pos = end; 169 return read; 170 } 171 } 172 173 /** 174 * Indicates whether this reader is ready to be read without blocking. This 175 * implementation always returns {@code true}. 176 * 177 * @return always {@code true}. 178 * @throws IOException 179 * if this reader is closed. 180 * @see #read() 181 * @see #read(char[], int, int) 182 */ 183 @Override 184 public boolean ready() throws IOException { 185 synchronized (lock) { 186 checkNotClosed(); 187 return true; 188 } 189 } 190 191 /** 192 * Resets this reader's position to the last {@code mark()} location. 193 * Invocations of {@code read()} and {@code skip()} will occur from this new 194 * location. If this reader has not been marked, it is reset to the 195 * beginning of the source string. 196 * 197 * @throws IOException 198 * if this reader is closed. 199 * @see #mark(int) 200 * @see #markSupported() 201 */ 202 @Override 203 public void reset() throws IOException { 204 synchronized (lock) { 205 checkNotClosed(); 206 pos = markpos != -1 ? markpos : 0; 207 } 208 } 209 210 /** 211 * Moves {@code charCount} characters in the source string. Unlike the {@link 212 * Reader#skip(long) overridden method}, this method may skip negative skip 213 * distances: this rewinds the input so that characters may be read again. 214 * When the end of the source string has been reached, the input cannot be 215 * rewound. 216 * 217 * @param charCount 218 * the maximum number of characters to skip. Positive values skip 219 * forward; negative values skip backward. 220 * @return the number of characters actually skipped. This is bounded below 221 * by the number of characters already read and above by the 222 * number of characters remaining:<br> {@code -(num chars already 223 * read) <= distance skipped <= num chars remaining}. 224 * @throws IOException 225 * if this reader is closed. 226 * @see #mark(int) 227 * @see #markSupported() 228 * @see #reset() 229 */ 230 @Override 231 public long skip(long charCount) throws IOException { 232 synchronized (lock) { 233 checkNotClosed(); 234 235 int minSkip = -pos; 236 int maxSkip = count - pos; 237 238 if (maxSkip == 0 || charCount > maxSkip) { 239 charCount = maxSkip; // no rewinding if we're at the end 240 } else if (charCount < minSkip) { 241 charCount = minSkip; 242 } 243 244 pos += charCount; 245 return charCount; 246 } 247 } 248 } 249