001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.io.input;
018    
019    import java.io.Reader;
020    import java.io.Serializable;
021    
022    /**
023     * {@link Reader} implementation that can read from String, StringBuffer,
024     * StringBuilder or CharBuffer.
025     * <p>
026     * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.
027     *
028     * @version $Revision: 610516 $ $Date: 2008-01-09 19:05:05 +0000 (Wed, 09 Jan 2008) $
029     * @since Commons IO 1.4
030     */
031    public class CharSequenceReader extends Reader implements Serializable {
032    
033        private final CharSequence charSequence;
034        private int idx;
035        private int mark;
036    
037        /**
038         * Construct a new instance with the specified character sequence.
039         *
040         * @param charSequence The character sequence, may be <code>null</code>
041         */
042        public CharSequenceReader(CharSequence charSequence) {
043            this.charSequence = (charSequence != null ? charSequence : "");
044        }
045    
046        /**
047         * Close resets the file back to the start and removes any marked position.
048         */
049        public void close() {
050            idx = 0;
051            mark = 0;
052        }
053    
054        /**
055         * Mark the current position.
056         *
057         * @param readAheadLimit ignored
058         */
059        public void mark(int readAheadLimit) {
060            mark = idx;
061        }
062    
063        /**
064         * Mark is supported (returns true).
065         *
066         * @return <code>true</code>
067         */
068        public boolean markSupported() {
069            return true;
070        }
071    
072        /**
073         * Read a single character.
074         *
075         * @return the next character from the character sequence
076         * or -1 if the end has been reached.
077         */
078        public int read() {
079            if (idx >= charSequence.length()) {
080                return -1;
081            } else {
082                return charSequence.charAt(idx++);
083            }
084        }
085    
086        /**
087         * Read the sepcified number of characters into the array.
088         *
089         * @param array The array to store the characters in
090         * @param offset The starting position in the array to store
091         * @param length The maximum number of characters to read
092         * @return The number of characters read or -1 if there are
093         * no more
094         */
095        public int read(char[] array, int offset, int length) {
096            if (idx >= charSequence.length()) {
097                return -1;
098            }
099            if (array == null) {
100                throw new NullPointerException("Character array is missing");
101            }
102            if (length < 0 || (offset + length) > array.length) {
103                throw new IndexOutOfBoundsException("Array Size=" + array.length +
104                        ", offset=" + offset + ", length=" + length);
105            }
106            int count = 0;
107            for (int i = 0; i < length; i++) {
108                int c = read();
109                if (c == -1) {
110                    return count;
111                }
112                array[offset + i] = (char)c;
113                count++;
114            }
115            return count;
116        }
117    
118        /**
119         * Reset the reader to the last marked position (or the beginning if
120         * mark has not been called).
121         */
122        public void reset() {
123            idx = mark;
124        }
125    
126        /**
127         * Skip the specified number of characters.
128         *
129         * @param n The number of characters to skip
130         * @return The actual number of characters skipped
131         */
132        public long skip(long n) {
133            if (n < 0) {
134                throw new IllegalArgumentException(
135                        "Number of characters to skip is less than zero: " + n);
136            }
137            if (idx >= charSequence.length()) {
138                return -1;
139            }
140            int dest = (int)Math.min(charSequence.length(), (idx + n));
141            int count = dest - idx;
142            idx = dest;
143            return count;
144        }
145    
146        /**
147         * Return a String representation of the underlying
148         * character sequence.
149         *
150         * @return The contents of the character sequence
151         */
152        public String toString() {
153            return charSequence.toString();
154        }
155    }