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