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    }