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 $Id: CharSequenceReader.java 1307461 2012-03-30 15:12:29Z ggregory $
029     * @since 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}
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        @Override
050        public void close() {
051            idx = 0;
052            mark = 0;
053        }
054    
055        /**
056         * Mark the current position.
057         *
058         * @param readAheadLimit ignored
059         */
060        @Override
061        public void mark(int readAheadLimit) {
062            mark = idx;
063        }
064    
065        /**
066         * Mark is supported (returns true).
067         *
068         * @return {@code true}
069         */
070        @Override
071        public boolean markSupported() {
072            return true;
073        }
074    
075        /**
076         * Read a single character.
077         *
078         * @return the next character from the character sequence
079         * or -1 if the end has been reached.
080         */
081        @Override
082        public int read() {
083            if (idx >= charSequence.length()) {
084                return -1;
085            } else {
086                return charSequence.charAt(idx++);
087            }
088        }
089    
090        /**
091         * Read the sepcified number of characters into the array.
092         *
093         * @param array The array to store the characters in
094         * @param offset The starting position in the array to store
095         * @param length The maximum number of characters to read
096         * @return The number of characters read or -1 if there are
097         * no more
098         */
099        @Override
100        public int read(char[] array, int offset, int length) {
101            if (idx >= charSequence.length()) {
102                return -1;
103            }
104            if (array == null) {
105                throw new NullPointerException("Character array is missing");
106            }
107            if (length < 0 || offset < 0 || offset + length > array.length) {
108                throw new IndexOutOfBoundsException("Array Size=" + array.length +
109                        ", offset=" + offset + ", length=" + length);
110            }
111            int count = 0;
112            for (int i = 0; i < length; i++) {
113                int c = read();
114                if (c == -1) {
115                    return count;
116                }
117                array[offset + i] = (char)c;
118                count++;
119            }
120            return count;
121        }
122    
123        /**
124         * Reset the reader to the last marked position (or the beginning if
125         * mark has not been called).
126         */
127        @Override
128        public void reset() {
129            idx = mark;
130        }
131    
132        /**
133         * Skip the specified number of characters.
134         *
135         * @param n The number of characters to skip
136         * @return The actual number of characters skipped
137         */
138        @Override
139        public long skip(long n) {
140            if (n < 0) {
141                throw new IllegalArgumentException(
142                        "Number of characters to skip is less than zero: " + n);
143            }
144            if (idx >= charSequence.length()) {
145                return -1;
146            }
147            int dest = (int)Math.min(charSequence.length(), idx + n);
148            int count = dest - idx;
149            idx = dest;
150            return count;
151        }
152    
153        /**
154         * Return a String representation of the underlying
155         * character sequence.
156         *
157         * @return The contents of the character sequence
158         */
159        @Override
160        public String toString() {
161            return charSequence.toString();
162        }
163    }