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 */
17package org.apache.commons.io.input;
18
19import java.io.Reader;
20import java.io.Serializable;
21
22/**
23 * {@link Reader} implementation that can read from String, StringBuffer,
24 * StringBuilder or CharBuffer.
25 * <p>
26 * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.
27 *
28 * @version $Revision: 610516 $ $Date: 2008-01-09 19:05:05 +0000 (Wed, 09 Jan 2008) $
29 * @since Commons IO 1.4
30 */
31public class CharSequenceReader extends Reader implements Serializable {
32
33    private final CharSequence charSequence;
34    private int idx;
35    private int mark;
36
37    /**
38     * Construct a new instance with the specified character sequence.
39     *
40     * @param charSequence The character sequence, may be <code>null</code>
41     */
42    public CharSequenceReader(CharSequence charSequence) {
43        this.charSequence = (charSequence != null ? charSequence : "");
44    }
45
46    /**
47     * Close resets the file back to the start and removes any marked position.
48     */
49    public void close() {
50        idx = 0;
51        mark = 0;
52    }
53
54    /**
55     * Mark the current position.
56     *
57     * @param readAheadLimit ignored
58     */
59    public void mark(int readAheadLimit) {
60        mark = idx;
61    }
62
63    /**
64     * Mark is supported (returns true).
65     *
66     * @return <code>true</code>
67     */
68    public boolean markSupported() {
69        return true;
70    }
71
72    /**
73     * Read a single character.
74     *
75     * @return the next character from the character sequence
76     * or -1 if the end has been reached.
77     */
78    public int read() {
79        if (idx >= charSequence.length()) {
80            return -1;
81        } else {
82            return charSequence.charAt(idx++);
83        }
84    }
85
86    /**
87     * Read the sepcified number of characters into the array.
88     *
89     * @param array The array to store the characters in
90     * @param offset The starting position in the array to store
91     * @param length The maximum number of characters to read
92     * @return The number of characters read or -1 if there are
93     * no more
94     */
95    public int read(char[] array, int offset, int length) {
96        if (idx >= charSequence.length()) {
97            return -1;
98        }
99        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}
156