GNU Classpath (0.19) | ||
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: public OutputStreamWriter(OutputStream out, Charset cs) 218: { 219: this.out = out; 220: encoder = cs.newEncoder(); 221: encoder.onMalformedInput(CodingErrorAction.REPLACE); 222: encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 223: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 224: } 225: 226: /** 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>. 230: * 231: * @param out The <code>OutputStream</code> to write to 232: * @param enc The <code>CharsetEncoder</code> to encode the output with 233: */ 234: public OutputStreamWriter(OutputStream out, CharsetEncoder enc) 235: { 236: this.out = out; 237: encoder = enc; 238: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 239: } 240: 241: /** 242: * This method closes this stream, and the underlying 243: * <code>OutputStream</code> 244: * 245: * @exception IOException If an error occurs 246: */ 247: public void close () throws IOException 248: { 249: if(out == null) 250: return; 251: flush(); 252: out.close (); 253: out = null; 254: } 255: 256: /** 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>. 260: * 261: * @return The encoding scheme name 262: */ 263: public String getEncoding () 264: { 265: return out != null ? encodingName : null; 266: } 267: 268: /** 269: * This method flushes any buffered bytes to the underlying output sink. 270: * 271: * @exception IOException If an error occurs 272: */ 273: public void flush () throws IOException 274: { 275: if(out != null){ 276: if(outputBuffer != null){ 277: char[] buf = new char[outputBuffer.position()]; 278: if(buf.length > 0){ 279: outputBuffer.flip(); 280: outputBuffer.get(buf); 281: writeConvert(buf, 0, buf.length); 282: outputBuffer.clear(); 283: } 284: } 285: out.flush (); 286: } 287: } 288: 289: /** 290: * This method writes <code>count</code> characters from the specified 291: * array to the output stream starting at position <code>offset</code> 292: * into the array. 293: * 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. 297: * 298: * @exception IOException If an error occurs 299: */ 300: public void write (char[] buf, int offset, int count) throws IOException 301: { 302: if(out == null) 303: throw new IOException("Stream is closed."); 304: if(buf == null) 305: throw new IOException("Buffer is null."); 306: 307: if(outputBuffer != null) 308: { 309: if(count >= outputBuffer.remaining()) 310: { 311: int r = outputBuffer.remaining(); 312: outputBuffer.put(buf, offset, r); 313: writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); 314: outputBuffer.clear(); 315: offset += r; 316: count -= r; 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); 321: return; 322: } 323: } 324: outputBuffer.put(buf, offset, count); 325: } else writeConvert(buf, offset, count); 326: } 327: 328: /** 329: * Converts and writes characters. 330: */ 331: private void writeConvert (char[] buf, int offset, int count) 332: throws IOException 333: { 334: if(encoder == null) 335: { 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]:'?'); 339: out.write(b); 340: } else { 341: try { 342: ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count)); 343: encoder.reset(); 344: if(output.hasArray()) 345: out.write(output.array()); 346: else 347: { 348: byte[] outbytes = new byte[output.remaining()]; 349: output.get(outbytes); 350: out.write(outbytes); 351: } 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."); 358: } 359: } 360: } 361: 362: /** 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>. 366: * 367: * @param str The <code>String</code> to write chars from 368: * @param offset The position in the <code>String</code> to start 369: * writing chars from 370: * @param count The number of chars to write 371: * 372: * @exception IOException If an error occurs 373: */ 374: public void write (String str, int offset, int count) throws IOException 375: { 376: if(str == null) 377: throw new IOException("String is null."); 378: 379: write(str.toCharArray(), offset, count); 380: } 381: 382: /** 383: * This method writes a single character to the output stream. 384: * 385: * @param ch The char to write, passed as an int. 386: * 387: * @exception IOException If an error occurs 388: */ 389: public void write (int ch) throws IOException 390: { 391: write(new char[]{ (char)ch }, 0, 1); 392: } 393: } // class OutputStreamWriter
GNU Classpath (0.19) |