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