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.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: flush(); 172: out.close(); 173: out = null; 174: } 175: work = null; 176: } 177: } 178: 179: /** 180: * This method returns the name of the character encoding scheme currently 181: * in use by this stream. If the stream has been closed, then this method 182: * may return <code>null</code>. 183: * 184: * @return The encoding scheme name 185: */ 186: public String getEncoding () 187: { 188: return out != null ? converter.getName() : null; 189: } 190: 191: /** 192: * This method flushes any buffered bytes to the underlying output sink. 193: * 194: * @exception IOException If an error occurs 195: */ 196: public void flush () throws IOException 197: { 198: synchronized (lock) 199: { 200: if (out == null) 201: throw new IOException("Stream closed"); 202: 203: if (wcount > 0) 204: { 205: writeChars(work, 0, wcount); 206: wcount = 0; 207: } 208: out.flush(); 209: } 210: } 211: 212: /** 213: * This method writes <code>count</code> characters from the specified 214: * array to the output stream starting at position <code>offset</code> 215: * into the array. 216: * 217: * @param buf The array of character to write from 218: * @param offset The offset into the array to start writing chars from 219: * @param count The number of chars to write. 220: * 221: * @exception IOException If an error occurs 222: */ 223: public void write (char[] buf, int offset, int count) throws IOException 224: { 225: synchronized (lock) 226: { 227: if (out == null) 228: throw new IOException("Stream closed"); 229: 230: if (wcount > 0) 231: { 232: writeChars(work, 0, wcount); 233: wcount = 0; 234: } 235: writeChars(buf, offset, count); 236: } 237: } 238: 239: /* 240: * Writes characters through to the inferior BufferedOutputStream. 241: * Ignores wcount and the work buffer. 242: */ 243: private void writeChars(char[] buf, int offset, int count) 244: throws IOException 245: { 246: while (count > 0 || converter.havePendingBytes()) 247: { 248: // We must flush if out.count == out.buf.length. 249: // It is probably a good idea to flush if out.buf is almost full. 250: // This test is an approximation for "almost full". 251: if (out.count + count >= out.buf.length) 252: { 253: out.flush(); 254: if (out.count != 0) 255: throw new IOException("unable to flush output byte buffer"); 256: } 257: converter.setOutput(out.buf, out.count); 258: int converted = converter.write(buf, offset, count); 259: // Flush if we cannot make progress. 260: if (converted == 0 && out.count == converter.count) 261: { 262: out.flush(); 263: if (out.count != 0) 264: throw new IOException("unable to flush output byte buffer"); 265: } 266: offset += converted; 267: count -= converted; 268: out.count = converter.count; 269: } 270: } 271: 272: /** 273: * This method writes <code>count</code> bytes from the specified 274: * <code>String</code> starting at position <code>offset</code> into the 275: * <code>String</code>. 276: * 277: * @param str The <code>String</code> to write chars from 278: * @param offset The position in the <code>String</code> to start 279: * writing chars from 280: * @param count The number of chars to write 281: * 282: * @exception IOException If an error occurs 283: */ 284: public void write (String str, int offset, int count) throws IOException 285: { 286: synchronized (lock) 287: { 288: if (out == null) 289: throw new IOException("Stream closed"); 290: 291: if (work == null) 292: work = new char[100]; 293: int wlength = work.length; 294: while (count > 0) 295: { 296: int size = count; 297: if (wcount + size > wlength) 298: { 299: if (2*wcount > wlength) 300: { 301: writeChars(work, 0, wcount); 302: wcount = 0; 303: } 304: if (wcount + size > wlength) 305: size = wlength - wcount; 306: } 307: str.getChars(offset, offset+size, work, wcount); 308: offset += size; 309: count -= size; 310: wcount += size; 311: } 312: } 313: } 314: 315: /** 316: * This method writes a single character to the output stream. 317: * 318: * @param ch The char to write, passed as an int. 319: * 320: * @exception IOException If an error occurs 321: */ 322: public void write (int ch) throws IOException 323: { 324: synchronized (lock) 325: { 326: if (out == null) 327: throw new IOException("Stream closed"); 328: 329: if (work == null) 330: work = new char[100]; 331: if (wcount >= work.length) 332: { 333: writeChars(work, 0, wcount); 334: wcount = 0; 335: } 336: work[wcount++] = (char) ch; 337: } 338: } 339: 340: } // class OutputStreamWriter