001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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)
110                    throws IOException {
111    
112                    while (length > 0) {
113                            int blockSize = Math.min(length, _inputBuffer.remaining());
114    
115                            _inputBuffer.put(bytes, offset, blockSize);
116    
117                            _doDecode(false);
118    
119                            length -= blockSize;
120                            offset += blockSize;
121                    }
122    
123                    if (_autoFlush) {
124                            _doFlush();
125                    }
126            }
127    
128            @Override
129            public void write(int b) throws IOException {
130                    write(new byte[] {(byte)b}, 0, 1);
131            }
132    
133            private void _doDecode(boolean endOfInput) throws IOException {
134                    _inputBuffer.flip();
135    
136                    while (true) {
137                            CoderResult coderResult = _charsetDecoder.decode(
138                                    _inputBuffer, _outputBuffer, endOfInput);
139    
140                            if (coderResult.isOverflow()) {
141                                    _doFlush();
142                            }
143                            else if (coderResult.isUnderflow()) {
144                                    break;
145                            }
146                            else {
147                                    throw new IOException("Unexcepted coder result " + coderResult);
148                            }
149                    }
150    
151                    _inputBuffer.compact();
152            }
153    
154            private void _doFlush() throws IOException {
155                    if (_outputBuffer.position() > 0) {
156                            _writer.write(_outputBuffer.array(), 0, _outputBuffer.position());
157    
158                            _outputBuffer.rewind();
159                    }
160            }
161    
162            private static final int _DEFAULT_INTPUT_BUFFER_SIZE = 128;
163    
164            private static final int _DEFAULT_OUTPUT_BUFFER_SIZE = 1024;
165    
166            private boolean _autoFlush;
167            private CharsetDecoder _charsetDecoder;
168            private String _charsetName;
169            private ByteBuffer _inputBuffer;
170            private CharBuffer _outputBuffer;
171            private Writer _writer;
172    
173    }