All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.embed.io.WriterOutputStream Maven / Gradle / Ivy

/**
 * **** BEGIN LICENSE BLOCK *****
 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Common Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.eclipse.org/legal/cpl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2009 Yoko Harada 
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the CPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the CPL, the GPL or the LGPL.
 * **** END LICENSE BLOCK *****
 */
package org.jruby.embed.io;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;

/**
 * A WriterOutputStream converts java.io.Writer to java.io.OutputStream.
 *
 * @author Yoko Harada 
 */
public class WriterOutputStream extends OutputStream {

    private final Writer writer;
    private boolean isOpen = true;
    private CharsetDecoder decoder;

    /**
     * Creates WriterOutputStream from given java.io.Writer object with a default encoding.
     *
     * @param writer java.io.Writer object to be converted to.
     */
    public WriterOutputStream(Writer writer) {
        this(writer, null);
    }

    /**
     * Creates WriterOutputStream from given java.io.Writer object with a specified encoding.
     *
     * @param writer java.io.Writer object to be converted to.
     */
    public WriterOutputStream(Writer writer, String encoding) {
        this.writer = writer;       
        if (encoding == null && writer instanceof OutputStreamWriter) {
            // this encoding might be null when writer has been closed
            encoding = ((OutputStreamWriter) writer).getEncoding();
        }
        if (encoding == null) {
            encoding = Charset.defaultCharset().name();
        } else if (!Charset.isSupported(encoding)) {
            throw new IllegalArgumentException(encoding + " is not supported");
        }
        decoder = Charset.forName(encoding).newDecoder();
        decoder.onMalformedInput(CodingErrorAction.REPLACE);
        decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
    }

    /**
     * Closes this output stream and releases any system resources
     * associated with this stream. The general contract of close
     * is that it closes the output stream. A closed stream cannot perform
     * output operations and cannot be reopened.
     * 

* * @exception IOException if an I/O error occurs. */ @Override public void close() throws IOException { synchronized (writer) { if (!isOpen) { throw new IOException("This stream has been already closed."); } isOpen = false; decoder = null; writer.close(); } } /** * Flushes this output stream and forces any buffered output bytes * to be written out. The general contract of flush is * that calling it is an indication that, if any bytes previously * written have been buffered by the implementation of the output * stream, such bytes should immediately be written to their * intended destination. *

* If the intended destination of this stream is an abstraction provided by * the underlying operating system, for example a file, then flushing the * stream guarantees only that bytes previously written to the stream are * passed to the operating system for writing; it does not guarantee that * they are actually written to a physical device such as a disk drive. *

* * @exception IOException if an I/O error occurs. */ @Override public void flush() throws IOException { synchronized (writer) { if (!isOpen) { return; } writer.flush(); } } /** * Writes the specified byte to this output stream. The general * contract for write is that one byte is written * to the output stream. The byte to be written is the eight * low-order bits of the argument b. The 24 * high-order bits of b are ignored. * * @param b the byte. * @exception IOException if an I/O error occurs. In particular, * an IOException may be thrown if the * output stream has been closed. */ @Override public void write(int b) throws IOException { byte[] bb = new byte[]{(byte) b}; write(bb, 0, 1); } /** * Writes b.length bytes from the specified byte array * to this output stream. The general contract for write(b) * is that it should have exactly the same effect as the call * write(b, 0, b.length). * * @param b the data. * @exception IOException if an I/O error occurs. * @see java.io.OutputStream#write(byte[], int, int) */ @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } /** * Writes len bytes from the specified byte array * starting at offset off to this output stream. * The general contract for write(b, off, len) is that * some of the bytes in the array b are written to the * output stream in order; element b[off] is the first * byte written and b[off+len-1] is the last byte written * by this operation. *

* If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is thrown. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @exception IOException if an I/O error occurs. In particular, * an IOException is thrown if the output * stream is closed. */ @Override public void write(byte[] b, int off, int len) throws IOException { synchronized (writer) { if (!isOpen) { return; } if (off < 0 || len <= 0 || (off + len) > b.length) { throw new IndexOutOfBoundsException(); } ByteBuffer bytes = ByteBuffer.wrap(b, off, len); CharBuffer chars = CharBuffer.allocate(len); byte2char(bytes, chars); char[] cbuf = new char[chars.length()]; chars.get(cbuf, 0, chars.length()); writer.write(cbuf); writer.flush(); } } private void byte2char(ByteBuffer bytes, CharBuffer chars) throws IOException { decoder.reset(); chars.clear(); CoderResult result = decoder.decode(bytes, chars, true); if (result.isError() || result.isOverflow()) { throw new IOException(result.toString()); } else if (result.isUnderflow()) { chars.flip(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy