1 package com.android.test.hierarchyviewer;
2 
3 import java.nio.ByteBuffer;
4 import java.nio.charset.Charset;
5 import java.util.HashMap;
6 import java.util.Map;
7 
8 public 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 
Decoder(byte[] buf)26     public Decoder(byte[] buf) {
27         this(ByteBuffer.wrap(buf));
28     }
29 
Decoder(ByteBuffer buf)30     public Decoder(ByteBuffer buf) {
31         mBuf = buf;
32     }
33 
hasRemaining()34     public boolean hasRemaining() {
35         return mBuf.hasRemaining();
36     }
37 
readObject()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 
readString()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 
readMap()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 {
DecoderException(byte seen, int pos)93         public DecoderException(byte seen, int pos) {
94             super(String.format("Unexpected byte %c seen at position %d", (char)seen, pos));
95         }
96 
DecoderException(String msg)97         public DecoderException(String msg) {
98             super(msg);
99         }
100     }
101 }
102