GNU Classpath (0.20) | |
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: } 227: 228: /** 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>. 232: * 233: * @param out The <code>OutputStream</code> to write to 234: * @param enc The <code>CharsetEncoder</code> to encode the output with 235: * 236: * @since 1.5 237: */ 238: public OutputStreamWriter(OutputStream out, CharsetEncoder enc) 239: { 240: this.out = out; 241: encoder = enc; 242: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 243: } 244: 245: /** 246: * This method closes this stream, and the underlying 247: * <code>OutputStream</code> 248: * 249: * @exception IOException If an error occurs 250: */ 251: public void close () throws IOException 252: { 253: if(out == null) 254: return; 255: flush(); 256: out.close (); 257: out = null; 258: } 259: 260: /** 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>. 264: * 265: * @return The encoding scheme name 266: */ 267: public String getEncoding () 268: { 269: return out != null ? encodingName : null; 270: } 271: 272: /** 273: * This method flushes any buffered bytes to the underlying output sink. 274: * 275: * @exception IOException If an error occurs 276: */ 277: public void flush () throws IOException 278: { 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(); 287: } 288: } 289: out.flush (); 290: } 291: } 292: 293: /** 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. 297: * 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. 301: * 302: * @exception IOException If an error occurs 303: */ 304: public void write (char[] buf, int offset, int count) throws IOException 305: { 306: if(out == null) 307: throw new IOException("Stream is closed."); 308: if(buf == null) 309: throw new IOException("Buffer is null."); 310: 311: if(outputBuffer != null) 312: { 313: if(count >= outputBuffer.remaining()) 314: { 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; 326: } 327: } 328: outputBuffer.put(buf, offset, count); 329: } else writeConvert(buf, offset, count); 330: } 331: 332: /** 333: * Converts and writes characters. 334: */ 335: private void writeConvert (char[] buf, int offset, int count) 336: throws IOException 337: { 338: if(encoder == null) 339: { 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 351: { 352: byte[] outbytes = new byte[output.remaining()]; 353: output.get(outbytes); 354: out.write(outbytes); 355: } 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."); 362: } 363: } 364: } 365: 366: /** 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>. 370: * 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 375: * 376: * @exception IOException If an error occurs 377: */ 378: public void write (String str, int offset, int count) throws IOException 379: { 380: if(str == null) 381: throw new IOException("String is null."); 382: 383: write(str.toCharArray(), offset, count); 384: } 385: 386: /** 387: * This method writes a single character to the output stream. 388: * 389: * @param ch The char to write, passed as an int. 390: * 391: * @exception IOException If an error occurs 392: */ 393: public void write (int ch) throws IOException 394: { 395: write(new char[]{ (char)ch }, 0, 1); 396: } 397: } // class OutputStreamWriter
GNU Classpath (0.20) |