// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.opendata.core.io; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; /** * An InputStreamReader that only consumes as many bytes as is necessary. * It does not do any read-ahead. From http://stackoverflow.com/q/2631507/2257172 */ public class InputStreamReaderUnbuffered extends Reader { private final CharsetDecoder charsetDecoder; private final InputStream inputStream; private final ByteBuffer byteBuffer = ByteBuffer.allocate(1); public InputStreamReaderUnbuffered(InputStream in, Charset charset) { inputStream = in; charsetDecoder = charset.newDecoder(); } @Override public int read() throws IOException { boolean middleOfReading = false; while (true) { int b = inputStream.read(); if (b == -1) { if (middleOfReading) throw new IOException( "Unexpected end of stream, byte truncated"); return -1; } byteBuffer.clear(); byteBuffer.put((byte) b); byteBuffer.flip(); CharBuffer charBuffer = charsetDecoder.decode(byteBuffer); // although this is theoretically possible this would violate the // unbuffered nature of this class so we throw an exception if (charBuffer.length() > 1) throw new IOException( "Decoded multiple characters from one byte!"); if (charBuffer.length() == 1) return charBuffer.get(); middleOfReading = true; } } public int read(char[] cbuf, int off, int len) throws IOException { for (int i = 0; i < len; i++) { int ch = read(); if (ch == -1) return i == 0 ? -1 : i; cbuf[i] = (char) ch; } return len; } public void close() throws IOException { inputStream.close(); } }