GNU Classpath (0.95) | |
Frames | No Frames |
1: /* OutputStreamWriter.java -- Writer that converts chars to bytes 2: Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 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. 10: 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. 15: 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. 20: 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. 25: 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. */ 37: 38: 39: package java.io; 40: 41: import gnu.java.nio.charset.EncodingHelper; 42: 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; 50: 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> 79: * 80: * @author Aaron M. Renn (arenn@urbanophile.com) 81: * @author Per Bothner (bothner@cygnus.com) 82: * @date April 17, 1998. 83: */ 84: public class OutputStreamWriter extends Writer 85: { 86: /** 87: * The output stream. 88: */ 89: private OutputStream out; 90: 91: /** 92: * The charset encoder. 93: */ 94: private CharsetEncoder encoder; 95: 96: /** 97: * java.io canonical name of the encoding. 98: */ 99: private String encodingName; 100: 101: /** 102: * Buffer output before character conversion as it has costly overhead. 103: */ 104: private CharBuffer outputBuffer; 105: private final static int BUFFER_SIZE = 1024; 106: 107: /** 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. 112: * 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 116: * 117: * @exception UnsupportedEncodingException If the named encoding is 118: * not available. 119: */ 120: public OutputStreamWriter (OutputStream out, String encoding_scheme) 121: throws UnsupportedEncodingException 122: { 123: this.out = out; 124: try 125: { 126: // Don't use NIO if avoidable 127: if(EncodingHelper.isISOLatin1(encoding_scheme)) 128: { 129: encodingName = "ISO8859_1"; 130: encoder = null; 131: return; 132: } 133: 134: /* 135: * Workraround for encodings with a byte-order-mark. 136: * We only want to write it once per stream. 137: */ 138: try 139: { 140: if(encoding_scheme.equalsIgnoreCase("UnicodeBig") || 141: encoding_scheme.equalsIgnoreCase("UTF-16") || 142: encoding_scheme.equalsIgnoreCase("UTF16")) 143: { 144: encoding_scheme = "UTF-16BE"; 145: out.write((byte)0xFE); 146: out.write((byte)0xFF); 147: } 148: else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")){ 149: encoding_scheme = "UTF-16LE"; 150: out.write((byte)0xFF); 151: out.write((byte)0xFE); 152: } 153: } 154: catch(IOException ioe) 155: { 156: } 157: 158: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 159: 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()); 166: 167: encoder.onMalformedInput(CodingErrorAction.REPLACE); 168: encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 169: } 170: catch(RuntimeException e) 171: { 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"; 176: } 177: } 178: 179: /** 180: * This method initializes a new instance of <code>OutputStreamWriter</code> 181: * to write to the specified stream using the default encoding. 182: * 183: * @param out The <code>OutputStream</code> to write to 184: */ 185: public OutputStreamWriter (OutputStream out) 186: { 187: this.out = out; 188: outputBuffer = null; 189: try 190: { 191: String encoding = System.getProperty("file.encoding"); 192: Charset cs = Charset.forName(encoding); 193: encoder = cs.newEncoder(); 194: encodingName = EncodingHelper.getOldCanonical(cs.name()); 195: } 196: catch(RuntimeException e) 197: { 198: encoder = null; 199: encodingName = "ISO8859_1"; 200: } 201: 202: if(encoder != null) 203: { 204: encoder.onMalformedInput(CodingErrorAction.REPLACE); 205: encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 206: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 207: } 208: } 209: 210: /** 211: * This method initializes a new instance of <code>OutputStreamWriter</code> 212: * to write to the specified stream using a given <code>Charset</code>. 213: * 214: * @param out The <code>OutputStream</code> to write to 215: * @param cs The <code>Charset</code> of the encoding to use 216: * 217: * @since 1.5 218: */ 219: public OutputStreamWriter(OutputStream out, Charset cs) 220: { 221: this.out = out; 222: encoder = cs.newEncoder(); 223: encoder.onMalformedInput(CodingErrorAction.REPLACE); 224: encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 225: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 226: encodingName = EncodingHelper.getOldCanonical(cs.name()); 227: } 228: 229: /** 230: * This method initializes a new instance of <code>OutputStreamWriter</code> 231: * to write to the specified stream using a given 232: * <code>CharsetEncoder</code>. 233: * 234: * @param out The <code>OutputStream</code> to write to 235: * @param enc The <code>CharsetEncoder</code> to encode the output with 236: * 237: * @since 1.5 238: */ 239: public OutputStreamWriter(OutputStream out, CharsetEncoder enc) 240: { 241: this.out = out; 242: encoder = enc; 243: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 244: Charset cs = enc.charset(); 245: if (cs == null) 246: encodingName = "US-ASCII"; 247: else 248: encodingName = EncodingHelper.getOldCanonical(cs.name()); 249: } 250: 251: /** 252: * This method closes this stream, and the underlying 253: * <code>OutputStream</code> 254: * 255: * @exception IOException If an error occurs 256: */ 257: public void close () throws IOException 258: { 259: if(out == null) 260: return; 261: flush(); 262: out.close (); 263: out = null; 264: } 265: 266: /** 267: * This method returns the name of the character encoding scheme currently 268: * in use by this stream. If the stream has been closed, then this method 269: * may return <code>null</code>. 270: * 271: * @return The encoding scheme name 272: */ 273: public String getEncoding () 274: { 275: return out != null ? encodingName : null; 276: } 277: 278: /** 279: * This method flushes any buffered bytes to the underlying output sink. 280: * 281: * @exception IOException If an error occurs 282: */ 283: public void flush () throws IOException 284: { 285: if(out != null){ 286: if(outputBuffer != null){ 287: char[] buf = new char[outputBuffer.position()]; 288: if(buf.length > 0){ 289: outputBuffer.flip(); 290: outputBuffer.get(buf); 291: writeConvert(buf, 0, buf.length); 292: outputBuffer.clear(); 293: } 294: } 295: out.flush (); 296: } 297: } 298: 299: /** 300: * This method writes <code>count</code> characters from the specified 301: * array to the output stream starting at position <code>offset</code> 302: * into the array. 303: * 304: * @param buf The array of character to write from 305: * @param offset The offset into the array to start writing chars from 306: * @param count The number of chars to write. 307: * 308: * @exception IOException If an error occurs 309: */ 310: public void write (char[] buf, int offset, int count) throws IOException 311: { 312: if(out == null) 313: throw new IOException("Stream is closed."); 314: if(buf == null) 315: throw new IOException("Buffer is null."); 316: 317: if(outputBuffer != null) 318: { 319: if(count >= outputBuffer.remaining()) 320: { 321: int r = outputBuffer.remaining(); 322: outputBuffer.put(buf, offset, r); 323: writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); 324: outputBuffer.clear(); 325: offset += r; 326: count -= r; 327: // if the remaining bytes is larger than the whole buffer, 328: // just don't buffer. 329: if(count >= outputBuffer.remaining()){ 330: writeConvert(buf, offset, count); 331: return; 332: } 333: } 334: outputBuffer.put(buf, offset, count); 335: } else writeConvert(buf, offset, count); 336: } 337: 338: /** 339: * Converts and writes characters. 340: */ 341: private void writeConvert (char[] buf, int offset, int count) 342: throws IOException 343: { 344: if(encoder == null) 345: { 346: byte[] b = new byte[count]; 347: for(int i=0;i<count;i++) 348: b[i] = (byte)((buf[offset+i] <= 0xFF)?buf[offset+i]:'?'); 349: out.write(b); 350: } else { 351: try { 352: ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count)); 353: encoder.reset(); 354: if(output.hasArray()) 355: out.write(output.array()); 356: else 357: { 358: byte[] outbytes = new byte[output.remaining()]; 359: output.get(outbytes); 360: out.write(outbytes); 361: } 362: } catch(IllegalStateException e) { 363: throw new IOException("Internal error."); 364: } catch(MalformedInputException e) { 365: throw new IOException("Invalid character sequence."); 366: } catch(CharacterCodingException e) { 367: throw new IOException("Unmappable character."); 368: } 369: } 370: } 371: 372: /** 373: * This method writes <code>count</code> bytes from the specified 374: * <code>String</code> starting at position <code>offset</code> into the 375: * <code>String</code>. 376: * 377: * @param str The <code>String</code> to write chars from 378: * @param offset The position in the <code>String</code> to start 379: * writing chars from 380: * @param count The number of chars to write 381: * 382: * @exception IOException If an error occurs 383: */ 384: public void write (String str, int offset, int count) throws IOException 385: { 386: if(str == null) 387: throw new IOException("String is null."); 388: 389: write(str.toCharArray(), offset, count); 390: } 391: 392: /** 393: * This method writes a single character to the output stream. 394: * 395: * @param ch The char to write, passed as an int. 396: * 397: * @exception IOException If an error occurs 398: */ 399: public void write (int ch) throws IOException 400: { 401: write(new char[]{ (char)ch }, 0, 1); 402: } 403: } // class OutputStreamWriter
GNU Classpath (0.95) |