/* * @(#)InputStreamReader.java 1.35 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.io; import sun.io.ByteToCharConverter; import sun.io.ConversionBufferFullException; /** * An InputStreamReader is a bridge from byte streams to character streams: It * reads bytes and translates them into characters according to a specified <a * href="../lang/package-summary.html#charenc">character encoding</a>. The * encoding that it uses may be specified by name, or the platform's default * encoding may be accepted. * * <p> Each invocation of one of an InputStreamReader's read() methods may * cause one or more bytes to be read from the underlying byte-input stream. * To enable the efficient conversion of bytes to characters, more bytes may * be read ahead from the underlying stream than are necessary to satisfy the * current read operation. * * <p> For top efficiency, consider wrapping an InputStreamReader within a * BufferedReader. For example: * * <pre> * BufferedReader in * = new BufferedReader(new InputStreamReader(System.in)); * </pre> * * @see BufferedReader * @see InputStream * @see <a href="../lang/package-summary.html#charenc">Character encodings</a> * * @version 1.25, 00/02/02 * @author Mark Reinhold * @since JDK1.1 */ public class InputStreamReader extends Reader { private ByteToCharConverter btc; private InputStream in; private static final int defaultByteBufferSize = 8192; private byte bb[]; /* Input buffer */ /** * Create an InputStreamReader that uses the default character encoding. * * @param in An InputStream */ public InputStreamReader(InputStream in) { this(in, ByteToCharConverter.getDefault()); } /** * Create an InputStreamReader that uses the named character encoding. * * @param in An InputStream * @param enc The name of a supported * <a href="../lang/package-summary.html#charenc">character * encoding</a> * * @exception UnsupportedEncodingException * If the named encoding is not supported */ public InputStreamReader(InputStream in, String enc) throws UnsupportedEncodingException { this(in, ByteToCharConverter.getConverter(enc)); } /** * Create an InputStreamReader that uses the specified byte-to-character * converter. The converter is assumed to have been reset. * * @param in An InputStream * @param btc A ByteToCharConverter */ private InputStreamReader(InputStream in, ByteToCharConverter btc) { super(in); if (in == null) throw new NullPointerException("input stream is null"); this.in = in; this.btc = btc; bb = new byte[defaultByteBufferSize]; } /** * Returns the canonical name of the character encoding being used by this * stream. * * <p> If this instance was created with the {@link * #InputStreamReader(InputStream, String)} constructor then the returned * encoding name, being canonical, may differ from the encoding name passed * to the constructor. This method may return <code>null</code> if the stream * has been closed. </p> * * <p> NOTE : In J2ME CDC, there is no concept of historical name, so only * canonical name of character encoding is returned. * * @return a String representing the encoding name, or possibly * <code>null</code> if the stream has been closed * * @see <a href="../lang/package-summary.html#charenc">Character * encodings</a> */ public String getEncoding() { synchronized (lock) { if (btc != null) return btc.getCharacterEncoding(); else return null; } } /* Buffer handling */ private int nBytes = 0; /* -1 implies EOF has been reached */ private int nextByte = 0; private void malfunction() { throw new InternalError("Converter malfunction (" + btc.getCharacterEncoding() + ") -- please submit a bug report via " + System.getProperty("java.vendor.url.bug")); } private int convertInto(char cbuf[], int off, int end) throws IOException { int nc = 0; if (nextByte < nBytes) { try { nc = btc.convert(bb, nextByte, nBytes, cbuf, off, end); nextByte = nBytes; if (btc.nextByteIndex() != nextByte) malfunction(); } catch (ConversionBufferFullException x) { nextByte = btc.nextByteIndex(); nc = btc.nextCharIndex() - off; } } return nc; } private int flushInto(char cbuf[], int off, int end) throws IOException { int nc = 0; try { nc = btc.flush(cbuf, off, end); } catch (ConversionBufferFullException x) { nc = btc.nextCharIndex() - off; } return nc; } private int fill(char cbuf[], int off, int end) throws IOException { int nc = 0; if (nextByte < nBytes) nc = convertInto(cbuf, off, end); while (off + nc < end) { if (nBytes != -1) { if ((nc > 0) && !inReady()) break; /* Block at most once */ nBytes = in.read(bb, 0, Math.min(end - off, bb.length)); } if (nBytes == -1) { nBytes = 0; /* Allow file to grow */ nc += flushInto(cbuf, off + nc, end); if (nc == 0) return -1; else break; } else { nextByte = 0; nc += convertInto(cbuf, off + nc, end); } } return nc; } /** * Tell whether the underlying byte stream is ready to be read. Return * false for those streams that do not support available(), such as the * Win32 console stream. */ private boolean inReady() { try { return in.available() > 0; } catch (IOException x) { return false; } } /** Check to make sure that the stream has not been closed */ private void ensureOpen() throws IOException { if (in == null) throw new IOException("Stream closed"); } /** * Read a single character. * * @return The character read, or -1 if the end of the stream has been * reached * * @exception IOException If an I/O error occurs */ public int read() throws IOException { char cb[] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else return cb[0]; } /** * Read characters into a portion of an array. * * @param cbuf Destination buffer * @param off Offset at which to start storing characters * @param len Maximum number of characters to read * * @return The number of characters read, or -1 if the end of the stream * has been reached * * @exception IOException If an I/O error occurs */ public int read(char cbuf[], int off, int len) throws IOException { synchronized (lock) { ensureOpen(); if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } return fill(cbuf, off, off + len); } } /** * Tell whether this stream is ready to be read. An InputStreamReader is * ready if its input buffer is not empty, or if bytes are available to be * read from the underlying byte stream. * * @exception IOException If an I/O error occurs */ public boolean ready() throws IOException { synchronized (lock) { ensureOpen(); return (nextByte < nBytes) || inReady(); } } /** * Close the stream. * * @exception IOException If an I/O error occurs */ public void close() throws IOException { synchronized (lock) { if (in == null) return; in.close(); in = null; bb = null; btc = null; } } }