Home | History | Annotate | Download | only in io
      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  */
     17 
     18 package java.io;
     19 
     20 import java.util.Arrays;
     21 
     22 /**
     23  * A specialized {@link Reader} that reads characters from a {@code String} in
     24  * a sequential manner.
     25  *
     26  * @see StringWriter
     27  */
     28 public class StringReader extends Reader {
     29     private String str;
     30 
     31     private int markpos = -1;
     32 
     33     private int pos;
     34 
     35     private int count;
     36 
     37     /**
     38      * Construct a new {@code StringReader} with {@code str} as source. The size
     39      * of the reader is set to the {@code length()} of the string and the Object
     40      * to synchronize access through is set to {@code str}.
     41      *
     42      * @param str
     43      *            the source string for this reader.
     44      */
     45     public StringReader(String str) {
     46         this.str = str;
     47         this.count = str.length();
     48     }
     49 
     50     /**
     51      * Closes this reader. Once it is closed, read operations on this reader
     52      * will throw an {@code IOException}. Only the first invocation of this
     53      * method has any effect.
     54      */
     55     @Override
     56     public void close() {
     57         str = null;
     58     }
     59 
     60     /**
     61      * Returns a boolean indicating whether this reader is closed.
     62      *
     63      * @return {@code true} if closed, otherwise {@code false}.
     64      */
     65     private boolean isClosed() {
     66         return str == null;
     67     }
     68 
     69     /**
     70      * Sets a mark position in this reader. The parameter {@code readLimit} is
     71      * ignored for this class. Calling {@code reset()} will reposition the
     72      * reader back to the marked position.
     73      *
     74      * @param readLimit
     75      *            ignored for {@code StringReader} instances.
     76      * @throws IllegalArgumentException
     77      *             if {@code readLimit < 0}.
     78      * @throws IOException
     79      *             if this reader is closed.
     80      * @see #markSupported()
     81      * @see #reset()
     82      */
     83     @Override
     84     public void mark(int readLimit) throws IOException {
     85         if (readLimit < 0) {
     86             throw new IllegalArgumentException();
     87         }
     88 
     89         synchronized (lock) {
     90             checkNotClosed();
     91             markpos = pos;
     92         }
     93     }
     94 
     95     private void checkNotClosed() throws IOException {
     96         if (isClosed()) {
     97             throw new IOException("StringReader is closed");
     98         }
     99     }
    100 
    101     /**
    102      * Indicates whether this reader supports the {@code mark()} and {@code
    103      * reset()} methods. This implementation returns {@code true}.
    104      *
    105      * @return always {@code true}.
    106      */
    107     @Override
    108     public boolean markSupported() {
    109         return true;
    110     }
    111 
    112     /**
    113      * Reads a single character from the source string and returns it as an
    114      * integer with the two higher-order bytes set to 0. Returns -1 if the end
    115      * of the source string has been reached.
    116      *
    117      * @return the character read or -1 if the end of the source string has been
    118      *         reached.
    119      * @throws IOException
    120      *             if this reader is closed.
    121      */
    122     @Override
    123     public int read() throws IOException {
    124         synchronized (lock) {
    125             checkNotClosed();
    126             if (pos != count) {
    127                 return str.charAt(pos++);
    128             }
    129             return -1;
    130         }
    131     }
    132 
    133     /**
    134      * Reads at most {@code len} characters from the source string and stores
    135      * them at {@code offset} in the character array {@code buf}. Returns the
    136      * number of characters actually read or -1 if the end of the source string
    137      * has been reached.
    138      *
    139      * @param buf
    140      *            the character array to store the characters read.
    141      * @param offset
    142      *            the initial position in {@code buffer} to store the characters
    143      *            read from this reader.
    144      * @param len
    145      *            the maximum number of characters to read.
    146      * @return the number of characters read or -1 if the end of the reader has
    147      *         been reached.
    148      * @throws IndexOutOfBoundsException
    149      *             if {@code offset < 0} or {@code len < 0}, or if
    150      *             {@code offset + len} is greater than the size of {@code buf}.
    151      * @throws IOException
    152      *             if this reader is closed.
    153      */
    154     @Override
    155     public int read(char[] buf, int offset, int len) throws IOException {
    156         synchronized (lock) {
    157             checkNotClosed();
    158             Arrays.checkOffsetAndCount(buf.length, offset, len);
    159             if (len == 0) {
    160                 return 0;
    161             }
    162             if (pos == this.count) {
    163                 return -1;
    164             }
    165             int end = pos + len > this.count ? this.count : pos + len;
    166             str.getChars(pos, end, buf, offset);
    167             int read = end - pos;
    168             pos = end;
    169             return read;
    170         }
    171     }
    172 
    173     /**
    174      * Indicates whether this reader is ready to be read without blocking. This
    175      * implementation always returns {@code true}.
    176      *
    177      * @return always {@code true}.
    178      * @throws IOException
    179      *             if this reader is closed.
    180      * @see #read()
    181      * @see #read(char[], int, int)
    182      */
    183     @Override
    184     public boolean ready() throws IOException {
    185         synchronized (lock) {
    186             checkNotClosed();
    187             return true;
    188         }
    189     }
    190 
    191     /**
    192      * Resets this reader's position to the last {@code mark()} location.
    193      * Invocations of {@code read()} and {@code skip()} will occur from this new
    194      * location. If this reader has not been marked, it is reset to the
    195      * beginning of the source string.
    196      *
    197      * @throws IOException
    198      *             if this reader is closed.
    199      * @see #mark(int)
    200      * @see #markSupported()
    201      */
    202     @Override
    203     public void reset() throws IOException {
    204         synchronized (lock) {
    205             checkNotClosed();
    206             pos = markpos != -1 ? markpos : 0;
    207         }
    208     }
    209 
    210     /**
    211      * Moves {@code charCount} characters in the source string. Unlike the {@link
    212      * Reader#skip(long) overridden method}, this method may skip negative skip
    213      * distances: this rewinds the input so that characters may be read again.
    214      * When the end of the source string has been reached, the input cannot be
    215      * rewound.
    216      *
    217      * @param charCount
    218      *            the maximum number of characters to skip. Positive values skip
    219      *            forward; negative values skip backward.
    220      * @return the number of characters actually skipped. This is bounded below
    221      *            by the number of characters already read and above by the
    222      *            number of characters remaining:<br> {@code -(num chars already
    223      *            read) <= distance skipped <= num chars remaining}.
    224      * @throws IOException
    225      *             if this reader is closed.
    226      * @see #mark(int)
    227      * @see #markSupported()
    228      * @see #reset()
    229      */
    230     @Override
    231     public long skip(long charCount) throws IOException {
    232         synchronized (lock) {
    233             checkNotClosed();
    234 
    235             int minSkip = -pos;
    236             int maxSkip = count - pos;
    237 
    238             if (maxSkip == 0 || charCount > maxSkip) {
    239                 charCount = maxSkip; // no rewinding if we're at the end
    240             } else if (charCount < minSkip) {
    241                 charCount = minSkip;
    242             }
    243 
    244             pos += charCount;
    245             return charCount;
    246         }
    247     }
    248 }
    249