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.nio.ByteBuffer;
     21 import java.nio.CharBuffer;
     22 import java.nio.charset.Charset;
     23 import java.nio.charset.CharsetDecoder;
     24 import java.nio.charset.CoderResult;
     25 import java.nio.charset.CodingErrorAction;
     26 import java.nio.charset.MalformedInputException;
     27 import java.nio.charset.UnmappableCharacterException;
     28 import java.util.Arrays;
     29 
     30 /**
     31  * A class for turning a byte stream into a character stream. Data read from the
     32  * source input stream is converted into characters by either a default or a
     33  * provided character converter. The default encoding is taken from the
     34  * "file.encoding" system property. {@code InputStreamReader} contains a buffer
     35  * of bytes read from the source stream and converts these into characters as
     36  * needed. The buffer size is 8K.
     37  *
     38  * @see OutputStreamWriter
     39  */
     40 public class InputStreamReader extends Reader {
     41     private InputStream in;
     42 
     43     private boolean endOfInput = false;
     44 
     45     private CharsetDecoder decoder;
     46 
     47     private final ByteBuffer bytes = ByteBuffer.allocate(8192);
     48 
     49     /**
     50      * Constructs a new {@code InputStreamReader} on the {@link InputStream}
     51      * {@code in}. This constructor sets the character converter to the encoding
     52      * specified in the "file.encoding" property and falls back to ISO 8859_1
     53      * (ISO-Latin-1) if the property doesn't exist.
     54      *
     55      * @param in
     56      *            the input stream from which to read characters.
     57      */
     58     public InputStreamReader(InputStream in) {
     59         this(in, Charset.defaultCharset());
     60     }
     61 
     62     /**
     63      * Constructs a new InputStreamReader on the InputStream {@code in}. The
     64      * character converter that is used to decode bytes into characters is
     65      * identified by name by {@code enc}. If the encoding cannot be found, an
     66      * UnsupportedEncodingException error is thrown.
     67      *
     68      * @param in
     69      *            the InputStream from which to read characters.
     70      * @param enc
     71      *            identifies the character converter to use.
     72      * @throws NullPointerException
     73      *             if {@code enc} is {@code null}.
     74      * @throws UnsupportedEncodingException
     75      *             if the encoding specified by {@code enc} cannot be found.
     76      */
     77     public InputStreamReader(InputStream in, final String enc)
     78             throws UnsupportedEncodingException {
     79         super(in);
     80         if (enc == null) {
     81             throw new NullPointerException();
     82         }
     83         this.in = in;
     84         try {
     85             decoder = Charset.forName(enc).newDecoder().onMalformedInput(
     86                     CodingErrorAction.REPLACE).onUnmappableCharacter(
     87                     CodingErrorAction.REPLACE);
     88         } catch (IllegalArgumentException e) {
     89             throw (UnsupportedEncodingException)
     90                     new UnsupportedEncodingException(enc).initCause(e);
     91         }
     92         bytes.limit(0);
     93     }
     94 
     95     /**
     96      * Constructs a new InputStreamReader on the InputStream {@code in} and
     97      * CharsetDecoder {@code dec}.
     98      *
     99      * @param in
    100      *            the source InputStream from which to read characters.
    101      * @param dec
    102      *            the CharsetDecoder used by the character conversion.
    103      */
    104     public InputStreamReader(InputStream in, CharsetDecoder dec) {
    105         super(in);
    106         dec.averageCharsPerByte();
    107         this.in = in;
    108         decoder = dec;
    109         bytes.limit(0);
    110     }
    111 
    112     /**
    113      * Constructs a new InputStreamReader on the InputStream {@code in} and
    114      * Charset {@code charset}.
    115      *
    116      * @param in
    117      *            the source InputStream from which to read characters.
    118      * @param charset
    119      *            the Charset that defines the character converter
    120      */
    121     public InputStreamReader(InputStream in, Charset charset) {
    122         super(in);
    123         this.in = in;
    124         decoder = charset.newDecoder().onMalformedInput(
    125                 CodingErrorAction.REPLACE).onUnmappableCharacter(
    126                 CodingErrorAction.REPLACE);
    127         bytes.limit(0);
    128     }
    129 
    130     /**
    131      * Closes this reader. This implementation closes the source InputStream and
    132      * releases all local storage.
    133      *
    134      * @throws IOException
    135      *             if an error occurs attempting to close this reader.
    136      */
    137     @Override
    138     public void close() throws IOException {
    139         synchronized (lock) {
    140             if (decoder != null) {
    141                 decoder.reset();
    142             }
    143             decoder = null;
    144             if (in != null) {
    145                 in.close();
    146                 in = null;
    147             }
    148         }
    149     }
    150 
    151     /**
    152      * Returns the historical name of the encoding used by this writer to convert characters to
    153      * bytes, or null if this writer has been closed. Most callers should probably keep
    154      * track of the String or Charset they passed in; this method may not return the same
    155      * name.
    156      */
    157     public String getEncoding() {
    158         if (!isOpen()) {
    159             return null;
    160         }
    161         return HistoricalCharsetNames.get(decoder.charset());
    162     }
    163 
    164     /**
    165      * Reads a single character from this reader and returns it as an integer
    166      * with the two higher-order bytes set to 0. Returns -1 if the end of the
    167      * reader has been reached. The byte value is either obtained from
    168      * converting bytes in this reader's buffer or by first filling the buffer
    169      * from the source InputStream and then reading from the buffer.
    170      *
    171      * @return the character read or -1 if the end of the reader has been
    172      *         reached.
    173      * @throws IOException
    174      *             if this reader is closed or some other I/O error occurs.
    175      */
    176     @Override
    177     public int read() throws IOException {
    178         synchronized (lock) {
    179             if (!isOpen()) {
    180                 throw new IOException("InputStreamReader is closed");
    181             }
    182             char[] buf = new char[1];
    183             return read(buf, 0, 1) != -1 ? buf[0] : -1;
    184         }
    185     }
    186 
    187     /**
    188      * Reads at most {@code length} characters from this reader and stores them
    189      * at position {@code offset} in the character array {@code buf}. Returns
    190      * the number of characters actually read or -1 if the end of the reader has
    191      * been reached. The bytes are either obtained from converting bytes in this
    192      * reader's buffer or by first filling the buffer from the source
    193      * InputStream and then reading from the buffer.
    194      *
    195      * @param buffer
    196      *            the array to store the characters read.
    197      * @param offset
    198      *            the initial position in {@code buf} to store the characters
    199      *            read from this reader.
    200      * @param length
    201      *            the maximum number of characters to read.
    202      * @return the number of characters read or -1 if the end of the reader has
    203      *         been reached.
    204      * @throws IndexOutOfBoundsException
    205      *             if {@code offset < 0} or {@code length < 0}, or if
    206      *             {@code offset + length} is greater than the length of
    207      *             {@code buf}.
    208      * @throws IOException
    209      *             if this reader is closed or some other I/O error occurs.
    210      */
    211     @Override
    212     public int read(char[] buffer, int offset, int length) throws IOException {
    213         synchronized (lock) {
    214             if (!isOpen()) {
    215                 throw new IOException("InputStreamReader is closed");
    216             }
    217 
    218             Arrays.checkOffsetAndCount(buffer.length, offset, length);
    219             if (length == 0) {
    220                 return 0;
    221             }
    222 
    223             CharBuffer out = CharBuffer.wrap(buffer, offset, length);
    224             CoderResult result = CoderResult.UNDERFLOW;
    225 
    226             // bytes.remaining() indicates number of bytes in buffer
    227             // when 1-st time entered, it'll be equal to zero
    228             boolean needInput = !bytes.hasRemaining();
    229 
    230             while (out.hasRemaining()) {
    231                 // fill the buffer if needed
    232                 if (needInput) {
    233                     try {
    234                         if (in.available() == 0 && out.position() > offset) {
    235                             // we could return the result without blocking read
    236                             break;
    237                         }
    238                     } catch (IOException e) {
    239                         // available didn't work so just try the read
    240                     }
    241 
    242                     int desiredByteCount = bytes.capacity() - bytes.limit();
    243                     int off = bytes.arrayOffset() + bytes.limit();
    244                     int actualByteCount = in.read(bytes.array(), off, desiredByteCount);
    245 
    246                     if (actualByteCount == -1) {
    247                         endOfInput = true;
    248                         break;
    249                     } else if (actualByteCount == 0) {
    250                         break;
    251                     }
    252                     bytes.limit(bytes.limit() + actualByteCount);
    253                     needInput = false;
    254                 }
    255 
    256                 // decode bytes
    257                 result = decoder.decode(bytes, out, false);
    258 
    259                 if (result.isUnderflow()) {
    260                     // compact the buffer if no space left
    261                     if (bytes.limit() == bytes.capacity()) {
    262                         bytes.compact();
    263                         bytes.limit(bytes.position());
    264                         bytes.position(0);
    265                     }
    266                     needInput = true;
    267                 } else {
    268                     break;
    269                 }
    270             }
    271 
    272             if (result == CoderResult.UNDERFLOW && endOfInput) {
    273                 result = decoder.decode(bytes, out, true);
    274                 decoder.flush(out);
    275                 decoder.reset();
    276             }
    277             if (result.isMalformed() || result.isUnmappable()) {
    278                 result.throwException();
    279             }
    280 
    281             return out.position() - offset == 0 ? -1 : out.position() - offset;
    282         }
    283     }
    284 
    285     private boolean isOpen() {
    286         return in != null;
    287     }
    288 
    289     /**
    290      * Indicates whether this reader is ready to be read without blocking. If
    291      * the result is {@code true}, the next {@code read()} will not block. If
    292      * the result is {@code false} then this reader may or may not block when
    293      * {@code read()} is called. This implementation returns {@code true} if
    294      * there are bytes available in the buffer or the source stream has bytes
    295      * available.
    296      *
    297      * @return {@code true} if the receiver will not block when {@code read()}
    298      *         is called, {@code false} if unknown or blocking will occur.
    299      * @throws IOException
    300      *             if this reader is closed or some other I/O error occurs.
    301      */
    302     @Override
    303     public boolean ready() throws IOException {
    304         synchronized (lock) {
    305             if (in == null) {
    306                 throw new IOException("InputStreamReader is closed");
    307             }
    308             try {
    309                 return bytes.hasRemaining() || in.available() > 0;
    310             } catch (IOException e) {
    311                 return false;
    312             }
    313         }
    314     }
    315 }
    316