package com.koushikdutta.async;
import com.koushikdutta.async.callback.DataCallback;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedList;
public class PushParser {
private LinkedList<Object> mWaiting = new LinkedList<Object>();
static class BufferWaiter {
int length;
}
static class StringWaiter extends BufferWaiter {
}
static class UntilWaiter {
byte value;
DataCallback callback;
}
int mNeeded = 0;
public PushParser readInt() {
mNeeded += 4;
mWaiting.add(int.class);
return this;
}
public PushParser readByte() {
mNeeded += 1;
mWaiting.add(byte.class);
return this;
}
public PushParser readShort() {
mNeeded += 2;
mWaiting.add(short.class);
return this;
}
public PushParser readLong() {
mNeeded += 8;
mWaiting.add(long.class);
return this;
}
public PushParser readBuffer(int length) {
if (length != -1)
mNeeded += length;
BufferWaiter bw = new BufferWaiter();
bw.length = length;
mWaiting.add(bw);
return this;
}
public PushParser readLenBuffer() {
readInt();
BufferWaiter bw = new BufferWaiter();
bw.length = -1;
mWaiting.add(bw);
return this;
}
public PushParser readString() {
readInt();
StringWaiter bw = new StringWaiter();
bw.length = -1;
mWaiting.add(bw);
return this;
}
public PushParser until(byte b, DataCallback callback) {
UntilWaiter waiter = new UntilWaiter();
waiter.value = b;
waiter.callback = callback;
mWaiting.add(waiter);
mNeeded++;
return this;
}
public PushParser noop() {
mWaiting.add(Object.class);
return this;
}
DataEmitterReader mReader;
DataEmitter mEmitter;
public PushParser(DataEmitter s) {
mEmitter = s;
mReader = new DataEmitterReader();
mEmitter.setDataCallback(mReader);
}
private ArrayList<Object> mArgs = new ArrayList<Object>();
private TapCallback mCallback;
Exception stack() {
try {
throw new Exception();
}
catch (Exception e) {
return e;
}
}
ByteOrder order = ByteOrder.BIG_ENDIAN;
public ByteOrder order() {
return order;
}
public PushParser order(ByteOrder order) {
this.order = order;
return this;
}
public void tap(TapCallback callback) {
assert mCallback == null;
assert mWaiting.size() > 0;
mCallback = callback;
new DataCallback() {
{
onDataAvailable(mEmitter, null);
}
@Override
public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
try {
if (bb != null)
bb.order(order);
while (mWaiting.size() > 0) {
Object waiting = mWaiting.peek();
if (waiting == null)
break;
// System.out.println("Remaining: " + bb.remaining());
if (waiting == int.class) {
mArgs.add(bb.getInt());
mNeeded -= 4;
}
else if (waiting == short.class) {
mArgs.add(bb.getShort());
mNeeded -= 2;
}
else if (waiting == byte.class) {
mArgs.add(bb.get());
mNeeded -= 1;
}
else if (waiting == long.class) {
mArgs.add(bb.getLong());
mNeeded -= 8;
}
else if (waiting == Object.class) {
mArgs.add(null);
}
else if (waiting instanceof UntilWaiter) {
UntilWaiter uw = (UntilWaiter)waiting;
boolean different = true;
ByteBufferList cb = new ByteBufferList();
while (bb.size() > 0) {
ByteBuffer b = bb.remove();
b.mark();
int index = 0;
while (b.remaining() > 0 && (different = (b.get() != uw.value))) {
index++;
}
b.reset();
if (!different) {
bb.addFirst(b);
bb.get(cb, index);
break;
}
else {
cb.add(b);
}
}
if (uw.callback != null)
uw.callback.onDataAvailable(emitter, cb);
if (!different) {
mNeeded--;
}
else {
throw new Exception();
}
}
else if (waiting instanceof BufferWaiter || waiting instanceof StringWaiter) {
BufferWaiter bw = (BufferWaiter)waiting;
int length = bw.length;
if (length == -1) {
length = (Integer)mArgs.get(mArgs.size() - 1);
mArgs.remove(mArgs.size() - 1);
bw.length = length;
mNeeded += length;
}
if (bb.remaining() < length) {
// System.out.print("imminient feilure detected");
throw new Exception();
}
// e.printStackTrace();
// System.out.println("Buffer length: " + length);
byte[] bytes = null;
if (length > 0) {
bytes = new byte[length];
bb.get(bytes);
}
mNeeded -= length;
if (waiting instanceof StringWaiter)
mArgs.add(new String(bytes));
else
mArgs.add(bytes);
}
else {
assert false;
}
// System.out.println("Parsed: " + mArgs.get(0));
mWaiting.remove();
}
}
catch (Exception ex) {
assert mNeeded != 0;
// ex.printStackTrace();
mReader.read(mNeeded, this);
return;
}
try {
Object[] args = mArgs.toArray();
mArgs.clear();
TapCallback callback = mCallback;
mCallback = null;
Method method = getTap(callback);
method.invoke(callback, args);
}
catch (Exception ex) {
assert false;
ex.printStackTrace();
}
}
};
}
static Hashtable<Class, Method> mTable = new Hashtable<Class, Method>();
static Method getTap(TapCallback callback) {
Method found = mTable.get(callback.getClass());
if (found != null)
return found;
// try the proguard friendly route, take the first/only method
// in case "tap" has been renamed
Method[] candidates = callback.getClass().getDeclaredMethods();
if (candidates.length == 1)
return candidates[0];
for (Method method : callback.getClass().getMethods()) {
if ("tap".equals(method.getName())) {
mTable.put(callback.getClass(), method);
return method;
}
}
String fail =
"-keep class * extends com.koushikdutta.async.TapCallback {\n" +
" *;\n" +
"}\n";
//null != "AndroidAsync: tap callback could not be found. Proguard? Use this in your proguard config:\n" + fail;
assert false;
return null;
}
}