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