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.CharsetEncoderUtil;
018    import com.liferay.portal.kernel.util.StringPool;
019    
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.Reader;
023    
024    import java.nio.ByteBuffer;
025    import java.nio.CharBuffer;
026    import java.nio.charset.CharsetEncoder;
027    
028    /**
029     * @author Tina Tian
030     */
031    public class ReaderInputStream extends InputStream {
032    
033            public ReaderInputStream(Reader reader) {
034                    this(
035                            reader, StringPool.UTF8, _DEFAULT_INTPUT_BUFFER_SIZE,
036                            _DEFAULT_OUTPUT_BUFFER_SIZE);
037            }
038    
039            public ReaderInputStream(Reader reader, String charsetName) {
040                    this(
041                            reader, charsetName, _DEFAULT_INTPUT_BUFFER_SIZE,
042                            _DEFAULT_OUTPUT_BUFFER_SIZE);
043            }
044    
045            public ReaderInputStream(
046                    Reader reader, String charsetName, int inputBufferSize,
047                    int outputBufferSize) {
048    
049                    _reader = reader;
050                    _charsetName = charsetName;
051    
052                    if (inputBufferSize <= 0) {
053                            throw new IllegalArgumentException(
054                                    "Input buffer size " + inputBufferSize +
055                                            " must be a positive number");
056                    }
057    
058                    _inputBuffer = CharBuffer.allocate(inputBufferSize);
059    
060                    _charsetEncoder = CharsetEncoderUtil.getCharsetEncoder(charsetName);
061    
062                    _maxBytesPerChar = (int)Math.ceil(_charsetEncoder.maxBytesPerChar());
063    
064                    if (outputBufferSize < _maxBytesPerChar) {
065                            throw new IllegalArgumentException(
066                                    "Output buffer size " + outputBufferSize + " is less than " +
067                                    _maxBytesPerChar);
068                    }
069    
070                    _outputBuffer = ByteBuffer.allocate(outputBufferSize);
071    
072                    _outputBuffer.flip();
073            }
074    
075            @Override
076            public int available() {
077                    return _outputBuffer.remaining() + _inputBuffer.position();
078            }
079    
080            @Override
081            public void close() throws IOException {
082                    if (_inputBuffer != null) {
083                            _inputBuffer.clear();
084    
085                            _inputBuffer = null;
086                    }
087    
088                    if (_outputBuffer != null) {
089                            _outputBuffer.clear();
090    
091                            _outputBuffer = null;
092                    }
093    
094                    _reader.close();
095            }
096    
097            public String getEncoding() {
098                    return _charsetName;
099            }
100    
101            @Override
102            public int read() throws IOException {
103                    byte[] bytes = new byte[1];
104    
105                    int result = read(bytes, 0, 1);
106    
107                    if (result == 1) {
108                            return bytes[0];
109                    }
110                    else {
111                            return -1;
112                    }
113            }
114    
115            @Override
116            public int read(byte[] bytes) throws IOException {
117                    return read(bytes, 0, bytes.length);
118            }
119    
120            @Override
121            public int read(byte[] bytes, int offset, int length) throws IOException {
122                    if (bytes == null) {
123                            throw new NullPointerException();
124                    }
125                    else if ((offset < 0) || (length < 0) ||
126                                     (length > (bytes.length - offset))) {
127    
128                            throw new IndexOutOfBoundsException();
129                    }
130                    else if (length == 0) {
131                            return 0;
132                    }
133    
134                    int originalLength = length;
135    
136                    while (length > 0) {
137                            int blockSize = Math.min(_outputBuffer.remaining(), length);
138    
139                            if (blockSize > 0) {
140                                    _outputBuffer.get(bytes, offset, blockSize);
141    
142                                    length -= blockSize;
143                                    offset += blockSize;
144    
145                                    if (length == 0) {
146                                            break;
147                                    }
148                            }
149    
150                            int inputPosition = _inputBuffer.position();
151    
152                            int result = _reader.read(
153                                    _inputBuffer.array(), inputPosition, _inputBuffer.remaining());
154    
155                            if (result != -1) {
156                                    _inputBuffer.position(inputPosition + result);
157                            }
158    
159                            _inputBuffer.flip();
160    
161                            int inputRemaining = _inputBuffer.remaining();
162    
163                            if (inputRemaining <= 0) {
164                                    break;
165                            }
166    
167                            if ((inputRemaining * _maxBytesPerChar) < length) {
168                                    ByteBuffer byteBuffer = ByteBuffer.wrap(bytes, offset, length);
169    
170                                    _charsetEncoder.encode(_inputBuffer, byteBuffer, true);
171    
172                                    int outputRemaining = byteBuffer.remaining();
173    
174                                    offset += length - outputRemaining;
175                                    length = outputRemaining;
176                            }
177                            else {
178                                    _outputBuffer.clear();
179    
180                                    _charsetEncoder.encode(_inputBuffer, _outputBuffer, true);
181    
182                                    _outputBuffer.flip();
183                            }
184    
185                            _inputBuffer.compact();
186                    }
187    
188                    int result = originalLength - length;
189    
190                    if (result == 0) {
191                            return -1;
192                    }
193    
194                    return result;
195            }
196    
197            @Override
198            public long skip(long length) throws IOException {
199                    if (length < 0) {
200                            throw new IllegalArgumentException();
201                    }
202    
203                    long originalLength = length;
204    
205                    while (length > 0) {
206                            int blockSize = (int)Math.min(_outputBuffer.remaining(), length);
207    
208                            if (blockSize > 0) {
209                                    _outputBuffer.position(_outputBuffer.position() + blockSize);
210    
211                                    length -= blockSize;
212    
213                                    if (length == 0) {
214                                            break;
215                                    }
216                            }
217    
218                            int inputPosition = _inputBuffer.position();
219    
220                            int result = _reader.read(
221                                    _inputBuffer.array(), inputPosition, _inputBuffer.remaining());
222    
223                            if (result != -1) {
224                                    _inputBuffer.position(inputPosition + result);
225                            }
226    
227                            _inputBuffer.flip();
228    
229                            if (_inputBuffer.remaining() <= 0) {
230                                    break;
231                            }
232    
233                            _outputBuffer.clear();
234    
235                            _charsetEncoder.encode(_inputBuffer, _outputBuffer, true);
236    
237                            _outputBuffer.flip();
238    
239                            _inputBuffer.compact();
240                    }
241    
242                    return originalLength - length;
243            }
244    
245            private static final int _DEFAULT_INTPUT_BUFFER_SIZE = 128;
246    
247            private static final int _DEFAULT_OUTPUT_BUFFER_SIZE = 1024;
248    
249            private CharsetEncoder _charsetEncoder;
250            private String _charsetName;
251            private CharBuffer _inputBuffer;
252            private int _maxBytesPerChar;
253            private ByteBuffer _outputBuffer;
254            private Reader _reader;
255    
256    }