1 /* 2 * Copyright (c) 1996, 2021, 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 import java.nio.CharBuffer; 29 import java.util.Objects; 30 31 /** 32 * This class implements a character buffer that can be used as a 33 * character-input stream. 34 * 35 * @author Herb Jellinek 36 * @since 1.1 37 */ 38 public class CharArrayReader extends Reader { 39 /** The character buffer. */ 40 protected char[] buf; 41 42 /** The current buffer position. */ 43 protected int pos; 44 45 /** The position of mark in buffer. */ 46 protected int markedPos = 0; 47 48 /** 49 * The index of the end of this buffer. There is not valid 50 * data at or beyond this index. 51 */ 52 protected int count; 53 54 /** 55 * Creates a CharArrayReader from the specified array of chars. 56 * @param buf Input buffer (not copied) 57 */ CharArrayReader(char[] buf)58 public CharArrayReader(char[] buf) { 59 this.buf = buf; 60 this.pos = 0; 61 this.count = buf.length; 62 } 63 64 /** 65 * Creates a CharArrayReader from the specified array of chars. 66 * 67 * <p> The resulting reader will start reading at the given 68 * {@code offset}. The total number of {@code char} values that can be 69 * read from this reader will be either {@code length} or 70 * {@code buf.length-offset}, whichever is smaller. 71 * 72 * @throws IllegalArgumentException 73 * If {@code offset} is negative or greater than 74 * {@code buf.length}, or if {@code length} is negative, or if 75 * the sum of these two values is negative. 76 * 77 * @param buf Input buffer (not copied) 78 * @param offset Offset of the first char to read 79 * @param length Number of chars to read 80 */ CharArrayReader(char[] buf, int offset, int length)81 public CharArrayReader(char[] buf, int offset, int length) { 82 if ((offset < 0) || (offset > buf.length) || (length < 0) || 83 ((offset + length) < 0)) { 84 throw new IllegalArgumentException(); 85 } 86 this.buf = buf; 87 this.pos = offset; 88 this.count = Math.min(offset + length, buf.length); 89 this.markedPos = offset; 90 } 91 92 /** Checks to make sure that the stream has not been closed */ ensureOpen()93 private void ensureOpen() throws IOException { 94 if (buf == null) 95 throw new IOException("Stream closed"); 96 } 97 98 /** 99 * Reads a single character. 100 * 101 * @throws IOException If an I/O error occurs 102 */ read()103 public int read() throws IOException { 104 synchronized (lock) { 105 ensureOpen(); 106 if (pos >= count) 107 return -1; 108 else 109 return buf[pos++]; 110 } 111 } 112 113 /** 114 * Reads characters into a portion of an array. 115 * 116 * <p> If {@code len} is zero, then no characters are read and {@code 0} is 117 * returned; otherwise, there is an attempt to read at least one character. 118 * If no character is available because the stream is at its end, the value 119 * {@code -1} is returned; otherwise, at least one character is read and 120 * stored into {@code cbuf}. 121 * 122 * @param cbuf {@inheritDoc} 123 * @param off {@inheritDoc} 124 * @param len {@inheritDoc} 125 * 126 * @return {@inheritDoc} 127 * 128 * @throws IndexOutOfBoundsException {@inheritDoc} 129 * @throws IOException {@inheritDoc} 130 */ read(char[] cbuf, int off, int len)131 public int read(char[] cbuf, int off, int len) throws IOException { 132 synchronized (lock) { 133 ensureOpen(); 134 Objects.checkFromIndexSize(off, len, cbuf.length); 135 if (len == 0) { 136 return 0; 137 } 138 139 if (pos >= count) { 140 return -1; 141 } 142 143 int avail = count - pos; 144 if (len > avail) { 145 len = avail; 146 } 147 if (len <= 0) { 148 return 0; 149 } 150 System.arraycopy(buf, pos, cbuf, off, len); 151 pos += len; 152 return len; 153 } 154 } 155 156 @Override read(CharBuffer target)157 public int read(CharBuffer target) throws IOException { 158 synchronized (lock) { 159 ensureOpen(); 160 161 if (pos >= count) { 162 return -1; 163 } 164 165 int avail = count - pos; 166 int len = Math.min(avail, target.remaining()); 167 target.put(buf, pos, len); 168 pos += len; 169 return len; 170 } 171 } 172 173 /** 174 * Skips characters. If the stream is already at its end before this method 175 * is invoked, then no characters are skipped and zero is returned. 176 * 177 * <p>The {@code n} parameter may be negative, even though the 178 * {@code skip} method of the {@link Reader} superclass throws 179 * an exception in this case. If {@code n} is negative, then 180 * this method does nothing and returns {@code 0}. 181 * 182 * @param n {@inheritDoc} 183 * 184 * @return {@inheritDoc} 185 * 186 * @throws IOException {@inheritDoc} 187 */ skip(long n)188 public long skip(long n) throws IOException { 189 synchronized (lock) { 190 ensureOpen(); 191 192 long avail = count - pos; 193 if (n > avail) { 194 n = avail; 195 } 196 if (n < 0) { 197 return 0; 198 } 199 pos += n; 200 return n; 201 } 202 } 203 204 /** 205 * Tells whether this stream is ready to be read. Character-array readers 206 * are always ready to be read. 207 * 208 * @throws IOException If an I/O error occurs 209 */ ready()210 public boolean ready() throws IOException { 211 synchronized (lock) { 212 ensureOpen(); 213 return (count - pos) > 0; 214 } 215 } 216 217 /** 218 * Tells whether this stream supports the mark() operation, which it does. 219 */ markSupported()220 public boolean markSupported() { 221 return true; 222 } 223 224 /** 225 * Marks the present position in the stream. Subsequent calls to reset() 226 * will reposition the stream to this point. 227 * 228 * @param readAheadLimit Limit on the number of characters that may be 229 * read while still preserving the mark. Because 230 * the stream's input comes from a character array, 231 * there is no actual limit; hence this argument is 232 * ignored. 233 * 234 * @throws IOException If an I/O error occurs 235 */ mark(int readAheadLimit)236 public void mark(int readAheadLimit) throws IOException { 237 synchronized (lock) { 238 ensureOpen(); 239 markedPos = pos; 240 } 241 } 242 243 /** 244 * Resets the stream to the most recent mark, or to the beginning if it has 245 * never been marked. 246 * 247 * @throws IOException If an I/O error occurs 248 */ reset()249 public void reset() throws IOException { 250 synchronized (lock) { 251 ensureOpen(); 252 pos = markedPos; 253 } 254 } 255 256 /** 257 * Closes the stream and releases any system resources associated with 258 * it. Once the stream has been closed, further read(), ready(), 259 * mark(), reset(), or skip() invocations will throw an IOException. 260 * Closing a previously closed stream has no effect. This method will block 261 * while there is another thread blocking on the reader. 262 */ close()263 public void close() { 264 synchronized (lock) { 265 buf = null; 266 } 267 } 268 } 269