package com.koushikdutta.async; 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; import junit.framework.Assert; import com.koushikdutta.async.callback.DataCallback; 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(b); 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.assertNull(mCallback); Assert.assertTrue(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 found = false; ByteBufferList cb = new ByteBufferList(); ByteBuffer lastBuffer = null; do { if (lastBuffer != bb.peek()) { lastBuffer.mark(); if (lastBuffer != null) { lastBuffer.reset(); cb.add(lastBuffer); } lastBuffer = bb.peek(); } } while (bb.remaining() > 0 && (found = (bb.get() != uw.value))); int mark = lastBuffer.position(); lastBuffer.reset(); ByteBuffer add = ByteBuffer.wrap(lastBuffer.array(), lastBuffer.arrayOffset() + lastBuffer.position(), mark - lastBuffer.position()); cb.add(add); lastBuffer.position(mark); if (!found) { if (uw.callback != null) uw.callback.onDataAvailable(emitter, cb); 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.fail(); } // System.out.println("Parsed: " + mArgs.get(0)); mWaiting.remove(); } } catch (Exception ex) { Assert.assertTrue(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) { 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; 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"; Assert.fail("AndroidAsync: tap callback could not be found. Proguard? Use this in your proguard config:\n" + fail); return null; } }