001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://res.imagero.com
005 * http://uio.imagero.com
006 *
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions are met:
009 *
010 * o Redistributions of source code must retain the above copyright notice,
011 * this list of conditions and the following disclaimer.
012 *
013 * o Redistributions in binary form must reproduce the above copyright notice,
014 * this list of conditions and the following disclaimer in the documentation
015 * and/or other materials provided with the distribution.
016 *
017 * o Neither the name of Andrey Kuznetsov nor the names of
018 * its contributors may be used to endorse or promote products derived
019 * from this software without specific prior written permission.
020 *
021 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
022 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
023 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
024 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
025 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
026 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
027 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
028 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
029 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
030 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
031 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
032 */
033 package com.imagero.uio.io;
034
035 import java.io.IOException;
036 import java.io.Reader;
037
038 /**
039 * A character stream whose source is a string array.
040 * Very similar to java.io.StringReader.
041 * @author Andrey Kuznetsov
042 */
043 public class StringArrayReader extends Reader {
044
045 private String[] str;
046 private int[] ends;
047 private int[] starts;
048 private int length;
049 private int next = 0;
050 private int mark = 0;
051
052 int current;
053
054 /**
055 * Create a new StringArrayReader.
056 *
057 * @param s String array providing the character stream.
058 */
059 public StringArrayReader(String[] s) {
060 this.str = s;
061 for (int i = 0; i < s.length; i++) {
062 length += s[i].length();
063 }
064
065 starts = new int[s.length];
066 for (int i = 1; i < s.length; i++) {
067 starts[i] = starts[i - 1] + s[i - 1].length();
068 }
069
070 ends = new int[s.length];
071 ends[0] = s[0].length();
072 for (int i = 1; i < s.length; i++) {
073 ends[i] = ends[i - 1] + s[i].length();
074 }
075 }
076
077 /**
078 * Check to make sure that the stream has not been closed
079 */
080 private void ensureOpen() throws IOException {
081 if (str == null) {
082 throw new IOException("Stream closed");
083 }
084 }
085
086 /**
087 * Read a single character.
088 * @return The character read, or -1 if the end of the stream has been reached
089 * @exception IOException If an I/O error occurs
090 */
091 public int read() throws IOException {
092 synchronized (lock) {
093 ensureOpen();
094 if (next >= length) {
095 return -1;
096 }
097 if (next >= ends[current]) {
098 current++;
099 }
100 return str[current].charAt(next++ - starts[current]);
101 }
102 }
103
104 /**
105 * Read characters into a portion of supplied char array.
106 *
107 * @param cbuf Destination char array
108 * @param off Where to start writing characters
109 * @param len Maximum number of characters to read
110 * @return The number of characters read, or -1 if the end of the stream has been reached
111 * @exception IOException If an I/O error occurs
112 */
113 public int read(char cbuf[], int off, int len) throws IOException {
114 synchronized (lock) {
115 ensureOpen();
116 if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) {
117 throw new IndexOutOfBoundsException();
118 }
119 else if (len == 0) {
120 return 0;
121 }
122 if (next >= length) {
123 return -1;
124 }
125 if (next >= ends[current]) {
126 current++;
127 }
128 int n = Math.min(ends[current] - next, len);
129 str[current].getChars(next - starts[current], next - starts[current] + n, cbuf, off);
130 next += n;
131 return n;
132 }
133 }
134
135 /**
136 * Skip characters.
137 * @exception IOException If an I/O error occurs
138 */
139 public long skip(long ns) throws IOException {
140 synchronized (lock) {
141 ensureOpen();
142 if (next >= length) {
143 return 0;
144 }
145 if (next >= ends[current]) {
146 current++;
147 }
148 long n = Math.min(ends[current] - next, ns);
149 next += n;
150 return n;
151 }
152 }
153
154 /**
155 * Tell whether this stream is ready to be read.
156 * @return True if the next read() is guaranteed not to block for input
157 * @exception IOException If the stream is closed
158 */
159 public boolean ready() throws IOException {
160 synchronized (lock) {
161 ensureOpen();
162 return true;
163 }
164 }
165
166 /**
167 * Tell whether this stream supports the mark() operation.
168 * @return true
169 */
170 public boolean markSupported() {
171 return true;
172 }
173
174 /**
175 * Mark the present position in the stream.
176 * Subsequent calls to reset() will reposition the stream to this point.
177 * @param readAheadLimit Limit on the number of characters that may be
178 * read while still preserving the mark.
179 * @exception IllegalArgumentException If readAheadLimit is < 0
180 * @exception IOException If an I/O error occurs
181 */
182 public void mark(int readAheadLimit) throws IOException {
183 if (readAheadLimit < 0) {
184 throw new IllegalArgumentException("Read-ahead limit < 0");
185 }
186 synchronized (lock) {
187 ensureOpen();
188 mark = next;
189 }
190 }
191
192 /**
193 * Reset the stream to the most recent mark,
194 * or to the beginning of the string if it has never been marked.
195 * @exception IOException If an I/O error occurs
196 */
197 public void reset() throws IOException {
198 synchronized (lock) {
199 ensureOpen();
200 next = mark;
201 if (starts[current] > next) {
202 current++;
203 }
204 }
205 }
206
207 /**
208 * Close the stream.
209 */
210 public void close() {
211 str = null;
212 }
213 }