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 tests.support;
19
20import java.io.IOException;
21import java.io.Reader;
22
23public class Support_StringReader extends Reader {
24    private String str;
25
26    private int markpos = -1;
27
28    private int pos = 0;
29
30    private int count;
31
32    /**
33     * Construct a StringReader on the String <code>str</code>. The size of
34     * the reader is set to the <code>length()</code> of the String and the
35     * Object to synchronize access through is set to <code>str</code>.
36     *
37     * @param str
38     *            the String to filter reads on.
39     */
40    public Support_StringReader(String str) {
41        super(str);
42        this.str = str;
43        this.count = str.length();
44    }
45
46    /**
47     * This method closes this StringReader. Once it is closed, you can no
48     * longer read from it. Only the first invocation of this method has any
49     * effect.
50     *
51     */
52    @Override
53    public void close() {
54        synchronized (lock) {
55            if (isOpen()) {
56                str = null;
57            }
58        }
59    }
60
61    /**
62     * Answer a boolean indicating whether or not this StringReader is open.
63     */
64    private boolean isOpen() {
65        return str != null;
66    }
67
68    /**
69     * Set a Mark position in this Reader. The parameter <code>readLimit</code>
70     * is ignored for StringReaders. Sending reset() will reposition the reader
71     * back to the marked position provided the mark has not been invalidated.
72     *
73     * @param readlimit
74     *            ignored for StringReaders.
75     *
76     * @exception java.io.IOException
77     *                If an error occurs attempting mark this StringReader.
78     */
79    @Override
80    public void mark(int readLimit) throws IOException {
81        if (readLimit >= 0) {
82            synchronized (lock) {
83                if (isOpen()) {
84                    markpos = pos;
85                } else {
86                    throw new IOException("StringReader is closed");
87                }
88            }
89        } else {
90            throw new IllegalArgumentException();
91        }
92    }
93
94    /**
95     * Answers a boolean indicating whether or not this StringReader supports
96     * mark() and reset(). This method always returns true.
97     *
98     * @return <code>true</code> if mark() and reset() are supported,
99     *         <code>false</code> otherwise. This implementation always
100     *         returns <code>true</code>.
101     */
102    @Override
103    public boolean markSupported() {
104        return true;
105    }
106
107    /**
108     * Reads a single character from this StringReader and returns the result as
109     * an int. The 2 higher-order bytes are set to 0. If the end of reader was
110     * encountered then return -1.
111     *
112     * @return the character read or -1 if end of reader.
113     *
114     * @exception java.io.IOException
115     *                If the StringReader is already closed.
116     */
117    @Override
118    public int read() throws IOException {
119        synchronized (lock) {
120            if (isOpen()) {
121                if (pos != count) {
122                    return str.charAt(pos++);
123                }
124                return -1;
125            }
126            throw new IOException("StringReader is closed");
127        }
128    }
129
130    /**
131     * Reads at most <code>count</code> characters from this StringReader and
132     * stores them at <code>offset</code> in the character array
133     * <code>buf</code>. Returns the number of characters actually read or -1
134     * if the end of reader was encountered.
135     *
136     * @param buf
137     *            character array to store the read characters
138     * @param offset
139     *            offset in buf to store the read characters
140     * @param count
141     *            maximum number of characters to read
142     * @return the number of characters read or -1 if end of reader.
143     *
144     * @exception java.io.IOException
145     *                If the StringReader is closed.
146     */
147    @Override
148    public int read(char buf[], int offset, int count) throws IOException {
149        // avoid int overflow
150        if (0 <= offset && offset <= buf.length && 0 <= count
151                && count <= buf.length - offset) {
152            synchronized (lock) {
153                if (isOpen()) {
154                    if (pos == this.count) {
155                        return -1;
156                    }
157                    int end = pos + count > this.count ? this.count : pos
158                            + count;
159                    str.getChars(pos, end, buf, offset);
160                    int read = end - pos;
161                    pos = end;
162                    return read;
163                }
164                throw new IOException("StringReader is closed");
165            }
166        }
167        throw new ArrayIndexOutOfBoundsException();
168    }
169
170    /**
171     * Answers a <code>boolean</code> indicating whether or not this
172     * StringReader is ready to be read without blocking. If the result is
173     * <code>true</code>, the next <code>read()</code> will not block. If
174     * the result is <code>false</code> this Reader may or may not block when
175     * <code>read()</code> is sent. The implementation in StringReader always
176     * returns <code>true</code> even when it has been closed.
177     *
178     * @return <code>true</code> if the receiver will not block when
179     *         <code>read()</code> is called, <code>false</code> if unknown
180     *         or blocking will occur.
181     *
182     * @exception java.io.IOException
183     *                If an IO error occurs.
184     */
185    @Override
186    public boolean ready() throws IOException {
187        synchronized (lock) {
188            if (isOpen()) {
189                return true;
190            }
191            throw new IOException("StringReader is closed");
192        }
193    }
194
195    /**
196     * Reset this StringReader's position to the last <code>mark()</code>
197     * location. Invocations of <code>read()/skip()</code> will occur from
198     * this new location. If this Reader was not marked, the StringReader is
199     * reset to the beginning of the String.
200     *
201     * @exception java.io.IOException
202     *                If this StringReader has already been closed.
203     */
204    @Override
205    public void reset() throws IOException {
206        synchronized (lock) {
207            if (isOpen()) {
208                pos = markpos != -1 ? markpos : 0;
209            } else {
210                throw new IOException("StringReader is closed");
211            }
212        }
213    }
214
215    /**
216     * Skips <code>count</code> number of characters in this StringReader.
217     * Subsequent <code>read()</code>'s will not return these characters
218     * unless <code>reset()</code> is used.
219     *
220     * @param count
221     *            The number of characters to skip.
222     * @return the number of characters actually skipped.
223     *
224     * @exception java.io.IOException
225     *                If this StringReader has already been closed.
226     */
227    @Override
228    public long skip(long count) throws IOException {
229        synchronized (lock) {
230            if (isOpen()) {
231                if (count <= 0) {
232                    return 0;
233                }
234                long skipped = 0;
235                if (count < this.count - pos) {
236                    pos = pos + (int) count;
237                    skipped = count;
238                } else {
239                    skipped = this.count - pos;
240                    pos = this.count;
241                }
242                return skipped;
243            }
244            throw new IOException("StringReader is closed");
245        }
246    }
247}
248