Frames | No Frames |
1: /* OutputStreamWriter.java -- Writer that converts chars to bytes 2: Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006 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.gcj.convert.UnicodeToBytes; 42: import gnu.gcj.convert.CharsetToBytesAdaptor; 43: import java.nio.charset.Charset; 44: import java.nio.charset.CharsetEncoder; 45: 46: /** 47: * This class writes characters to an output stream that is byte oriented 48: * It converts the chars that are written to bytes using an encoding layer, 49: * which is specific to a particular encoding standard. The desired 50: * encoding can either be specified by name, or if no encoding is specified, 51: * the system default encoding will be used. The system default encoding 52: * name is determined from the system property <code>file.encoding</code>. 53: * The only encodings that are guaranteed to be available are "8859_1" 54: * (the Latin-1 character set) and "UTF8". Unfortunately, Java does not 55: * provide a mechanism for listing the encodings that are supported in 56: * a given implementation. 57: * <p> 58: * Here is a list of standard encoding names that may be available: 59: * <p> 60: * <ul> 61: * <li>8859_1 (ISO-8859-1/Latin-1) 62: * <li>8859_2 (ISO-8859-2/Latin-2) 63: * <li>8859_3 (ISO-8859-3/Latin-3) 64: * <li>8859_4 (ISO-8859-4/Latin-4) 65: * <li>8859_5 (ISO-8859-5/Latin-5) 66: * <li>8859_6 (ISO-8859-6/Latin-6) 67: * <li>8859_7 (ISO-8859-7/Latin-7) 68: * <li>8859_8 (ISO-8859-8/Latin-8) 69: * <li>8859_9 (ISO-8859-9/Latin-9) 70: * <li>ASCII (7-bit ASCII) 71: * <li>UTF8 (UCS Transformation Format-8) 72: * <li>More Later 73: * </ul> 74: * 75: * @author Aaron M. Renn (arenn@urbanophile.com) 76: * @author Per Bothner (bothner@cygnus.com) 77: * @date April 17, 1998. 78: */ 79: public class OutputStreamWriter extends Writer 80: { 81: BufferedOutputStream out; 82: 83: /** 84: * This is the byte-character encoder class that does the writing and 85: * translation of characters to bytes before writing to the underlying 86: * class. 87: */ 88: UnicodeToBytes converter; 89: 90: /* Temporary buffer. */ 91: private char[] work; 92: private int wcount; 93: 94: private OutputStreamWriter(OutputStream out, UnicodeToBytes encoder) 95: { 96: this.out = out instanceof BufferedOutputStream 97: ? (BufferedOutputStream) out 98: : new BufferedOutputStream(out, 250); 99: /* Don't need to call super(out) here as long as the lock gets set. */ 100: this.lock = out; 101: this.converter = encoder; 102: } 103: 104: /** 105: * This method initializes a new instance of <code>OutputStreamWriter</code> 106: * to write to the specified stream using a caller supplied character 107: * encoding scheme. Note that due to a deficiency in the Java language 108: * design, there is no way to determine which encodings are supported. 109: * 110: * @param out The <code>OutputStream</code> to write to 111: * @param encoding_scheme The name of the encoding scheme to use for 112: * character to byte translation 113: * 114: * @exception UnsupportedEncodingException If the named encoding is 115: * not available. 116: */ 117: public OutputStreamWriter (OutputStream out, String encoding_scheme) 118: throws UnsupportedEncodingException 119: { 120: this(out, UnicodeToBytes.getEncoder(encoding_scheme)); 121: } 122: 123: /** 124: * This method initializes a new instance of <code>OutputStreamWriter</code> 125: * to write to the specified stream using the default encoding. 126: * 127: * @param out The <code>OutputStream</code> to write to 128: */ 129: public OutputStreamWriter (OutputStream out) 130: { 131: this(out, UnicodeToBytes.getDefaultEncoder()); 132: } 133: 134: /** 135: * This method initializes a new instance of <code>OutputStreamWriter</code> 136: * to write to the specified stream using a given <code>Charset</code>. 137: * 138: * @param out The <code>OutputStream</code> to write to 139: * @param cs The <code>Charset</code> of the encoding to use 140: */ 141: public OutputStreamWriter(OutputStream out, Charset cs) 142: { 143: this(out, new CharsetToBytesAdaptor(cs)); 144: } 145: 146: /** 147: * This method initializes a new instance of <code>OutputStreamWriter</code> 148: * to write to the specified stream using a given 149: * <code>CharsetEncoder</code>. 150: * 151: * @param out The <code>OutputStream</code> to write to 152: * @param enc The <code>CharsetEncoder</code> to encode the output with 153: */ 154: public OutputStreamWriter(OutputStream out, CharsetEncoder enc) 155: { 156: this(out, new CharsetToBytesAdaptor(enc)); 157: } 158: 159: /** 160: * This method closes this stream, and the underlying 161: * <code>OutputStream</code> 162: * 163: * @exception IOException If an error occurs 164: */ 165: public void close () throws IOException 166: { 167: synchronized (lock) 168: { 169: if (out != null) 170: { 171: converter.setFinished(); 172: flush(); 173: out.close(); 174: out = null; 175: } 176: work = null; 177: } 178: } 179: 180: /** 181: * This method returns the name of the character encoding scheme currently 182: * in use by this stream. If the stream has been closed, then this method 183: * may return <code>null</code>. 184: * 185: * @return The encoding scheme name 186: */ 187: public String getEncoding () 188: { 189: return out != null ? converter.getName() : null; 190: } 191: 192: /** 193: * This method flushes any buffered bytes to the underlying output sink. 194: * 195: * @exception IOException If an error occurs 196: */ 197: public void flush () throws IOException 198: { 199: synchronized (lock) 200: { 201: if (out == null) 202: throw new IOException("Stream closed"); 203: 204: // Always write -- if we are close()ing then we want to make 205: // sure the converter is flushed. 206: if (work == null) 207: work = new char[100]; 208: writeChars(work, 0, wcount); 209: wcount = 0; 210: 211: out.flush(); 212: } 213: } 214: 215: /** 216: * This method writes <code>count</code> characters from the specified 217: * array to the output stream starting at position <code>offset</code> 218: * into the array. 219: * 220: * @param buf The array of character to write from 221: * @param offset The offset into the array to start writing chars from 222: * @param count The number of chars to write. 223: * 224: * @exception IOException If an error occurs 225: */ 226: public void write (char[] buf, int offset, int count) throws IOException 227: { 228: synchronized (lock) 229: { 230: if (out == null) 231: throw new IOException("Stream closed"); 232: 233: if (wcount > 0) 234: { 235: writeChars(work, 0, wcount); 236: wcount = 0; 237: } 238: writeChars(buf, offset, count); 239: } 240: } 241: 242: /* 243: * Writes characters through to the inferior BufferedOutputStream. 244: * Ignores wcount and the work buffer. 245: */ 246: private void writeChars(char[] buf, int offset, int count) 247: throws IOException 248: { 249: do 250: { 251: // We must flush if out.count == out.buf.length. 252: // It is probably a good idea to flush if out.buf is almost full. 253: // This test is an approximation for "almost full". 254: if (out.count + count >= out.buf.length) 255: { 256: out.flush(); 257: if (out.count != 0) 258: throw new IOException("unable to flush output byte buffer"); 259: } 260: converter.setOutput(out.buf, out.count); 261: int converted = converter.write(buf, offset, count); 262: // Must set this before we flush the output stream, because 263: // flushing will reset 'out.count'. 264: out.count = converter.count; 265: // Flush if we cannot make progress. 266: if (converted == 0 && out.count == converter.count) 267: { 268: out.flush(); 269: if (out.count != 0) 270: throw new IOException("unable to flush output byte buffer"); 271: } 272: offset += converted; 273: count -= converted; 274: } 275: while (count > 0 || converter.havePendingBytes()); 276: } 277: 278: /** 279: * This method writes <code>count</code> bytes from the specified 280: * <code>String</code> starting at position <code>offset</code> into the 281: * <code>String</code>. 282: * 283: * @param str The <code>String</code> to write chars from 284: * @param offset The position in the <code>String</code> to start 285: * writing chars from 286: * @param count The number of chars to write 287: * 288: * @exception IOException If an error occurs 289: */ 290: public void write (String str, int offset, int count) throws IOException 291: { 292: synchronized (lock) 293: { 294: if (out == null) 295: throw new IOException("Stream closed"); 296: 297: if (work == null) 298: work = new char[100]; 299: int wlength = work.length; 300: while (count > 0) 301: { 302: int size = count; 303: if (wcount + size > wlength) 304: { 305: if (2*wcount > wlength) 306: { 307: writeChars(work, 0, wcount); 308: wcount = 0; 309: } 310: if (wcount + size > wlength) 311: size = wlength - wcount; 312: } 313: str.getChars(offset, offset+size, work, wcount); 314: offset += size; 315: count -= size; 316: wcount += size; 317: } 318: } 319: } 320: 321: /** 322: * This method writes a single character to the output stream. 323: * 324: * @param ch The char to write, passed as an int. 325: * 326: * @exception IOException If an error occurs 327: */ 328: public void write (int ch) throws IOException 329: { 330: synchronized (lock) 331: { 332: if (out == null) 333: throw new IOException("Stream closed"); 334: 335: if (work == null) 336: work = new char[100]; 337: if (wcount >= work.length) 338: { 339: writeChars(work, 0, wcount); 340: wcount = 0; 341: } 342: work[wcount++] = (char) ch; 343: } 344: } 345: 346: } // class OutputStreamWriter