1. /*
  2. * @(#)OutputStreamWriter.java 1.23 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.CharToByteConverter;
  9. import sun.io.ConversionBufferFullException;
  10. /**
  11. * Write characters to an output stream, translating characters into bytes
  12. * according to a specified character encoding. Each OutputStreamWriter
  13. * incorporates its own CharToByteConverter, and is thus a bridge from
  14. * character streams to byte streams.
  15. *
  16. * <p> The encoding used by an OutputStreamWriter may be specified by name, by
  17. * providing a CharToByteConverter, or by accepting the default encoding, which
  18. * is defined by the system property <tt>file.encoding</tt>.
  19. *
  20. * <p> Each invocation of a write() method causes the encoding converter to be
  21. * invoked on the given character(s). The resulting bytes are accumulated in a
  22. * buffer before being written to the underlying output stream. The size of
  23. * this buffer may be specified, but by default it is large enough for most
  24. * purposes. Note that the characters passed to the write() methods are not
  25. * buffered. For top efficiency, consider wrapping an OutputStreamWriter
  26. * within a BufferedWriter so as to avoid frequent converter invocations. For
  27. * example,
  28. *
  29. * <pre>
  30. * Writer out
  31. * = new BufferedWriter(new OutputStreamWriter(System.out));
  32. * </pre>
  33. *
  34. * @see BufferedWriter
  35. * @see OutputStream
  36. *
  37. * @version 1.10, 97/01/27
  38. * @author Mark Reinhold
  39. * @since JDK1.1
  40. */
  41. public class OutputStreamWriter extends Writer {
  42. private CharToByteConverter ctb;
  43. private OutputStream out;
  44. private static final int defaultByteBufferSize = 8192;
  45. /* bb is a temporary output buffer into which bytes are written. */
  46. private byte bb[];
  47. /* nextByte is where the next byte will be written into bb */
  48. private int nextByte = 0;
  49. /* nBytes is the buffer size = defaultByteBufferSize in this class */
  50. private int nBytes = 0;
  51. /**
  52. * Create an OutputStreamWriter that uses the named character encoding.
  53. *
  54. * @param out An OutputStream
  55. * @param enc Name of the encoding to be used
  56. *
  57. * @exception UnsupportedEncodingException
  58. * If the named encoding is not supported
  59. */
  60. public OutputStreamWriter(OutputStream out, String enc)
  61. throws UnsupportedEncodingException
  62. {
  63. this(out, CharToByteConverter.getConverter(enc));
  64. }
  65. /**
  66. * Create an OutputStreamWriter that uses the default character encoding.
  67. *
  68. * @param out An OutputStream
  69. */
  70. public OutputStreamWriter(OutputStream out) {
  71. this(out, CharToByteConverter.getDefault());
  72. }
  73. /**
  74. * Create an OutputStreamWriter that uses the specified character-to-byte
  75. * converter. The converter is assumed to have been reset.
  76. *
  77. * @param out An OutputStream
  78. * @param ctb A CharToByteConverter
  79. */
  80. private OutputStreamWriter(OutputStream out, CharToByteConverter ctb) {
  81. super(out);
  82. if (out == null)
  83. throw new NullPointerException("out is null");
  84. this.out = out;
  85. this.ctb = ctb;
  86. bb = new byte[defaultByteBufferSize];
  87. nBytes = defaultByteBufferSize;
  88. }
  89. /**
  90. * Returns the canonical name of the character encoding being used by
  91. * this stream. If this <code>OutputStreamWriter</code> was created
  92. * with the {@link #OutputStreamWriter(OutputStream, String)} constructor,
  93. * the returned encoding name, being canonical, may differ from the
  94. * encoding name passed to the constructor. May return <code>null</code>
  95. * if the stream has been closed.
  96. */
  97. public String getEncoding() {
  98. synchronized (lock) {
  99. if (ctb != null)
  100. return ctb.getCharacterEncoding();
  101. else
  102. return null;
  103. }
  104. }
  105. /** Check to make sure that the stream has not been closed */
  106. private void ensureOpen() throws IOException {
  107. if (out == null)
  108. throw new IOException("Stream closed");
  109. }
  110. /**
  111. * Write a single character.
  112. *
  113. * @exception IOException If an I/O error occurs
  114. */
  115. public void write(int c) throws IOException {
  116. char cbuf[] = new char[1];
  117. cbuf[0] = (char) c;
  118. write(cbuf, 0, 1);
  119. }
  120. /**
  121. * Write a portion of an array of characters.
  122. *
  123. * @param cbuf Buffer of characters
  124. * @param off Offset from which to start writing characters
  125. * @param len Number of characters to write
  126. *
  127. * @exception IOException If an I/O error occurs
  128. */
  129. public void write(char cbuf[], int off, int len) throws IOException {
  130. synchronized (lock) {
  131. ensureOpen();
  132. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
  133. ((off + len) > cbuf.length) || ((off + len) < 0)) {
  134. throw new IndexOutOfBoundsException();
  135. } else if (len == 0) {
  136. return;
  137. }
  138. int ci = off, end = off + len;
  139. boolean bufferFlushed = false;
  140. while (ci < end) {
  141. boolean bufferFull = false;
  142. try {
  143. nextByte += ctb.convertAny(cbuf, ci, end,
  144. bb, nextByte, nBytes);
  145. ci = end;
  146. }
  147. catch (ConversionBufferFullException x) {
  148. int nci = ctb.nextCharIndex();
  149. if ((nci == ci) && bufferFlushed) {
  150. /* If the buffer has been flushed and it
  151. still does not hold even one character */
  152. throw new
  153. CharConversionException("Output buffer too small");
  154. }
  155. ci = nci;
  156. bufferFull = true;
  157. nextByte = ctb.nextByteIndex();
  158. }
  159. if ((nextByte >= nBytes) || bufferFull) {
  160. out.write(bb, 0, nextByte);
  161. nextByte = 0;
  162. bufferFlushed = true;
  163. }
  164. }
  165. }
  166. }
  167. /**
  168. * Write a portion of a string.
  169. *
  170. * @param str A String
  171. * @param off Offset from which to start writing characters
  172. * @param len Number of characters to write
  173. *
  174. * @exception IOException If an I/O error occurs
  175. */
  176. public void write(String str, int off, int len) throws IOException {
  177. char cbuf[] = new char[len];
  178. str.getChars(off, off + len, cbuf, 0);
  179. write(cbuf, 0, len);
  180. }
  181. /**
  182. * Flush the output buffer to the underlying byte stream, without flushing
  183. * the byte stream itself. This method is non-private only so that it may
  184. * be invoked by PrintStream.
  185. */
  186. void flushBuffer() throws IOException {
  187. synchronized (lock) {
  188. ensureOpen();
  189. for (;;) {
  190. try {
  191. nextByte += ctb.flushAny(bb, nextByte, nBytes);
  192. }
  193. catch (ConversionBufferFullException x) {
  194. nextByte = ctb.nextByteIndex();
  195. }
  196. if (nextByte == 0)
  197. break;
  198. if (nextByte > 0) {
  199. out.write(bb, 0, nextByte);
  200. nextByte = 0;
  201. }
  202. }
  203. }
  204. }
  205. /**
  206. * Flush the stream.
  207. *
  208. * @exception IOException If an I/O error occurs
  209. */
  210. public void flush() throws IOException {
  211. synchronized (lock) {
  212. flushBuffer();
  213. out.flush();
  214. }
  215. }
  216. /**
  217. * Close the stream.
  218. *
  219. * @exception IOException If an I/O error occurs
  220. */
  221. public void close() throws IOException {
  222. synchronized (lock) {
  223. if (out == null)
  224. return;
  225. flush();
  226. out.close();
  227. out = null;
  228. bb = null;
  229. ctb = null;
  230. }
  231. }
  232. }