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} for reading the contents of a char array. 24 * 25 * @see CharArrayWriter 26 */ 27 public class CharArrayReader extends Reader { 28 /** 29 * The buffer for characters. 30 */ 31 protected char[] buf; 32 33 /** 34 * The current buffer position. 35 */ 36 protected int pos; 37 38 /** 39 * The current mark position. 40 */ 41 protected int markedPos = -1; 42 43 /** 44 * The ending index of the buffer. 45 */ 46 protected int count; 47 48 /** 49 * Constructs a CharArrayReader on the char array {@code buf}. The size of 50 * the reader is set to the length of the buffer and the object to to read 51 * from is set to {@code buf}. 52 * 53 * @param buf 54 * the char array from which to read. 55 */ 56 public CharArrayReader(char[] buf) { 57 this.buf = buf; 58 this.count = buf.length; 59 } 60 61 /** 62 * Constructs a CharArrayReader on the char array {@code buf}. The size of 63 * the reader is set to {@code length} and the start position from which to 64 * read the buffer is set to {@code offset}. 65 * 66 * @param buf 67 * the char array from which to read. 68 * @param offset 69 * the index of the first character in {@code buf} to read. 70 * @param length 71 * the number of characters that can be read from {@code buf}. 72 * @throws IllegalArgumentException 73 * if {@code offset < 0} or {@code length < 0}, or if 74 * {@code offset} is greater than the size of {@code buf} . 75 */ 76 public CharArrayReader(char[] buf, int offset, int length) { 77 /* 78 * The spec of this constructor is broken. In defining the legal values 79 * of offset and length, it doesn't consider buffer's length. And to be 80 * compatible with the broken spec, we must also test whether 81 * (offset + length) overflows. 82 */ 83 if (offset < 0 || offset > buf.length || length < 0 || offset + length < 0) { 84 throw new IllegalArgumentException(); 85 } 86 this.buf = buf; 87 this.pos = offset; 88 this.markedPos = offset; 89 90 /* This is according to spec */ 91 int bufferLength = buf.length; 92 this.count = offset + length < bufferLength ? length : bufferLength; 93 } 94 95 /** 96 * This method closes this CharArrayReader. Once it is closed, you can no 97 * longer read from it. Only the first invocation of this method has any 98 * effect. 99 */ 100 @Override 101 public void close() { 102 synchronized (lock) { 103 if (isOpen()) { 104 buf = null; 105 } 106 } 107 } 108 109 /** 110 * Indicates whether this reader is open. 111 * 112 * @return {@code true} if the reader is open, {@code false} otherwise. 113 */ 114 private boolean isOpen() { 115 return buf != null; 116 } 117 118 /** 119 * Indicates whether this reader is closed. 120 * 121 * @return {@code true} if the reader is closed, {@code false} otherwise. 122 */ 123 private boolean isClosed() { 124 return buf == null; 125 } 126 127 /** 128 * Sets a mark position in this reader. The parameter {@code readLimit} is 129 * ignored for CharArrayReaders. Calling {@code reset()} will reposition the 130 * reader back to the marked position provided the mark has not been 131 * invalidated. 132 * 133 * @param readLimit 134 * ignored for CharArrayReaders. 135 * @throws IOException 136 * if this reader is closed. 137 */ 138 @Override 139 public void mark(int readLimit) throws IOException { 140 synchronized (lock) { 141 checkNotClosed(); 142 markedPos = pos; 143 } 144 } 145 146 private void checkNotClosed() throws IOException { 147 if (isClosed()) { 148 throw new IOException("CharArrayReader is closed"); 149 } 150 } 151 152 /** 153 * Indicates whether this reader supports the {@code mark()} and 154 * {@code reset()} methods. 155 * 156 * @return {@code true} for CharArrayReader. 157 * @see #mark(int) 158 * @see #reset() 159 */ 160 @Override 161 public boolean markSupported() { 162 return true; 163 } 164 165 /** 166 * Reads a single character from this reader and returns it as an integer 167 * with the two higher-order bytes set to 0. Returns -1 if no more 168 * characters are available from this reader. 169 * 170 * @return the character read as an int or -1 if the end of the reader has 171 * been reached. 172 * @throws IOException 173 * if this reader is closed. 174 */ 175 @Override 176 public int read() throws IOException { 177 synchronized (lock) { 178 checkNotClosed(); 179 if (pos == count) { 180 return -1; 181 } 182 return buf[pos++]; 183 } 184 } 185 186 /** 187 * Reads at most {@code count} characters from this CharArrayReader and 188 * stores them at {@code offset} in the character array {@code buf}. 189 * Returns the number of characters actually read or -1 if the end of reader 190 * was encountered. 191 * 192 * @param buffer 193 * the character array to store the characters read. 194 * @param offset 195 * the initial position in {@code buffer} to store the characters 196 * read from this reader. 197 * @param len 198 * the maximum number of characters to read. 199 * @return number of characters read or -1 if the end of the reader has been 200 * reached. 201 * @throws IndexOutOfBoundsException 202 * if {@code offset < 0} or {@code len < 0}, or if 203 * {@code offset + len} is bigger than the size of 204 * {@code buffer}. 205 * @throws IOException 206 * if this reader is closed. 207 */ 208 @Override 209 public int read(char[] buffer, int offset, int len) throws IOException { 210 Arrays.checkOffsetAndCount(buffer.length, offset, len); 211 synchronized (lock) { 212 checkNotClosed(); 213 if (pos < this.count) { 214 int bytesRead = pos + len > this.count ? this.count - pos : len; 215 System.arraycopy(this.buf, pos, buffer, offset, bytesRead); 216 pos += bytesRead; 217 return bytesRead; 218 } 219 return -1; 220 } 221 } 222 223 /** 224 * Indicates whether this reader is ready to be read without blocking. 225 * Returns {@code true} if the next {@code read} will not block. Returns 226 * {@code false} if this reader may or may not block when {@code read} is 227 * called. The implementation in CharArrayReader always returns {@code true} 228 * even when it has been closed. 229 * 230 * @return {@code true} if this reader will not block when {@code read} is 231 * called, {@code false} if unknown or blocking will occur. 232 * @throws IOException 233 * if this reader is closed. 234 */ 235 @Override 236 public boolean ready() throws IOException { 237 synchronized (lock) { 238 checkNotClosed(); 239 return pos != count; 240 } 241 } 242 243 /** 244 * Resets this reader's position to the last {@code mark()} location. 245 * Invocations of {@code read()} and {@code skip()} will occur from this new 246 * location. If this reader has not been marked, it is reset to the 247 * beginning of the string. 248 * 249 * @throws IOException 250 * if this reader is closed. 251 */ 252 @Override 253 public void reset() throws IOException { 254 synchronized (lock) { 255 checkNotClosed(); 256 pos = markedPos != -1 ? markedPos : 0; 257 } 258 } 259 260 /** 261 * Skips {@code charCount} characters in this reader. Subsequent calls to 262 * {@code read} will not return these characters unless {@code reset} 263 * is used. This method does nothing and returns 0 if {@code charCount <= 0}. 264 * 265 * @return the number of characters actually skipped. 266 * @throws IOException 267 * if this reader is closed. 268 */ 269 @Override 270 public long skip(long charCount) throws IOException { 271 synchronized (lock) { 272 checkNotClosed(); 273 if (charCount <= 0) { 274 return 0; 275 } 276 long skipped = 0; 277 if (charCount < this.count - pos) { 278 pos = pos + (int) charCount; 279 skipped = charCount; 280 } else { 281 skipped = this.count - pos; 282 pos = this.count; 283 } 284 return skipped; 285 } 286 } 287 } 288