View Javadoc
1   /*
2    * Copyright (C) 2013 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.io;
16  
17  import static com.google.common.base.Preconditions.checkArgument;
18  import static com.google.common.base.Preconditions.checkNotNull;
19  import static com.google.common.base.Preconditions.checkPositionIndexes;
20  
21  import com.google.common.annotations.GwtIncompatible;
22  import java.io.IOException;
23  import java.io.Reader;
24  import java.nio.CharBuffer;
25  
26  /**
27   * A {@link Reader} that reads the characters in a {@link CharSequence}. Like {@code StringReader},
28   * but works with any {@link CharSequence}.
29   *
30   * @author Colin Decker
31   */
32  // TODO(cgdecker): make this public? as a type, or a method in CharStreams?
33  @GwtIncompatible
34  final class CharSequenceReader extends Reader {
35  
36    private CharSequence seq;
37    private int pos;
38    private int mark;
39  
40    /**
41     * Creates a new reader wrapping the given character sequence.
42     */
43    public CharSequenceReader(CharSequence seq) {
44      this.seq = checkNotNull(seq);
45    }
46  
47    private void checkOpen() throws IOException {
48      if (seq == null) {
49        throw new IOException("reader closed");
50      }
51    }
52  
53    private boolean hasRemaining() {
54      return remaining() > 0;
55    }
56  
57    private int remaining() {
58      return seq.length() - pos;
59    }
60  
61    @Override
62    public synchronized int read(CharBuffer target) throws IOException {
63      checkNotNull(target);
64      checkOpen();
65      if (!hasRemaining()) {
66        return -1;
67      }
68      int charsToRead = Math.min(target.remaining(), remaining());
69      for (int i = 0; i < charsToRead; i++) {
70        target.put(seq.charAt(pos++));
71      }
72      return charsToRead;
73    }
74  
75    @Override
76    public synchronized int read() throws IOException {
77      checkOpen();
78      return hasRemaining() ? seq.charAt(pos++) : -1;
79    }
80  
81    @Override
82    public synchronized int read(char[] cbuf, int off, int len) throws IOException {
83      checkPositionIndexes(off, off + len, cbuf.length);
84      checkOpen();
85      if (!hasRemaining()) {
86        return -1;
87      }
88      int charsToRead = Math.min(len, remaining());
89      for (int i = 0; i < charsToRead; i++) {
90        cbuf[off + i] = seq.charAt(pos++);
91      }
92      return charsToRead;
93    }
94  
95    @Override
96    public synchronized long skip(long n) throws IOException {
97      checkArgument(n >= 0, "n (%s) may not be negative", n);
98      checkOpen();
99      int charsToSkip = (int) Math.min(remaining(), n); // safe because remaining is an int
100     pos += charsToSkip;
101     return charsToSkip;
102   }
103 
104   @Override
105   public synchronized boolean ready() throws IOException {
106     checkOpen();
107     return true;
108   }
109 
110   @Override
111   public boolean markSupported() {
112     return true;
113   }
114 
115   @Override
116   public synchronized void mark(int readAheadLimit) throws IOException {
117     checkArgument(readAheadLimit >= 0, "readAheadLimit (%s) may not be negative", readAheadLimit);
118     checkOpen();
119     mark = pos;
120   }
121 
122   @Override
123   public synchronized void reset() throws IOException {
124     checkOpen();
125     pos = mark;
126   }
127 
128   @Override
129   public synchronized void close() throws IOException {
130     seq = null;
131   }
132 }