001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.kernel.io;
016    
017    import com.liferay.portal.kernel.nio.charset.CharsetDecoderUtil;
018    import com.liferay.portal.kernel.util.StringPool;
019    
020    import java.io.IOException;
021    import java.io.OutputStream;
022    import java.io.Writer;
023    
024    import java.nio.ByteBuffer;
025    import java.nio.CharBuffer;
026    import java.nio.charset.CharsetDecoder;
027    import java.nio.charset.CoderResult;
028    
029    /**
030     * @author Shuyang Zhou
031     */
032    public class WriterOutputStream extends OutputStream {
033    
034            public WriterOutputStream(Writer writer) {
035                    this(
036                            writer, StringPool.UTF8, _DEFAULT_INTPUT_BUFFER_SIZE,
037                            _DEFAULT_OUTPUT_BUFFER_SIZE, false);
038            }
039    
040            public WriterOutputStream(Writer writer, String charsetName) {
041                    this(
042                            writer, charsetName, _DEFAULT_INTPUT_BUFFER_SIZE,
043                            _DEFAULT_OUTPUT_BUFFER_SIZE, false);
044            }
045    
046            public WriterOutputStream(
047                    Writer writer, String charsetName, boolean autoFlush) {
048    
049                    this(
050                            writer, charsetName, _DEFAULT_INTPUT_BUFFER_SIZE,
051                            _DEFAULT_OUTPUT_BUFFER_SIZE, autoFlush);
052            }
053    
054            public WriterOutputStream(
055                    Writer writer, String charsetName, int inputBufferSize,
056                    int outputBufferSize) {
057    
058                    this(writer, charsetName, inputBufferSize, outputBufferSize, false);
059            }
060    
061            public WriterOutputStream(
062                    Writer writer, String charsetName, int inputBufferSize,
063                    int outputBufferSize, boolean autoFlush) {
064    
065                    if (inputBufferSize < 4) {
066                            throw new IllegalArgumentException(
067                                    "Input buffer size " + inputBufferSize + " is less than 4");
068                    }
069    
070                    if (outputBufferSize <= 0) {
071                            throw new IllegalArgumentException(
072                                    "Output buffer size " + outputBufferSize +
073                                            " must be a positive number");
074                    }
075    
076                    _writer = writer;
077                    _charsetName = charsetName;
078                    _charsetDecoder = CharsetDecoderUtil.getCharsetDecoder(charsetName);
079                    _inputBuffer = ByteBuffer.allocate(inputBufferSize);
080                    _outputBuffer = CharBuffer.allocate(outputBufferSize);
081                    _autoFlush = autoFlush;
082            }
083    
084            public void close() throws IOException {
085                    _doDecode(true);
086                    _doFlush();
087    
088                    _writer.close();
089            }
090    
091            public void flush() throws IOException {
092                    _doFlush();
093    
094                    _writer.flush();
095            }
096    
097            public String getEncoding() {
098                    return _charsetName;
099            }
100    
101            public void write(byte[] byteArray) throws IOException {
102                    write(byteArray, 0, byteArray.length);
103            }
104    
105            public void write(byte[] byteArray, int offset, int length)
106                    throws IOException {
107    
108                    while (length > 0) {
109                            int blockSize = Math.min(length, _inputBuffer.remaining());
110    
111                            _inputBuffer.put(byteArray, offset, blockSize);
112    
113                            _doDecode(false);
114    
115                            length -= blockSize;
116                            offset += blockSize;
117                    }
118    
119                    if (_autoFlush) {
120                            _doFlush();
121                    }
122            }
123    
124            public void write(int b) throws IOException {
125                    write(new byte[] {(byte)b}, 0, 1);
126            }
127    
128            private void _doDecode(boolean endOfInput) throws IOException {
129                    _inputBuffer.flip();
130    
131                    while (true) {
132                            CoderResult coderResult = _charsetDecoder.decode(
133                                    _inputBuffer, _outputBuffer, endOfInput);
134    
135                            if (coderResult.isOverflow()) {
136                                    _doFlush();
137                            }
138                            else if (coderResult.isUnderflow()) {
139                                    break;
140                            }
141                            else {
142                                    throw new IOException("Unexcepted coder result " + coderResult);
143                            }
144                    }
145    
146                    _inputBuffer.compact();
147            }
148    
149            private void _doFlush() throws IOException {
150                    if (_outputBuffer.position() > 0) {
151                            _writer.write(_outputBuffer.array(), 0, _outputBuffer.position());
152    
153                            _outputBuffer.rewind();
154                    }
155            }
156    
157            private static final int _DEFAULT_INTPUT_BUFFER_SIZE = 128;
158    
159            private static final int _DEFAULT_OUTPUT_BUFFER_SIZE = 1024;
160    
161            private boolean _autoFlush;
162            private CharsetDecoder _charsetDecoder;
163            private String _charsetName;
164            private ByteBuffer _inputBuffer;
165            private CharBuffer _outputBuffer;
166            private Writer _writer;
167    
168    }