/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.engine.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import org.restlet.data.CharacterSet;
// [excludes gwt]
/**
* Input stream based on a reader. The implementation relies on the NIO
* {@link CharsetEncoder} class.
*
* @author Jerome Louvel
*/
public class ReaderInputStream extends InputStream {
/** The NIO byte buffer. */
private final ByteBuffer byteBuffer;
/** The NIO character buffer. */
private final CharBuffer charBuffer;
/** The character set encoder. */
private final CharsetEncoder charsetEncoder;
/** Indicates if the end of the wrapped reader has been reached. */
private volatile boolean endReached;
/** The wrapped reader. */
private final BufferedReader reader;
/**
* Constructor. Uses the {@link CharacterSet#ISO_8859_1} character set by
* default.
*
* @param reader
* The reader to wrap as an input stream.
* @throws IOException
*/
public ReaderInputStream(Reader reader) throws IOException {
this(reader, CharacterSet.ISO_8859_1);
}
/**
* Constructor.
*
* @param reader
* The reader to wrap as an input stream.
* @param characterSet
* The character set to use for encoding.
* @throws IOException
*/
public ReaderInputStream(Reader reader, CharacterSet characterSet)
throws IOException {
this.byteBuffer = ByteBuffer.allocate(1024);
this.byteBuffer.flip();
this.charBuffer = CharBuffer.allocate(1024);
this.charBuffer.flip();
this.charsetEncoder = (characterSet == null) ? CharacterSet.ISO_8859_1
.toCharset().newEncoder() : characterSet.toCharset()
.newEncoder();
this.endReached = false;
this.reader = (reader instanceof BufferedReader) ? (BufferedReader) reader
: new BufferedReader(reader, IoUtils.BUFFER_SIZE);
}
@Override
public int available() throws IOException {
return this.byteBuffer.hasRemaining() ? this.byteBuffer.remaining() : 0;
}
/**
* Closes the wrapped reader.
*/
@Override
public void close() throws IOException {
this.reader.close();
}
@Override
public int read() throws IOException {
byte[] temp = new byte[1];
return (read(temp) == -1) ? -1 : temp[0] & 0xFF;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int result = 0;
boolean iterate = true;
while (iterate) {
// Do we need to refill the byte buffer?
if (!this.byteBuffer.hasRemaining() && !this.endReached) {
// Do we need to refill the char buffer?
if (!this.charBuffer.hasRemaining()) {
this.charBuffer.clear();
int read = this.reader.read(this.charBuffer);
this.charBuffer.flip();
if (read == -1) {
this.endReached = true;
}
}
if ((len > 0) && this.charBuffer.hasRemaining()) {
// Refill the byte buffer
this.byteBuffer.clear();
this.charsetEncoder.encode(this.charBuffer,
this.byteBuffer, this.endReached);
this.byteBuffer.flip();
}
}
// Copies as much bytes as possible in the target array
int readLength = Math.min(len, this.byteBuffer.remaining());
if (readLength > 0) {
this.byteBuffer.get(b, off, readLength);
off += readLength;
len -= readLength;
result += readLength;
}
// Can we iterate again?
iterate = (len > 0)
&& (!this.endReached || this.byteBuffer.hasRemaining() || this.charBuffer
.hasRemaining());
}
if (this.endReached && (result == 0)) {
result = -1;
}
return result;
}
}