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