Home | History | Annotate | Download | only in io
      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    */
     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 }
    133