1 /* OutputStreamWriter.java -- Writer that converts chars to bytes
2 Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import gnu
.java
.nio
.charset
.EncodingHelper
;
43 import java
.nio
.ByteBuffer
;
44 import java
.nio
.CharBuffer
;
45 import java
.nio
.charset
.CharacterCodingException
;
46 import java
.nio
.charset
.Charset
;
47 import java
.nio
.charset
.CharsetEncoder
;
48 import java
.nio
.charset
.CodingErrorAction
;
49 import java
.nio
.charset
.MalformedInputException
;
52 * This class writes characters to an output stream that is byte oriented
53 * It converts the chars that are written to bytes using an encoding layer,
54 * which is specific to a particular encoding standard. The desired
55 * encoding can either be specified by name, or if no encoding is specified,
56 * the system default encoding will be used. The system default encoding
57 * name is determined from the system property <code>file.encoding</code>.
58 * The only encodings that are guaranteed to be available are "8859_1"
59 * (the Latin-1 character set) and "UTF8". Unfortunately, Java does not
60 * provide a mechanism for listing the encodings that are supported in
61 * a given implementation.
63 * Here is a list of standard encoding names that may be available:
66 * <li>8859_1 (ISO-8859-1/Latin-1)
67 * <li>8859_2 (ISO-8859-2/Latin-2)
68 * <li>8859_3 (ISO-8859-3/Latin-3)
69 * <li>8859_4 (ISO-8859-4/Latin-4)
70 * <li>8859_5 (ISO-8859-5/Latin-5)
71 * <li>8859_6 (ISO-8859-6/Latin-6)
72 * <li>8859_7 (ISO-8859-7/Latin-7)
73 * <li>8859_8 (ISO-8859-8/Latin-8)
74 * <li>8859_9 (ISO-8859-9/Latin-9)
75 * <li>ASCII (7-bit ASCII)
76 * <li>UTF8 (UCS Transformation Format-8)
80 * @author Aaron M. Renn (arenn@urbanophile.com)
81 * @author Per Bothner (bothner@cygnus.com)
82 * @date April 17, 1998.
84 public class OutputStreamWriter
extends Writer
89 private OutputStream out
;
92 * The charset encoder.
94 private CharsetEncoder encoder
;
97 * java.io canonical name of the encoding.
99 private String encodingName
;
102 * Buffer output before character conversion as it has costly overhead.
104 private CharBuffer outputBuffer
;
105 private final static int BUFFER_SIZE
= 1024;
108 * This method initializes a new instance of <code>OutputStreamWriter</code>
109 * to write to the specified stream using a caller supplied character
110 * encoding scheme. Note that due to a deficiency in the Java language
111 * design, there is no way to determine which encodings are supported.
113 * @param out The <code>OutputStream</code> to write to
114 * @param encoding_scheme The name of the encoding scheme to use for
115 * character to byte translation
117 * @exception UnsupportedEncodingException If the named encoding is
120 public OutputStreamWriter (OutputStream out
, String encoding_scheme
)
121 throws UnsupportedEncodingException
126 // Don't use NIO if avoidable
127 if(EncodingHelper
.isISOLatin1(encoding_scheme
))
129 encodingName
= "ISO8859_1";
135 * Workraround for encodings with a byte-order-mark.
136 * We only want to write it once per stream.
140 if(encoding_scheme
.equalsIgnoreCase("UnicodeBig") ||
141 encoding_scheme
.equalsIgnoreCase("UTF-16") ||
142 encoding_scheme
.equalsIgnoreCase("UTF16"))
144 encoding_scheme
= "UTF-16BE";
145 out
.write((byte)0xFE);
146 out
.write((byte)0xFF);
148 else if(encoding_scheme
.equalsIgnoreCase("UnicodeLittle")){
149 encoding_scheme
= "UTF-16LE";
150 out
.write((byte)0xFF);
151 out
.write((byte)0xFE);
154 catch(IOException ioe
)
158 outputBuffer
= CharBuffer
.allocate(BUFFER_SIZE
);
160 Charset cs
= EncodingHelper
.getCharset(encoding_scheme
);
162 throw new UnsupportedEncodingException("Encoding "+encoding_scheme
+
164 encoder
= cs
.newEncoder();
165 encodingName
= EncodingHelper
.getOldCanonical(cs
.name());
167 encoder
.onMalformedInput(CodingErrorAction
.REPLACE
);
168 encoder
.onUnmappableCharacter(CodingErrorAction
.REPLACE
);
170 catch(RuntimeException e
)
172 // Default to ISO Latin-1, will happen if this is called, for instance,
173 // before the NIO provider is loadable.
175 encodingName
= "ISO8859_1";
180 * This method initializes a new instance of <code>OutputStreamWriter</code>
181 * to write to the specified stream using the default encoding.
183 * @param out The <code>OutputStream</code> to write to
185 public OutputStreamWriter (OutputStream out
)
191 String encoding
= System
.getProperty("file.encoding");
192 Charset cs
= Charset
.forName(encoding
);
193 encoder
= cs
.newEncoder();
194 encodingName
= EncodingHelper
.getOldCanonical(cs
.name());
196 catch(RuntimeException e
)
199 encodingName
= "ISO8859_1";
204 encoder
.onMalformedInput(CodingErrorAction
.REPLACE
);
205 encoder
.onUnmappableCharacter(CodingErrorAction
.REPLACE
);
206 outputBuffer
= CharBuffer
.allocate(BUFFER_SIZE
);
211 * This method initializes a new instance of <code>OutputStreamWriter</code>
212 * to write to the specified stream using a given <code>Charset</code>.
214 * @param out The <code>OutputStream</code> to write to
215 * @param cs The <code>Charset</code> of the encoding to use
217 public OutputStreamWriter(OutputStream out
, Charset cs
)
220 encoder
= cs
.newEncoder();
221 encoder
.onMalformedInput(CodingErrorAction
.REPLACE
);
222 encoder
.onUnmappableCharacter(CodingErrorAction
.REPLACE
);
223 outputBuffer
= CharBuffer
.allocate(BUFFER_SIZE
);
227 * This method initializes a new instance of <code>OutputStreamWriter</code>
228 * to write to the specified stream using a given
229 * <code>CharsetEncoder</code>.
231 * @param out The <code>OutputStream</code> to write to
232 * @param enc The <code>CharsetEncoder</code> to encode the output with
234 public OutputStreamWriter(OutputStream out
, CharsetEncoder enc
)
238 outputBuffer
= CharBuffer
.allocate(BUFFER_SIZE
);
242 * This method closes this stream, and the underlying
243 * <code>OutputStream</code>
245 * @exception IOException If an error occurs
247 public void close () throws IOException
257 * This method returns the name of the character encoding scheme currently
258 * in use by this stream. If the stream has been closed, then this method
259 * may return <code>null</code>.
261 * @return The encoding scheme name
263 public String
getEncoding ()
265 return out
!= null ? encodingName
: null;
269 * This method flushes any buffered bytes to the underlying output sink.
271 * @exception IOException If an error occurs
273 public void flush () throws IOException
276 if(outputBuffer
!= null){
277 char[] buf
= new char[outputBuffer
.position()];
280 outputBuffer
.get(buf
);
281 writeConvert(buf
, 0, buf
.length
);
282 outputBuffer
.clear();
290 * This method writes <code>count</code> characters from the specified
291 * array to the output stream starting at position <code>offset</code>
294 * @param buf The array of character to write from
295 * @param offset The offset into the array to start writing chars from
296 * @param count The number of chars to write.
298 * @exception IOException If an error occurs
300 public void write (char[] buf
, int offset
, int count
) throws IOException
303 throw new IOException("Stream is closed.");
305 throw new IOException("Buffer is null.");
307 if(outputBuffer
!= null)
309 if(count
>= outputBuffer
.remaining())
311 int r
= outputBuffer
.remaining();
312 outputBuffer
.put(buf
, offset
, r
);
313 writeConvert(outputBuffer
.array(), 0, BUFFER_SIZE
);
314 outputBuffer
.clear();
317 // if the remaining bytes is larger than the whole buffer,
318 // just don't buffer.
319 if(count
>= outputBuffer
.remaining()){
320 writeConvert(buf
, offset
, count
);
324 outputBuffer
.put(buf
, offset
, count
);
325 } else writeConvert(buf
, offset
, count
);
329 * Converts and writes characters.
331 private void writeConvert (char[] buf
, int offset
, int count
)
336 byte[] b
= new byte[count
];
337 for(int i
=0;i
<count
;i
++)
338 b
[i
] = (byte)((buf
[offset
+i
] <= 0xFF)?buf
[offset
+i
]:'?');
342 ByteBuffer output
= encoder
.encode(CharBuffer
.wrap(buf
,offset
,count
));
344 if(output
.hasArray())
345 out
.write(output
.array());
348 byte[] outbytes
= new byte[output
.remaining()];
349 output
.get(outbytes
);
352 } catch(IllegalStateException e
) {
353 throw new IOException("Internal error.");
354 } catch(MalformedInputException e
) {
355 throw new IOException("Invalid character sequence.");
356 } catch(CharacterCodingException e
) {
357 throw new IOException("Unmappable character.");
363 * This method writes <code>count</code> bytes from the specified
364 * <code>String</code> starting at position <code>offset</code> into the
365 * <code>String</code>.
367 * @param str The <code>String</code> to write chars from
368 * @param offset The position in the <code>String</code> to start
370 * @param count The number of chars to write
372 * @exception IOException If an error occurs
374 public void write (String str
, int offset
, int count
) throws IOException
377 throw new IOException("String is null.");
379 write(str
.toCharArray(), offset
, count
);
383 * This method writes a single character to the output stream.
385 * @param ch The char to write, passed as an int.
387 * @exception IOException If an error occurs
389 public void write (int ch
) throws IOException
391 write(new char[]{ (char)ch
}, 0, 1);
393 } // class OutputStreamWriter