Support_StringReader.java revision e98fbf8686c5289bf03fe5c3de7ff82d3a77104d
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