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