GNU Classpath (0.17) | ||
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: import java.nio.ByteBuffer; 43: import java.nio.CharBuffer; 44: import java.nio.charset.MalformedInputException; 45: import java.nio.charset.UnsupportedCharsetException; 46: import java.nio.charset.CharacterCodingException; 47: import java.nio.charset.IllegalCharsetNameException; 48: import java.nio.charset.CoderResult; 49: import java.nio.charset.CodingErrorAction; 50: import java.nio.charset.Charset; 51: import java.nio.charset.CharsetEncoder; 52: 53: /** 54: * This class writes characters to an output stream that is byte oriented 55: * It converts the chars that are written to bytes using an encoding layer, 56: * which is specific to a particular encoding standard. The desired 57: * encoding can either be specified by name, or if no encoding is specified, 58: * the system default encoding will be used. The system default encoding 59: * name is determined from the system property <code>file.encoding</code>. 60: * The only encodings that are guaranteed to be available are "8859_1" 61: * (the Latin-1 character set) and "UTF8". Unfortunately, Java does not 62: * provide a mechanism for listing the encodings that are supported in 63: * a given implementation. 64: * <p> 65: * Here is a list of standard encoding names that may be available: 66: * <p> 67: * <ul> 68: * <li>8859_1 (ISO-8859-1/Latin-1) 69: * <li>8859_2 (ISO-8859-2/Latin-2) 70: * <li>8859_3 (ISO-8859-3/Latin-3) 71: * <li>8859_4 (ISO-8859-4/Latin-4) 72: * <li>8859_5 (ISO-8859-5/Latin-5) 73: * <li>8859_6 (ISO-8859-6/Latin-6) 74: * <li>8859_7 (ISO-8859-7/Latin-7) 75: * <li>8859_8 (ISO-8859-8/Latin-8) 76: * <li>8859_9 (ISO-8859-9/Latin-9) 77: * <li>ASCII (7-bit ASCII) 78: * <li>UTF8 (UCS Transformation Format-8) 79: * <li>More Later 80: * </ul> 81: * 82: * @author Aaron M. Renn (arenn@urbanophile.com) 83: * @author Per Bothner (bothner@cygnus.com) 84: * @date April 17, 1998. 85: */ 86: public class OutputStreamWriter extends Writer 87: { 88: /** 89: * The output stream. 90: */ 91: private OutputStream out; 92: 93: /** 94: * The charset encoder. 95: */ 96: private CharsetEncoder encoder; 97: 98: /** 99: * java.io canonical name of the encoding. 100: */ 101: private String encodingName; 102: 103: /** 104: * Buffer output before character conversion as it has costly overhead. 105: */ 106: private CharBuffer outputBuffer; 107: private final static int BUFFER_SIZE = 1024; 108: 109: /** 110: * This method initializes a new instance of <code>OutputStreamWriter</code> 111: * to write to the specified stream using a caller supplied character 112: * encoding scheme. Note that due to a deficiency in the Java language 113: * design, there is no way to determine which encodings are supported. 114: * 115: * @param out The <code>OutputStream</code> to write to 116: * @param encoding_scheme The name of the encoding scheme to use for 117: * character to byte translation 118: * 119: * @exception UnsupportedEncodingException If the named encoding is 120: * not available. 121: */ 122: public OutputStreamWriter (OutputStream out, String encoding_scheme) 123: throws UnsupportedEncodingException 124: { 125: this.out = out; 126: try 127: { 128: // Don't use NIO if avoidable 129: if(EncodingHelper.isISOLatin1(encoding_scheme)) 130: { 131: encodingName = "ISO8859_1"; 132: encoder = null; 133: return; 134: } 135: 136: /* 137: * Workraround for encodings with a byte-order-mark. 138: * We only want to write it once per stream. 139: */ 140: try { 141: if(encoding_scheme.equalsIgnoreCase("UnicodeBig") || 142: encoding_scheme.equalsIgnoreCase("UTF-16") || 143: encoding_scheme.equalsIgnoreCase("UTF16")) 144: { 145: encoding_scheme = "UTF-16BE"; 146: out.write((byte)0xFE); 147: 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); 152: } 153: } catch(IOException ioe){ 154: } 155: 156: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 157: 158: Charset cs = EncodingHelper.getCharset(encoding_scheme); 159: if(cs == null) 160: throw new UnsupportedEncodingException("Encoding "+encoding_scheme+ 161: " unknown"); 162: encoder = cs.newEncoder(); 163: encodingName = EncodingHelper.getOldCanonical(cs.name()); 164: 165: encoder.onMalformedInput(CodingErrorAction.REPLACE); 166: encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 167: } catch(RuntimeException e) { 168: // Default to ISO Latin-1, will happen if this is called, for instance, 169: // before the NIO provider is loadable. 170: encoder = null; 171: encodingName = "ISO8859_1"; 172: } 173: } 174: 175: /** 176: * This method initializes a new instance of <code>OutputStreamWriter</code> 177: * to write to the specified stream using the default encoding. 178: * 179: * @param out The <code>OutputStream</code> to write to 180: */ 181: public OutputStreamWriter (OutputStream out) 182: { 183: this.out = out; 184: outputBuffer = null; 185: try 186: { 187: String encoding = System.getProperty("file.encoding"); 188: Charset cs = Charset.forName(encoding); 189: encoder = cs.newEncoder(); 190: encodingName = EncodingHelper.getOldCanonical(cs.name()); 191: } catch(RuntimeException e) { 192: encoder = null; 193: encodingName = "ISO8859_1"; 194: } 195: if(encoder != null) 196: { 197: encoder.onMalformedInput(CodingErrorAction.REPLACE); 198: encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 199: outputBuffer = CharBuffer.allocate(BUFFER_SIZE); 200: } 201: } 202: 203: /** 204: * This method closes this stream, and the underlying 205: * <code>OutputStream</code> 206: * 207: * @exception IOException If an error occurs 208: */ 209: public void close () throws IOException 210: { 211: if(out == null) 212: return; 213: flush(); 214: out.close (); 215: out = null; 216: } 217: 218: /** 219: * This method returns the name of the character encoding scheme currently 220: * in use by this stream. If the stream has been closed, then this method 221: * may return <code>null</code>. 222: * 223: * @return The encoding scheme name 224: */ 225: public String getEncoding () 226: { 227: return out != null ? encodingName : null; 228: } 229: 230: /** 231: * This method flushes any buffered bytes to the underlying output sink. 232: * 233: * @exception IOException If an error occurs 234: */ 235: public void flush () throws IOException 236: { 237: if(out != null){ 238: if(outputBuffer != null){ 239: char[] buf = new char[outputBuffer.position()]; 240: if(buf.length > 0){ 241: outputBuffer.flip(); 242: outputBuffer.get(buf); 243: writeConvert(buf, 0, buf.length); 244: outputBuffer.clear(); 245: } 246: } 247: out.flush (); 248: } 249: } 250: 251: /** 252: * This method writes <code>count</code> characters from the specified 253: * array to the output stream starting at position <code>offset</code> 254: * into the array. 255: * 256: * @param buf The array of character to write from 257: * @param offset The offset into the array to start writing chars from 258: * @param count The number of chars to write. 259: * 260: * @exception IOException If an error occurs 261: */ 262: public void write (char[] buf, int offset, int count) throws IOException 263: { 264: if(out == null) 265: throw new IOException("Stream is closed."); 266: if(buf == null) 267: throw new IOException("Buffer is null."); 268: 269: if(outputBuffer != null) 270: { 271: if(count >= outputBuffer.remaining()) 272: { 273: int r = outputBuffer.remaining(); 274: outputBuffer.put(buf, offset, r); 275: writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); 276: outputBuffer.clear(); 277: offset += r; 278: count -= r; 279: // if the remaining bytes is larger than the whole buffer, 280: // just don't buffer. 281: if(count >= outputBuffer.remaining()){ 282: writeConvert(buf, offset, count); 283: return; 284: } 285: } 286: outputBuffer.put(buf, offset, count); 287: } else writeConvert(buf, offset, count); 288: } 289: 290: /** 291: * Converts and writes characters. 292: */ 293: private void writeConvert (char[] buf, int offset, int count) 294: throws IOException 295: { 296: if(encoder == null) 297: { 298: byte[] b = new byte[count]; 299: for(int i=0;i<count;i++) 300: b[i] = (byte)((buf[offset+i] <= 0xFF)?buf[offset+i]:'?'); 301: out.write(b); 302: } else { 303: try { 304: ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count)); 305: encoder.reset(); 306: if(output.hasArray()) 307: out.write(output.array()); 308: else 309: { 310: byte[] outbytes = new byte[output.remaining()]; 311: output.get(outbytes); 312: out.write(outbytes); 313: } 314: } catch(IllegalStateException e) { 315: throw new IOException("Internal error."); 316: } catch(MalformedInputException e) { 317: throw new IOException("Invalid character sequence."); 318: } catch(CharacterCodingException e) { 319: throw new IOException("Unmappable character."); 320: } 321: } 322: } 323: 324: /** 325: * This method writes <code>count</code> bytes from the specified 326: * <code>String</code> starting at position <code>offset</code> into the 327: * <code>String</code>. 328: * 329: * @param str The <code>String</code> to write chars from 330: * @param offset The position in the <code>String</code> to start 331: * writing chars from 332: * @param count The number of chars to write 333: * 334: * @exception IOException If an error occurs 335: */ 336: public void write (String str, int offset, int count) throws IOException 337: { 338: if(str == null) 339: throw new IOException("String is null."); 340: 341: write(str.toCharArray(), offset, count); 342: } 343: 344: /** 345: * This method writes a single character to the output stream. 346: * 347: * @param ch The char to write, passed as an int. 348: * 349: * @exception IOException If an error occurs 350: */ 351: public void write (int ch) throws IOException 352: { 353: write(new char[]{ (char)ch }, 0, 1); 354: } 355: } // class OutputStreamWriter
GNU Classpath (0.17) |