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
219 public OutputStreamWriter(OutputStream out
, Charset cs
)
222 encoder
= cs
.newEncoder();
223 encoder
.onMalformedInput(CodingErrorAction
.REPLACE
);
224 encoder
.onUnmappableCharacter(CodingErrorAction
.REPLACE
);
225 outputBuffer
= CharBuffer
.allocate(BUFFER_SIZE
);
229 * This method initializes a new instance of <code>OutputStreamWriter</code>
230 * to write to the specified stream using a given
231 * <code>CharsetEncoder</code>.
233 * @param out The <code>OutputStream</code> to write to
234 * @param enc The <code>CharsetEncoder</code> to encode the output with
238 public OutputStreamWriter(OutputStream out
, CharsetEncoder enc
)
242 outputBuffer
= CharBuffer
.allocate(BUFFER_SIZE
);
246 * This method closes this stream, and the underlying
247 * <code>OutputStream</code>
249 * @exception IOException If an error occurs
251 public void close () throws IOException
261 * This method returns the name of the character encoding scheme currently
262 * in use by this stream. If the stream has been closed, then this method
263 * may return <code>null</code>.
265 * @return The encoding scheme name
267 public String
getEncoding ()
269 return out
!= null ? encodingName
: null;
273 * This method flushes any buffered bytes to the underlying output sink.
275 * @exception IOException If an error occurs
277 public void flush () throws IOException
280 if(outputBuffer
!= null){
281 char[] buf
= new char[outputBuffer
.position()];
284 outputBuffer
.get(buf
);
285 writeConvert(buf
, 0, buf
.length
);
286 outputBuffer
.clear();
294 * This method writes <code>count</code> characters from the specified
295 * array to the output stream starting at position <code>offset</code>
298 * @param buf The array of character to write from
299 * @param offset The offset into the array to start writing chars from
300 * @param count The number of chars to write.
302 * @exception IOException If an error occurs
304 public void write (char[] buf
, int offset
, int count
) throws IOException
307 throw new IOException("Stream is closed.");
309 throw new IOException("Buffer is null.");
311 if(outputBuffer
!= null)
313 if(count
>= outputBuffer
.remaining())
315 int r
= outputBuffer
.remaining();
316 outputBuffer
.put(buf
, offset
, r
);
317 writeConvert(outputBuffer
.array(), 0, BUFFER_SIZE
);
318 outputBuffer
.clear();
321 // if the remaining bytes is larger than the whole buffer,
322 // just don't buffer.
323 if(count
>= outputBuffer
.remaining()){
324 writeConvert(buf
, offset
, count
);
328 outputBuffer
.put(buf
, offset
, count
);
329 } else writeConvert(buf
, offset
, count
);
333 * Converts and writes characters.
335 private void writeConvert (char[] buf
, int offset
, int count
)
340 byte[] b
= new byte[count
];
341 for(int i
=0;i
<count
;i
++)
342 b
[i
] = (byte)((buf
[offset
+i
] <= 0xFF)?buf
[offset
+i
]:'?');
346 ByteBuffer output
= encoder
.encode(CharBuffer
.wrap(buf
,offset
,count
));
348 if(output
.hasArray())
349 out
.write(output
.array());
352 byte[] outbytes
= new byte[output
.remaining()];
353 output
.get(outbytes
);
356 } catch(IllegalStateException e
) {
357 throw new IOException("Internal error.");
358 } catch(MalformedInputException e
) {
359 throw new IOException("Invalid character sequence.");
360 } catch(CharacterCodingException e
) {
361 throw new IOException("Unmappable character.");
367 * This method writes <code>count</code> bytes from the specified
368 * <code>String</code> starting at position <code>offset</code> into the
369 * <code>String</code>.
371 * @param str The <code>String</code> to write chars from
372 * @param offset The position in the <code>String</code> to start
374 * @param count The number of chars to write
376 * @exception IOException If an error occurs
378 public void write (String str
, int offset
, int count
) throws IOException
381 throw new IOException("String is null.");
383 write(str
.toCharArray(), offset
, count
);
387 * This method writes a single character to the output stream.
389 * @param ch The char to write, passed as an int.
391 * @exception IOException If an error occurs
393 public void write (int ch
) throws IOException
395 write(new char[]{ (char)ch
}, 0, 1);
397 } // class OutputStreamWriter