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.Charset;
027    import java.nio.charset.CharsetDecoder;
028    import java.nio.charset.CoderResult;
029    
030    /**
031     * @author Shuyang Zhou
032     */
033    public class WriterOutputStream extends OutputStream {
034    
035            public WriterOutputStream(Writer writer) {
036                    this(
037                            writer, StringPool.UTF8, _DEFAULT_INTPUT_BUFFER_SIZE,
038                            _DEFAULT_OUTPUT_BUFFER_SIZE, false);
039            }
040    
041            public WriterOutputStream(Writer writer, String charsetName) {
042                    this(
043                            writer, charsetName, _DEFAULT_INTPUT_BUFFER_SIZE,
044                            _DEFAULT_OUTPUT_BUFFER_SIZE, false);
045            }
046    
047            public WriterOutputStream(
048                    Writer writer, String charsetName, boolean autoFlush) {
049    
050                    this(
051                            writer, charsetName, _DEFAULT_INTPUT_BUFFER_SIZE,
052                            _DEFAULT_OUTPUT_BUFFER_SIZE, autoFlush);
053            }
054    
055            public WriterOutputStream(
056                    Writer writer, String charsetName, int inputBufferSize,
057                    int outputBufferSize) {
058    
059                    this(writer, charsetName, inputBufferSize, outputBufferSize, false);
060            }
061    
062            public WriterOutputStream(
063                    Writer writer, String charsetName, int inputBufferSize,
064                    int outputBufferSize, boolean autoFlush) {
065    
066                    if (inputBufferSize < 4) {
067                            throw new IllegalArgumentException(
068                                    "Input buffer size " + inputBufferSize + " is less than 4");
069                    }
070    
071                    if (outputBufferSize <= 0) {
072                            throw new IllegalArgumentException(
073                                    "Output buffer size " + outputBufferSize +
074                                            " must be a positive number");
075                    }
076    
077                    if (charsetName == null) {
078                            charsetName = _DEFAULT_CHARSET_NAME;
079                    }
080    
081                    _writer = writer;
082                    _charsetName = charsetName;
083                    _charsetDecoder = CharsetDecoderUtil.getCharsetDecoder(charsetName);
084                    _inputBuffer = ByteBuffer.allocate(inputBufferSize);
085                    _outputBuffer = CharBuffer.allocate(outputBufferSize);
086                    _autoFlush = autoFlush;
087            }
088    
089            @Override
090            public void close() throws IOException {
091                    _doDecode(true);
092                    _doFlush();
093    
094                    _writer.close();
095            }
096    
097            @Override
098            public void flush() throws IOException {
099                    _doFlush();
100    
101                    _writer.flush();
102            }
103    
104            public String getEncoding() {
105                    return _charsetName;
106            }
107    
108            @Override
109            public void write(byte[] bytes) throws IOException {
110                    write(bytes, 0, bytes.length);
111            }
112    
113            @Override
114            public void write(byte[] bytes, int offset, int length) throws IOException {
115                    while (length > 0) {
116                            int blockSize = Math.min(length, _inputBuffer.remaining());
117    
118                            _inputBuffer.put(bytes, offset, blockSize);
119    
120                            _doDecode(false);
121    
122                            length -= blockSize;
123                            offset += blockSize;
124                    }
125    
126                    if (_autoFlush) {
127                            _doFlush();
128                    }
129            }
130    
131            @Override
132            public void write(int b) throws IOException {
133                    write(new byte[] {(byte)b}, 0, 1);
134            }
135    
136            private void _doDecode(boolean endOfInput) throws IOException {
137                    _inputBuffer.flip();
138    
139                    while (true) {
140                            CoderResult coderResult = _charsetDecoder.decode(
141                                    _inputBuffer, _outputBuffer, endOfInput);
142    
143                            if (coderResult.isOverflow()) {
144                                    _doFlush();
145                            }
146                            else if (coderResult.isUnderflow()) {
147                                    break;
148                            }
149                            else {
150                                    throw new IOException("Unexcepted coder result " + coderResult);
151                            }
152                    }
153    
154                    _inputBuffer.compact();
155            }
156    
157            private void _doFlush() throws IOException {
158                    if (_outputBuffer.position() > 0) {
159                            _writer.write(_outputBuffer.array(), 0, _outputBuffer.position());
160    
161                            _outputBuffer.rewind();
162                    }
163            }
164    
165            private static final String _DEFAULT_CHARSET_NAME =
166                    Charset.defaultCharset().name();
167    
168            private static final int _DEFAULT_INTPUT_BUFFER_SIZE = 128;
169    
170            private static final int _DEFAULT_OUTPUT_BUFFER_SIZE = 1024;
171    
172            private boolean _autoFlush;
173            private CharsetDecoder _charsetDecoder;
174            private String _charsetName;
175            private ByteBuffer _inputBuffer;
176            private CharBuffer _outputBuffer;
177            private Writer _writer;
178    
179    }