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 18package java.io; 19 20import 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 */ 28public 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("readLimit < 0: " + readLimit); 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 up to {@code count} characters from the source string and stores 135 * them at {@code offset} in the character array {@code buffer}. Returns the 136 * number of characters actually read or -1 if the end of the source string 137 * has been reached. 138 * 139 * @throws IndexOutOfBoundsException 140 if {@code offset < 0 || count < 0 || offset + count > buffer.length}. 141 * @throws IOException 142 * if this reader is closed. 143 */ 144 @Override 145 public int read(char[] buffer, int offset, int count) throws IOException { 146 synchronized (lock) { 147 checkNotClosed(); 148 Arrays.checkOffsetAndCount(buffer.length, offset, count); 149 if (count == 0) { 150 return 0; 151 } 152 if (pos == this.count) { 153 return -1; 154 } 155 int end = pos + count > this.count ? this.count : pos + count; 156 str.getChars(pos, end, buffer, offset); 157 int read = end - pos; 158 pos = end; 159 return read; 160 } 161 } 162 163 /** 164 * Indicates whether this reader is ready to be read without blocking. This 165 * implementation always returns {@code true}. 166 * 167 * @return always {@code true}. 168 * @throws IOException 169 * if this reader is closed. 170 * @see #read() 171 * @see #read(char[], int, int) 172 */ 173 @Override 174 public boolean ready() throws IOException { 175 synchronized (lock) { 176 checkNotClosed(); 177 return true; 178 } 179 } 180 181 /** 182 * Resets this reader's position to the last {@code mark()} location. 183 * Invocations of {@code read()} and {@code skip()} will occur from this new 184 * location. If this reader has not been marked, it is reset to the 185 * beginning of the source string. 186 * 187 * @throws IOException 188 * if this reader is closed. 189 * @see #mark(int) 190 * @see #markSupported() 191 */ 192 @Override 193 public void reset() throws IOException { 194 synchronized (lock) { 195 checkNotClosed(); 196 pos = markpos != -1 ? markpos : 0; 197 } 198 } 199 200 /** 201 * Moves {@code charCount} characters in the source string. Unlike the {@link 202 * Reader#skip(long) overridden method}, this method may skip negative skip 203 * distances: this rewinds the input so that characters may be read again. 204 * When the end of the source string has been reached, the input cannot be 205 * rewound. 206 * 207 * @param charCount 208 * the maximum number of characters to skip. Positive values skip 209 * forward; negative values skip backward. 210 * @return the number of characters actually skipped. This is bounded below 211 * by the number of characters already read and above by the 212 * number of characters remaining:<br> {@code -(num chars already 213 * read) <= distance skipped <= num chars remaining}. 214 * @throws IOException 215 * if this reader is closed. 216 * @see #mark(int) 217 * @see #markSupported() 218 * @see #reset() 219 */ 220 @Override 221 public long skip(long charCount) throws IOException { 222 synchronized (lock) { 223 checkNotClosed(); 224 225 int minSkip = -pos; 226 int maxSkip = count - pos; 227 228 if (maxSkip == 0 || charCount > maxSkip) { 229 charCount = maxSkip; // no rewinding if we're at the end 230 } else if (charCount < minSkip) { 231 charCount = minSkip; 232 } 233 234 pos += charCount; 235 return charCount; 236 } 237 } 238} 239