package org.torrent.basnark.bencode;
import java.io.*;
import java.math.BigDecimal;
import java.util.Stack;
/**
* Date: 21.09.2009
* Time: 21:08:46 (Moscow Standard Time)
*
* @author Vlad Vinichenko (akerigan@gmail.com)
*/
public class BEncodedStreamReader {
public static final int DICTIONARY_START = 0;
public static final int DICTIONARY_END = 1;
public static final int LIST_START = 10;
public static final int LIST_END = 11;
public static final int INTEGER = 20;
public static final int BYTE_ARRAY = 30;
private InputStream in;
private int current = -1;
private Stack<Integer> events = new Stack<Integer>();
private ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
private BigDecimal parsedNumber;
private byte[] parsedByteArray;
public BEncodedStreamReader(InputStream in) {
if (in == null) {
throw new IllegalStateException("Input stream is null");
}
this.in = in;
}
public BigDecimal getNumber() {
return parsedNumber;
}
public byte[] getByteArray() {
return parsedByteArray;
}
public boolean hasNext() throws IOException {
return current != -1 || (current = in.read()) != -1;
}
public int next() throws IOException {
if (current == -1) {
current = in.read();
}
if (current == -1) {
if (events.size() > 0) {
throw new EOFException("Unexpected end of stream");
}
}
if (current == 'd') {
current = -1;
events.push(DICTIONARY_START);
return DICTIONARY_START;
} else if (current == 'l') {
current = -1;
events.push(LIST_START);
return LIST_START;
} else if (current == 'e') {
current = -1;
if (events.size() == 0) {
throw new EOFException("Unexpected end of data");
}
int event = events.pop();
switch (event) {
case DICTIONARY_START:
return DICTIONARY_END;
case LIST_START:
return LIST_END;
default:
throw new EOFException("Unexpected end of data");
}
} else if (current == 'i') {
current = -1;
parsedNumber = readNumber('e');
return INTEGER;
} else if (current >= '0' && current <= '9') {
parsedByteArray = readBytesArray();
return BYTE_ARRAY;
} else {
throw new InvalidObjectException("Unparsable token: " + current);
}
}
private byte[] readBytesArray() throws IOException {
BigDecimal count = readNumber(':');
return readCount(count.intValue());
}
private BigDecimal readNumber(char end) throws IOException {
BigDecimal result = new BigDecimal(new String(readUntil(end)));
current = in.read();
return result;
}
private byte[] readUntil(char end) throws IOException {
byteBuffer.reset();
if (current == -1) {
current = in.read();
}
while (current != -1 && current != end) {
byteBuffer.write(current);
current = in.read();
}
if (current != end) {
throw new EOFException("Unexpected end of stream");
}
return byteBuffer.toByteArray();
}
private byte[] readCount(int count) throws IOException {
byteBuffer.reset();
if (current == -1) {
current = in.read();
}
int i;
for (i = 0; current != -1 && i != count; ++i) {
byteBuffer.write(current);
current = in.read();
}
if (i != count) {
throw new EOFException("Unexpected end of stream");
}
return byteBuffer.toByteArray();
}
}