1 /* 2 * Copyright (C) 2013 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.io; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static com.google.common.base.Preconditions.checkPositionIndexes; 22 23 import java.io.IOException; 24 import java.io.Reader; 25 import java.nio.CharBuffer; 26 27 /** 28 * A {@link Reader} that reads the characters in a {@link CharSequence}. Like {@code StringReader}, 29 * but works with any {@link CharSequence}. 30 * 31 * @author Colin Decker 32 */ 33 // TODO(user): make this public? as a type, or a method in CharStreams? 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 */ CharSequenceReader(CharSequence seq)43 public CharSequenceReader(CharSequence seq) { 44 this.seq = checkNotNull(seq); 45 } 46 checkOpen()47 private void checkOpen() throws IOException { 48 if (seq == null) { 49 throw new IOException("reader closed"); 50 } 51 } 52 hasRemaining()53 private boolean hasRemaining() { 54 return remaining() > 0; 55 } 56 remaining()57 private int remaining() { 58 return seq.length() - pos; 59 } 60 61 @Override read(CharBuffer target)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 read()76 public synchronized int read() throws IOException { 77 checkOpen(); 78 return hasRemaining() ? seq.charAt(pos++) : -1; 79 } 80 81 @Override read(char[] cbuf, int off, int len)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 skip(long n)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 ready()105 public synchronized boolean ready() throws IOException { 106 checkOpen(); 107 return true; 108 } 109 110 @Override markSupported()111 public boolean markSupported() { 112 return true; 113 } 114 115 @Override mark(int readAheadLimit)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 reset()123 public synchronized void reset() throws IOException { 124 checkOpen(); 125 pos = mark; 126 } 127 128 @Override close()129 public synchronized void close() throws IOException { 130 seq = null; 131 } 132 } 133