mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			430 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
/* OutputStreamWriter.java -- Writer that converts chars to bytes
 | 
						|
   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005  Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package java.io;
 | 
						|
 | 
						|
import gnu.java.nio.charset.EncodingHelper;
 | 
						|
 | 
						|
import java.nio.ByteBuffer;
 | 
						|
import java.nio.CharBuffer;
 | 
						|
import java.nio.charset.CharacterCodingException;
 | 
						|
import java.nio.charset.Charset;
 | 
						|
import java.nio.charset.CharsetEncoder;
 | 
						|
import java.nio.charset.CodingErrorAction;
 | 
						|
import java.nio.charset.MalformedInputException;
 | 
						|
 | 
						|
/**
 | 
						|
 * This class writes characters to an output stream that is byte oriented
 | 
						|
 * It converts the chars that are written to bytes using an encoding layer,
 | 
						|
 * which is specific to a particular encoding standard.  The desired
 | 
						|
 * encoding can either be specified by name, or if no encoding is specified,
 | 
						|
 * the system default encoding will be used.  The system default encoding
 | 
						|
 * name is determined from the system property <code>file.encoding</code>.
 | 
						|
 * The only encodings that are guaranteed to be available are "8859_1"
 | 
						|
 * (the Latin-1 character set) and "UTF8".  Unfortunately, Java does not
 | 
						|
 * provide a mechanism for listing the encodings that are supported in
 | 
						|
 * a given implementation.
 | 
						|
 * <p>
 | 
						|
 * Here is a list of standard encoding names that may be available:
 | 
						|
 * <p>
 | 
						|
 * <ul>
 | 
						|
 * <li>8859_1 (ISO-8859-1/Latin-1)
 | 
						|
 * <li>8859_2 (ISO-8859-2/Latin-2)
 | 
						|
 * <li>8859_3 (ISO-8859-3/Latin-3)
 | 
						|
 * <li>8859_4 (ISO-8859-4/Latin-4)
 | 
						|
 * <li>8859_5 (ISO-8859-5/Latin-5)
 | 
						|
 * <li>8859_6 (ISO-8859-6/Latin-6)
 | 
						|
 * <li>8859_7 (ISO-8859-7/Latin-7)
 | 
						|
 * <li>8859_8 (ISO-8859-8/Latin-8)
 | 
						|
 * <li>8859_9 (ISO-8859-9/Latin-9)
 | 
						|
 * <li>ASCII (7-bit ASCII)
 | 
						|
 * <li>UTF8 (UCS Transformation Format-8)
 | 
						|
 * <li>More Later
 | 
						|
 * </ul>
 | 
						|
 *
 | 
						|
 * @author Aaron M. Renn (arenn@urbanophile.com)
 | 
						|
 * @author Per Bothner (bothner@cygnus.com)
 | 
						|
 * @date April 17, 1998.
 | 
						|
 */
 | 
						|
public class OutputStreamWriter extends Writer
 | 
						|
{
 | 
						|
  /**
 | 
						|
   * The output stream.
 | 
						|
   */
 | 
						|
  private OutputStream out;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The charset encoder.
 | 
						|
   */
 | 
						|
  private final CharsetEncoder encoder;
 | 
						|
 | 
						|
  /**
 | 
						|
   * java.io canonical name of the encoding.
 | 
						|
   */
 | 
						|
  private final String encodingName;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Buffer output before character conversion as it has costly overhead.
 | 
						|
   */
 | 
						|
  private final CharBuffer outputBuffer;
 | 
						|
  private final static int BUFFER_SIZE = 1024;
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method initializes a new instance of <code>OutputStreamWriter</code>
 | 
						|
   * to write to the specified stream using a caller supplied character
 | 
						|
   * encoding scheme.  Note that due to a deficiency in the Java language
 | 
						|
   * design, there is no way to determine which encodings are supported.
 | 
						|
   *
 | 
						|
   * @param out The <code>OutputStream</code> to write to
 | 
						|
   * @param encoding_scheme The name of the encoding scheme to use for
 | 
						|
   * character to byte translation
 | 
						|
   *
 | 
						|
   * @exception UnsupportedEncodingException If the named encoding is
 | 
						|
   * not available.
 | 
						|
   */
 | 
						|
  public OutputStreamWriter (OutputStream out, String encoding_scheme)
 | 
						|
    throws UnsupportedEncodingException
 | 
						|
  {
 | 
						|
    CharsetEncoder encoder;
 | 
						|
    String encodingName;
 | 
						|
    this.out = out;
 | 
						|
    outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        // Don't use NIO if avoidable
 | 
						|
        if(EncodingHelper.isISOLatin1(encoding_scheme))
 | 
						|
          {
 | 
						|
            encodingName = "ISO8859_1";
 | 
						|
            encoder = null;
 | 
						|
          }
 | 
						|
       else
 | 
						|
         {
 | 
						|
           /*
 | 
						|
            * Workaround for encodings with a byte-order-mark.
 | 
						|
            * We only want to write it once per stream.
 | 
						|
            */
 | 
						|
           try
 | 
						|
             {
 | 
						|
               if(encoding_scheme.equalsIgnoreCase("UnicodeBig") ||
 | 
						|
                  encoding_scheme.equalsIgnoreCase("UTF-16") ||
 | 
						|
                  encoding_scheme.equalsIgnoreCase("UTF16"))
 | 
						|
                 {
 | 
						|
                   encoding_scheme = "UTF-16BE";
 | 
						|
                   out.write((byte)0xFE);
 | 
						|
                   out.write((byte)0xFF);
 | 
						|
                 }
 | 
						|
               else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle"))
 | 
						|
                 {
 | 
						|
                   encoding_scheme = "UTF-16LE";
 | 
						|
                   out.write((byte)0xFF);
 | 
						|
                   out.write((byte)0xFE);
 | 
						|
                 }
 | 
						|
             }
 | 
						|
           catch(IOException ioe)
 | 
						|
             {
 | 
						|
             }
 | 
						|
 | 
						|
           Charset cs = EncodingHelper.getCharset(encoding_scheme);
 | 
						|
           if(cs == null)
 | 
						|
             throw new UnsupportedEncodingException("Encoding "+encoding_scheme+
 | 
						|
                                                    " unknown");
 | 
						|
           encoder = cs.newEncoder();
 | 
						|
           encodingName = EncodingHelper.getOldCanonical(cs.name());
 | 
						|
 | 
						|
           encoder.onMalformedInput(CodingErrorAction.REPLACE);
 | 
						|
           encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
 | 
						|
         }
 | 
						|
      }
 | 
						|
    catch(RuntimeException e)
 | 
						|
      {
 | 
						|
        // Default to ISO Latin-1, will happen if this is called, for instance,
 | 
						|
        //  before the NIO provider is loadable.
 | 
						|
        encoder = null;
 | 
						|
        encodingName = "ISO8859_1";
 | 
						|
      }
 | 
						|
    this.encoder = encoder;
 | 
						|
    this.encodingName = encodingName;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method initializes a new instance of <code>OutputStreamWriter</code>
 | 
						|
   * to write to the specified stream using the default encoding.
 | 
						|
   *
 | 
						|
   * @param out The <code>OutputStream</code> to write to
 | 
						|
   */
 | 
						|
  public OutputStreamWriter (OutputStream out)
 | 
						|
  {
 | 
						|
    CharsetEncoder encoder;
 | 
						|
    String encodingName;
 | 
						|
    this.out = out;
 | 
						|
    outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
 | 
						|
    try
 | 
						|
      {
 | 
						|
        String encoding = System.getProperty("file.encoding");
 | 
						|
        Charset cs = Charset.forName(encoding);
 | 
						|
        encoder = cs.newEncoder();
 | 
						|
        encodingName =  EncodingHelper.getOldCanonical(cs.name());
 | 
						|
      }
 | 
						|
    catch(RuntimeException e)
 | 
						|
      {
 | 
						|
        encoder = null;
 | 
						|
        encodingName = "ISO8859_1";
 | 
						|
      }
 | 
						|
 | 
						|
    if(encoder != null)
 | 
						|
      {
 | 
						|
        encoder.onMalformedInput(CodingErrorAction.REPLACE);
 | 
						|
        encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
 | 
						|
      }
 | 
						|
    this.encoder = encoder;
 | 
						|
    this.encodingName = encodingName;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method initializes a new instance of <code>OutputStreamWriter</code>
 | 
						|
   * to write to the specified stream using a given <code>Charset</code>.
 | 
						|
   *
 | 
						|
   * @param out The <code>OutputStream</code> to write to
 | 
						|
   * @param cs The <code>Charset</code> of the encoding to use
 | 
						|
   *
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public OutputStreamWriter(OutputStream out, Charset cs)
 | 
						|
  {
 | 
						|
    this.out = out;
 | 
						|
    encoder = cs.newEncoder();
 | 
						|
    encoder.onMalformedInput(CodingErrorAction.REPLACE);
 | 
						|
    encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
 | 
						|
    outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
 | 
						|
    encodingName = EncodingHelper.getOldCanonical(cs.name());
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method initializes a new instance of <code>OutputStreamWriter</code>
 | 
						|
   * to write to the specified stream using a given
 | 
						|
   * <code>CharsetEncoder</code>.
 | 
						|
   *
 | 
						|
   * @param out The <code>OutputStream</code> to write to
 | 
						|
   * @param enc The <code>CharsetEncoder</code> to encode the output with
 | 
						|
   *
 | 
						|
   * @since 1.5
 | 
						|
   */
 | 
						|
  public OutputStreamWriter(OutputStream out, CharsetEncoder enc)
 | 
						|
  {
 | 
						|
    this.out = out;
 | 
						|
    encoder = enc;
 | 
						|
    outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
 | 
						|
    Charset cs = enc.charset();
 | 
						|
    if (cs == null)
 | 
						|
      encodingName = "US-ASCII";
 | 
						|
    else
 | 
						|
      encodingName = EncodingHelper.getOldCanonical(cs.name());
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method closes this stream, and the underlying
 | 
						|
   * <code>OutputStream</code>
 | 
						|
   *
 | 
						|
   * @exception IOException If an error occurs
 | 
						|
   */
 | 
						|
  public void close () throws IOException
 | 
						|
  {
 | 
						|
    if(out == null)
 | 
						|
      return;
 | 
						|
    flush();
 | 
						|
    out.close ();
 | 
						|
    out = null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method returns the name of the character encoding scheme currently
 | 
						|
   * in use by this stream.  If the stream has been closed, then this method
 | 
						|
   * may return <code>null</code>.
 | 
						|
   *
 | 
						|
   * @return The encoding scheme name
 | 
						|
   */
 | 
						|
  public String getEncoding ()
 | 
						|
  {
 | 
						|
    return out != null ? encodingName : null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method flushes any buffered bytes to the underlying output sink.
 | 
						|
   *
 | 
						|
   * @exception IOException If an error occurs
 | 
						|
   */
 | 
						|
  public void flush () throws IOException
 | 
						|
  {
 | 
						|
      if(out != null){
 | 
						|
          if(outputBuffer != null){
 | 
						|
              char[] buf = new char[outputBuffer.position()];
 | 
						|
              if(buf.length > 0){
 | 
						|
                  outputBuffer.flip();
 | 
						|
                  outputBuffer.get(buf);
 | 
						|
                  writeConvert(buf, 0, buf.length);
 | 
						|
                  outputBuffer.clear();
 | 
						|
              }
 | 
						|
          }
 | 
						|
          out.flush ();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method writes <code>count</code> characters from the specified
 | 
						|
   * array to the output stream starting at position <code>offset</code>
 | 
						|
   * into the array.
 | 
						|
   *
 | 
						|
   * @param buf The array of character to write from
 | 
						|
   * @param offset The offset into the array to start writing chars from
 | 
						|
   * @param count The number of chars to write.
 | 
						|
   *
 | 
						|
   * @exception IOException If an error occurs
 | 
						|
   */
 | 
						|
  public void write (char[] buf, int offset, int count) throws IOException
 | 
						|
  {
 | 
						|
    if(out == null)
 | 
						|
      throw new IOException("Stream is closed.");
 | 
						|
    if(buf == null)
 | 
						|
      throw new IOException("Buffer is null.");
 | 
						|
 | 
						|
    if(outputBuffer != null)
 | 
						|
        {
 | 
						|
            if(count >= outputBuffer.remaining())
 | 
						|
                {
 | 
						|
                    int r = outputBuffer.remaining();
 | 
						|
                    outputBuffer.put(buf, offset, r);
 | 
						|
                    writeConvert(outputBuffer.array(), 0, BUFFER_SIZE);
 | 
						|
                    outputBuffer.clear();
 | 
						|
                    offset += r;
 | 
						|
                    count -= r;
 | 
						|
                    // if the remaining bytes is larger than the whole buffer,
 | 
						|
                    // just don't buffer.
 | 
						|
                    if(count >= outputBuffer.remaining()){
 | 
						|
                      writeConvert(buf, offset, count);
 | 
						|
                      return;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            outputBuffer.put(buf, offset, count);
 | 
						|
        } else writeConvert(buf, offset, count);
 | 
						|
  }
 | 
						|
 | 
						|
 /**
 | 
						|
  * Converts and writes characters.
 | 
						|
  */
 | 
						|
  private void writeConvert (char[] buf, int offset, int count)
 | 
						|
      throws IOException
 | 
						|
  {
 | 
						|
    if(encoder == null)
 | 
						|
    {
 | 
						|
      byte[] b = new byte[count];
 | 
						|
      for(int i=0;i<count;i++)
 | 
						|
        b[i] = nullConversion(buf[offset+i]);
 | 
						|
      out.write(b);
 | 
						|
    } else {
 | 
						|
      try  {
 | 
						|
        ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count));
 | 
						|
        encoder.reset();
 | 
						|
        if(output.hasArray())
 | 
						|
          out.write(output.array());
 | 
						|
        else
 | 
						|
          {
 | 
						|
            byte[] outbytes = new byte[output.remaining()];
 | 
						|
            output.get(outbytes);
 | 
						|
            out.write(outbytes);
 | 
						|
          }
 | 
						|
      } catch(IllegalStateException e) {
 | 
						|
        throw new IOException("Internal error.");
 | 
						|
      } catch(MalformedInputException e) {
 | 
						|
        throw new IOException("Invalid character sequence.");
 | 
						|
      } catch(CharacterCodingException e) {
 | 
						|
        throw new IOException("Unmappable character.");
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private byte nullConversion(char c) {
 | 
						|
          return (byte)((c <= 0xFF)?c:'?');
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method writes <code>count</code> bytes from the specified
 | 
						|
   * <code>String</code> starting at position <code>offset</code> into the
 | 
						|
   * <code>String</code>.
 | 
						|
   *
 | 
						|
   * @param str The <code>String</code> to write chars from
 | 
						|
   * @param offset The position in the <code>String</code> to start
 | 
						|
   * writing chars from
 | 
						|
   * @param count The number of chars to write
 | 
						|
   *
 | 
						|
   * @exception IOException If an error occurs
 | 
						|
   */
 | 
						|
  public void write (String str, int offset, int count) throws IOException
 | 
						|
  {
 | 
						|
    if(str == null)
 | 
						|
      throw new IOException("String is null.");
 | 
						|
 | 
						|
    write(str.toCharArray(), offset, count);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This method writes a single character to the output stream.
 | 
						|
   *
 | 
						|
   * @param ch The char to write, passed as an int.
 | 
						|
   *
 | 
						|
   * @exception IOException If an error occurs
 | 
						|
   */
 | 
						|
  public void write (int ch) throws IOException
 | 
						|
  {
 | 
						|
          // No buffering, no encoding ... just pass through
 | 
						|
          if (encoder == null && outputBuffer == null) {
 | 
						|
                  out.write(nullConversion((char)ch));
 | 
						|
          } else {
 | 
						|
                  if (outputBuffer != null) {
 | 
						|
                          if (outputBuffer.remaining() == 0) {
 | 
						|
                                  writeConvert(outputBuffer.array(), 0, BUFFER_SIZE);
 | 
						|
                                  outputBuffer.clear();
 | 
						|
                          }
 | 
						|
                          outputBuffer.put((char)ch);
 | 
						|
                  } else {
 | 
						|
                      writeConvert(new char[]{ (char)ch }, 0, 1);
 | 
						|
                  }
 | 
						|
          }
 | 
						|
  }
 | 
						|
} // class OutputStreamWriter
 |