Merge from mainline
[official-gcc.git] / libjava / classpath / java / io / OutputStreamWriter.java
blob572683834be1e0703989a56c1f6ce064aec24c80
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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. */
39 package java.io;
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;
51 /**
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.
62 * <p>
63 * Here is a list of standard encoding names that may be available:
64 * <p>
65 * <ul>
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)
77 * <li>More Later
78 * </ul>
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
86 /**
87 * The output stream.
89 private OutputStream out;
91 /**
92 * The charset encoder.
94 private CharsetEncoder encoder;
96 /**
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
118 * not available.
120 public OutputStreamWriter (OutputStream out, String encoding_scheme)
121 throws UnsupportedEncodingException
123 this.out = out;
124 try
126 // Don't use NIO if avoidable
127 if(EncodingHelper.isISOLatin1(encoding_scheme))
129 encodingName = "ISO8859_1";
130 encoder = null;
131 return;
135 * Workraround for encodings with a byte-order-mark.
136 * We only want to write it once per stream.
138 try
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);
161 if(cs == null)
162 throw new UnsupportedEncodingException("Encoding "+encoding_scheme+
163 " unknown");
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.
174 encoder = null;
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)
187 this.out = out;
188 outputBuffer = null;
189 try
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)
198 encoder = null;
199 encodingName = "ISO8859_1";
202 if(encoder != null)
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 * @since 1.5
219 public OutputStreamWriter(OutputStream out, Charset cs)
221 this.out = out;
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
236 * @since 1.5
238 public OutputStreamWriter(OutputStream out, CharsetEncoder enc)
240 this.out = out;
241 encoder = 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
253 if(out == null)
254 return;
255 flush();
256 out.close ();
257 out = null;
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
279 if(out != null){
280 if(outputBuffer != null){
281 char[] buf = new char[outputBuffer.position()];
282 if(buf.length > 0){
283 outputBuffer.flip();
284 outputBuffer.get(buf);
285 writeConvert(buf, 0, buf.length);
286 outputBuffer.clear();
289 out.flush ();
294 * This method writes <code>count</code> characters from the specified
295 * array to the output stream starting at position <code>offset</code>
296 * into the array.
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
306 if(out == null)
307 throw new IOException("Stream is closed.");
308 if(buf == null)
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();
319 offset += r;
320 count -= r;
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);
325 return;
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)
336 throws IOException
338 if(encoder == null)
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]:'?');
343 out.write(b);
344 } else {
345 try {
346 ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count));
347 encoder.reset();
348 if(output.hasArray())
349 out.write(output.array());
350 else
352 byte[] outbytes = new byte[output.remaining()];
353 output.get(outbytes);
354 out.write(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
373 * writing chars from
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
380 if(str == null)
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