1package com.android.test.hierarchyviewer;
2
3import java.nio.ByteBuffer;
4import java.nio.charset.Charset;
5import java.util.HashMap;
6import java.util.Map;
7
8public class Decoder {
9    // Prefixes for simple primitives. These match the JNI definitions.
10    public static final byte SIG_BOOLEAN = 'Z';
11    public static final byte SIG_BYTE = 'B';
12    public static final byte SIG_SHORT = 'S';
13    public static final byte SIG_INT = 'I';
14    public static final byte SIG_LONG = 'J';
15    public static final byte SIG_FLOAT = 'F';
16    public static final byte SIG_DOUBLE = 'D';
17
18    // Prefixes for some commonly used objects
19    public static final byte SIG_STRING = 'R';
20
21    public static final byte SIG_MAP = 'M'; // a map with an short key
22    public static final short SIG_END_MAP = 0;
23
24    private final ByteBuffer mBuf;
25
26    public Decoder(byte[] buf) {
27        this(ByteBuffer.wrap(buf));
28    }
29
30    public Decoder(ByteBuffer buf) {
31        mBuf = buf;
32    }
33
34    public boolean hasRemaining() {
35        return mBuf.hasRemaining();
36    }
37
38    public Object readObject() {
39        byte sig = mBuf.get();
40
41        switch (sig) {
42            case SIG_BOOLEAN:
43                return mBuf.get() == 0 ? Boolean.FALSE : Boolean.TRUE;
44            case SIG_BYTE:
45                return mBuf.get();
46            case SIG_SHORT:
47                return mBuf.getShort();
48            case SIG_INT:
49                return mBuf.getInt();
50            case SIG_LONG:
51                return mBuf.getLong();
52            case SIG_FLOAT:
53                return mBuf.getFloat();
54            case SIG_DOUBLE:
55                return mBuf.getDouble();
56            case SIG_STRING:
57                return readString();
58            case SIG_MAP:
59                return readMap();
60            default:
61                throw new DecoderException(sig, mBuf.position() - 1);
62        }
63    }
64
65    private String readString() {
66        short len = mBuf.getShort();
67        byte[] b = new byte[len];
68        mBuf.get(b, 0, len);
69        return new String(b, Charset.forName("utf-8"));
70    }
71
72    private Map<Short, Object> readMap() {
73        Map<Short, Object> m = new HashMap<Short, Object>();
74
75        while (true) {
76            Object o = readObject();
77            if (!(o instanceof Short)) {
78                throw new DecoderException("Expected short key, got " + o.getClass());
79            }
80
81            Short key = (Short)o;
82            if (key == SIG_END_MAP) {
83                break;
84            }
85
86            m.put(key, readObject());
87        }
88
89        return m;
90    }
91
92    public static class DecoderException extends RuntimeException {
93        public DecoderException(byte seen, int pos) {
94            super(String.format("Unexpected byte %c seen at position %d", (char)seen, pos));
95        }
96
97        public DecoderException(String msg) {
98            super(msg);
99        }
100    }
101}
102