1. /*
  2. * @(#)InputStreamReader.java 1.25 00/02/02
  3. *
  4. * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.io;
  11. import sun.io.ByteToCharConverter;
  12. import sun.io.ConversionBufferFullException;
  13. /**
  14. * An InputStreamReader is a bridge from byte streams to character streams: It
  15. * reads bytes and translates them into characters according to a specified <a
  16. * href="../lang/package-summary.html#charenc">character encoding</a>. The
  17. * encoding that it uses may be specified by name, or the platform's default
  18. * encoding may be accepted.
  19. *
  20. * <p> Each invocation of one of an InputStreamReader's read() methods may
  21. * cause one or more bytes to be read from the underlying byte-input stream.
  22. * To enable the efficient conversion of bytes to characters, more bytes may
  23. * be read ahead from the underlying stream than are necessary to satisfy the
  24. * current read operation.
  25. *
  26. * <p> For top efficiency, consider wrapping an InputStreamReader within a
  27. * BufferedReader. For example:
  28. *
  29. * <pre>
  30. * BufferedReader in
  31. * = new BufferedReader(new InputStreamReader(System.in));
  32. * </pre>
  33. *
  34. * @see BufferedReader
  35. * @see InputStream
  36. * @see <a href="../lang/package-summary.html#charenc">Character encodings</a>
  37. *
  38. * @version 1.25, 00/02/02
  39. * @author Mark Reinhold
  40. * @since JDK1.1
  41. */
  42. public class InputStreamReader extends Reader {
  43. private ByteToCharConverter btc;
  44. private InputStream in;
  45. private static final int defaultByteBufferSize = 8192;
  46. private byte bb[]; /* Input buffer */
  47. /**
  48. * Create an InputStreamReader that uses the default character encoding.
  49. *
  50. * @param in An InputStream
  51. */
  52. public InputStreamReader(InputStream in) {
  53. this(in, ByteToCharConverter.getDefault());
  54. }
  55. /**
  56. * Create an InputStreamReader that uses the named character encoding.
  57. *
  58. * @param in An InputStream
  59. * @param enc The name of a supported
  60. * <a href="../lang/package-summary.html#charenc">character
  61. * encoding</a>
  62. *
  63. * @exception UnsupportedEncodingException
  64. * If the named encoding is not supported
  65. */
  66. public InputStreamReader(InputStream in, String enc)
  67. throws UnsupportedEncodingException
  68. {
  69. this(in, ByteToCharConverter.getConverter(enc));
  70. }
  71. /**
  72. * Create an InputStreamReader that uses the specified byte-to-character
  73. * converter. The converter is assumed to have been reset.
  74. *
  75. * @param in An InputStream
  76. * @param btc A ByteToCharConverter
  77. */
  78. private InputStreamReader(InputStream in, ByteToCharConverter btc) {
  79. super(in);
  80. if (in == null)
  81. throw new NullPointerException("input stream is null");
  82. this.in = in;
  83. this.btc = btc;
  84. bb = new byte[defaultByteBufferSize];
  85. }
  86. /**
  87. * Returns the canonical name of the character encoding being used by this
  88. * stream. If this <code>InputStreamReader</code> was created with the
  89. * {@link #InputStreamReader(InputStream, String)} constructor then the
  90. * returned encoding name, being canonical, may differ from the encoding
  91. * name passed to the constructor. May return <code>null</code> if the
  92. * stream has been closed.
  93. *
  94. * @return a String representing the encoding name, or possibly
  95. * <code>null</code> if the stream has been closed
  96. *
  97. * @see <a href="../lang/package-summary.html#charenc">Character
  98. * encodings</a>
  99. */
  100. public String getEncoding() {
  101. synchronized (lock) {
  102. if (btc != null)
  103. return btc.getCharacterEncoding();
  104. else
  105. return null;
  106. }
  107. }
  108. /* Buffer handling */
  109. private int nBytes = 0; /* -1 implies EOF has been reached */
  110. private int nextByte = 0;
  111. private void malfunction() {
  112. throw new InternalError("Converter malfunction (" +
  113. btc.getCharacterEncoding() +
  114. ") -- please submit a bug report via " +
  115. System.getProperty("java.vendor.url.bug"));
  116. }
  117. private int convertInto(char cbuf[], int off, int end) throws IOException {
  118. int nc = 0;
  119. if (nextByte < nBytes) {
  120. try {
  121. nc = btc.convert(bb, nextByte, nBytes,
  122. cbuf, off, end);
  123. nextByte = nBytes;
  124. if (btc.nextByteIndex() != nextByte)
  125. malfunction();
  126. }
  127. catch (ConversionBufferFullException x) {
  128. nextByte = btc.nextByteIndex();
  129. nc = btc.nextCharIndex() - off;
  130. }
  131. }
  132. return nc;
  133. }
  134. private int flushInto(char cbuf[], int off, int end) throws IOException {
  135. int nc = 0;
  136. try {
  137. nc = btc.flush(cbuf, off, end);
  138. }
  139. catch (ConversionBufferFullException x) {
  140. nc = btc.nextCharIndex() - off;
  141. }
  142. return nc;
  143. }
  144. private int fill(char cbuf[], int off, int end) throws IOException {
  145. int nc = 0;
  146. if (nextByte < nBytes)
  147. nc = convertInto(cbuf, off, end);
  148. while (off + nc < end) {
  149. if (nBytes != -1) {
  150. if ((nc > 0) && !inReady())
  151. break; /* Block at most once */
  152. nBytes = in.read(bb);
  153. }
  154. if (nBytes == -1) {
  155. nBytes = 0; /* Allow file to grow */
  156. nc += flushInto(cbuf, off + nc, end);
  157. if (nc == 0)
  158. return -1;
  159. else
  160. break;
  161. }
  162. else {
  163. nextByte = 0;
  164. nc += convertInto(cbuf, off + nc, end);
  165. }
  166. }
  167. return nc;
  168. }
  169. /**
  170. * Tell whether the underlying byte stream is ready to be read. Return
  171. * false for those streams that do not support available(), such as the
  172. * Win32 console stream.
  173. */
  174. private boolean inReady() {
  175. try {
  176. return in.available() > 0;
  177. } catch (IOException x) {
  178. return false;
  179. }
  180. }
  181. /** Check to make sure that the stream has not been closed */
  182. private void ensureOpen() throws IOException {
  183. if (in == null)
  184. throw new IOException("Stream closed");
  185. }
  186. /**
  187. * Read a single character.
  188. *
  189. * @return The character read, or -1 if the end of the stream has been
  190. * reached
  191. *
  192. * @exception IOException If an I/O error occurs
  193. */
  194. public int read() throws IOException {
  195. char cb[] = new char[1];
  196. if (read(cb, 0, 1) == -1)
  197. return -1;
  198. else
  199. return cb[0];
  200. }
  201. /**
  202. * Read characters into a portion of an array.
  203. *
  204. * @param cbuf Destination buffer
  205. * @param off Offset at which to start storing characters
  206. * @param len Maximum number of characters to read
  207. *
  208. * @return The number of characters read, or -1 if the end of the stream
  209. * has been reached
  210. *
  211. * @exception IOException If an I/O error occurs
  212. */
  213. public int read(char cbuf[], int off, int len) throws IOException {
  214. synchronized (lock) {
  215. ensureOpen();
  216. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
  217. ((off + len) > cbuf.length) || ((off + len) < 0)) {
  218. throw new IndexOutOfBoundsException();
  219. } else if (len == 0) {
  220. return 0;
  221. }
  222. return fill(cbuf, off, off + len);
  223. }
  224. }
  225. /**
  226. * Tell whether this stream is ready to be read. An InputStreamReader is
  227. * ready if its input buffer is not empty, or if bytes are available to be
  228. * read from the underlying byte stream.
  229. *
  230. * @exception IOException If an I/O error occurs
  231. */
  232. public boolean ready() throws IOException {
  233. synchronized (lock) {
  234. ensureOpen();
  235. return (nextByte < nBytes) || inReady();
  236. }
  237. }
  238. /**
  239. * Close the stream.
  240. *
  241. * @exception IOException If an I/O error occurs
  242. */
  243. public void close() throws IOException {
  244. synchronized (lock) {
  245. if (in == null)
  246. return;
  247. in.close();
  248. in = null;
  249. bb = null;
  250. btc = null;
  251. }
  252. }
  253. }