blob: fc6f6bd7abedbdc9d08f40c4e47dabbc84406c78 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.lib.json;
2
3import java.lang.reflect.*;
4import java.util.*;
5
6public class ObjectHandler extends Handler {
7 @SuppressWarnings("rawtypes")
8 final Class rawClass;
9 final Field fields[];
10 final Type types[];
11 final Object defaults[];
12 final Field extra;
13
14 ObjectHandler(JSONCodec codec, Class<?> c) throws Exception {
15 rawClass = c;
16 fields = c.getFields();
17
18 // Sort the fields so the output is canonical
19 Arrays.sort(fields, new Comparator<Field>() {
20 public int compare(Field o1, Field o2) {
21 return o1.getName().compareTo(o2.getName());
22 }
23 });
24
25 types = new Type[fields.length];
26 defaults = new Object[fields.length];
27
28 Field x = null;
29 for (int i = 0; i < fields.length; i++) {
30 if (fields[i].getName().equals("__extra"))
31 x = fields[i];
32 types[i] = fields[i].getGenericType();
33 }
34 if (x != null && Map.class.isAssignableFrom(x.getType()))
35 extra = x;
36 else
37 extra = null;
38
39 try {
40 Object template = c.newInstance();
41
42 for (int i = 0; i < fields.length; i++) {
43 defaults[i] = fields[i].get(template);
44 }
45 } catch (Exception e) {
46 // Ignore
47 }
48 }
49
50 @Override void encode(Encoder app, Object object, Map<Object, Type> visited) throws Exception {
51 app.append("{");
52 String del = "";
53 for (int i = 0; i < fields.length; i++) {
54 if (fields[i].getName().startsWith("__"))
55 continue;
56
57 Object value = fields[i].get(object);
58 if (!app.writeDefaults) {
59 if (value == defaults[i])
60 continue;
61
62 if (value != null && value.equals(defaults[i]))
63 continue;
64 }
65
66 app.append(del);
67 StringHandler.string(app, fields[i].getName());
68 app.append(":");
69 app.encode(value, types[i], visited);
70 del = ",";
71 }
72 app.append("}");
73 }
74
75 @SuppressWarnings("unchecked") @Override Object decodeObject(Decoder r) throws Exception {
76 assert r.current() == '{';
77 Object targetObject = rawClass.newInstance();
78
79 int c = r.next();
80 while (JSONCodec.START_CHARACTERS.indexOf(c) >= 0) {
81
82 // Get key
83 String key = r.codec.parseString(r);
84
85 // Get separator
86 c = r.skipWs();
87 if (c != ':')
88 throw new IllegalArgumentException("Expected ':' but got " + (char) c);
89
90 c = r.next();
91
92 // Get value
93
94 Field f = getField(key);
95 if (f != null) {
96 // We have a field and thus a type
97 Object value = r.codec.decode(f.getGenericType(), r);
98 f.set(targetObject, value);
99 } else {
100 // No field, but may extra is defined
101 if (extra == null) {
102 if (r.strict)
103 throw new IllegalArgumentException("No such field " + key);
104 Object value = r.codec.decode(null, r);
105 r.getExtra().put(rawClass.getName() + "." + key, value);
106 } else {
107
108 Map<String, Object> map = (Map<String, Object>) extra.get(targetObject);
109 if (map == null) {
110 map = new LinkedHashMap<String, Object>();
111 extra.set(targetObject, map);
112 }
113 Object value = r.codec.decode(null, r);
114 map.put(key, value);
115 }
116 }
117
118 c = r.skipWs();
119
120 if (c == '}')
121 break;
122
123 if (c == ',') {
124 c = r.next();
125 continue;
126 }
127
128 throw new IllegalArgumentException(
129 "Invalid character in parsing object, expected } or , but found " + (char) c);
130 }
131 assert r.current() == '}';
132 r.read(); // skip closing
133 return targetObject;
134 }
135
136 private Field getField(String key) {
137 for (int i = 0; i < fields.length; i++) {
138 int n = key.compareTo(fields[i].getName());
139 if (n == 0)
140 return fields[i];
141 if (n < 0)
142 return null;
143 }
144 return null;
145 }
146
147}