001    /**
002     * Copyright (c) 2000-2012 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            @Override
085            public void close() throws IOException {
086                    _doDecode(true);
087                    _doFlush();
088    
089                    _writer.close();
090            }
091    
092            @Override
093            public void flush() throws IOException {
094                    _doFlush();
095    
096                    _writer.flush();
097            }
098    
099            public String getEncoding() {
100                    return _charsetName;
101            }
102    
103            @Override
104            public void write(byte[] bytes) throws IOException {
105                    write(bytes, 0, bytes.length);
106            }
107    
108            @Override
109            public void write(byte[] bytes, int offset, int length) throws IOException {
110                    while (length > 0) {
111                            int blockSize = Math.min(length, _inputBuffer.remaining());
112    
113                            _inputBuffer.put(bytes, offset, blockSize);
114    
115                            _doDecode(false);
116    
117                            length -= blockSize;
118                            offset += blockSize;
119                    }
120    
121                    if (_autoFlush) {
122                            _doFlush();
123                    }
124            }
125    
126            @Override
127            public void write(int b) throws IOException {
128                    write(new byte[] {(byte)b}, 0, 1);
129            }
130    
131            private void _doDecode(boolean endOfInput) throws IOException {
132                    _inputBuffer.flip();
133    
134                    while (true) {
135                            CoderResult coderResult = _charsetDecoder.decode(
136                                    _inputBuffer, _outputBuffer, endOfInput);
137    
138                            if (coderResult.isOverflow()) {
139                                    _doFlush();
140                            }
141                            else if (coderResult.isUnderflow()) {
142                                    break;
143                            }
144                            else {
145                                    throw new IOException("Unexcepted coder result " + coderResult);
146                            }
147                    }
148    
149                    _inputBuffer.compact();
150            }
151    
152            private void _doFlush() throws IOException {
153                    if (_outputBuffer.position() > 0) {
154                            _writer.write(_outputBuffer.array(), 0, _outputBuffer.position());
155    
156                            _outputBuffer.rewind();
157                    }
158            }
159    
160            private static final int _DEFAULT_INTPUT_BUFFER_SIZE = 128;
161    
162            private static final int _DEFAULT_OUTPUT_BUFFER_SIZE = 1024;
163    
164            private boolean _autoFlush;
165            private CharsetDecoder _charsetDecoder;
166            private String _charsetName;
167            private ByteBuffer _inputBuffer;
168            private CharBuffer _outputBuffer;
169            private Writer _writer;
170    
171    }