1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts2.json;
22
23 import java.text.CharacterIterator;
24 import java.text.StringCharacterIterator;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 /***
31 * <p>
32 * Deserializes and object from a JSON string
33 * </p>
34 */
35 class JSONReader {
36 private static final Object OBJECT_END = new Object();
37 private static final Object ARRAY_END = new Object();
38 private static final Object COLON = new Object();
39 private static final Object COMMA = new Object();
40 private static Map<Character, Character> escapes = new HashMap<Character, Character>();
41
42 static {
43 escapes.put(new Character('"'), new Character('"'));
44 escapes.put(new Character('//'), new Character('//'));
45 escapes.put(new Character('/'), new Character('/'));
46 escapes.put(new Character('b'), new Character('\b'));
47 escapes.put(new Character('f'), new Character('\f'));
48 escapes.put(new Character('n'), new Character('\n'));
49 escapes.put(new Character('r'), new Character('\r'));
50 escapes.put(new Character('t'), new Character('\t'));
51 }
52
53 private CharacterIterator it;
54 private char c;
55 private Object token;
56 private StringBuilder buf = new StringBuilder();
57
58 private char next() {
59 this.c = this.it.next();
60
61 return this.c;
62 }
63
64 private void skipWhiteSpace() {
65 while (Character.isWhitespace(this.c)) {
66 this.next();
67 }
68 }
69
70 public Object read(String string) throws JSONException {
71 this.it = new StringCharacterIterator(string);
72 this.c = this.it.first();
73
74 return this.read();
75 }
76
77 private Object read() throws JSONException {
78 Object ret = null;
79
80 this.skipWhiteSpace();
81
82 if (this.c == '"') {
83 this.next();
84 ret = this.string('"');
85 } else if (this.c == '\'') {
86 this.next();
87 ret = this.string('\'');
88 } else if (this.c == '[') {
89 this.next();
90 ret = this.array();
91 } else if (this.c == ']') {
92 ret = ARRAY_END;
93 this.next();
94 } else if (this.c == ',') {
95 ret = COMMA;
96 this.next();
97 } else if (this.c == '{') {
98 this.next();
99 ret = this.object();
100 } else if (this.c == '}') {
101 ret = OBJECT_END;
102 this.next();
103 } else if (this.c == ':') {
104 ret = COLON;
105 this.next();
106 } else if ((this.c == 't') && (this.next() == 'r') && (this.next() == 'u') && (this.next() == 'e')) {
107 ret = Boolean.TRUE;
108 this.next();
109 } else if ((this.c == 'f') && (this.next() == 'a') && (this.next() == 'l') && (this.next() == 's')
110 && (this.next() == 'e')) {
111 ret = Boolean.FALSE;
112 this.next();
113 } else if ((this.c == 'n') && (this.next() == 'u') && (this.next() == 'l') && (this.next() == 'l')) {
114 ret = null;
115 this.next();
116 } else if (Character.isDigit(this.c) || (this.c == '-')) {
117 ret = this.number();
118 } else {
119 throw buildInvalidInputException();
120 }
121
122 this.token = ret;
123
124 return ret;
125 }
126
127 @SuppressWarnings("unchecked")
128 private Map object() throws JSONException {
129 Map ret = new HashMap();
130 Object next = this.read();
131 if (next != OBJECT_END) {
132 String key = (String) next;
133 while (this.token != OBJECT_END) {
134 this.read();
135
136 if (this.token != OBJECT_END) {
137 ret.put(key, this.read());
138
139 if (this.read() == COMMA) {
140 Object name = this.read();
141
142 if (name instanceof String) {
143 key = (String) name;
144 } else
145 throw buildInvalidInputException();
146 }
147 }
148 }
149 }
150
151 return ret;
152 }
153
154 private JSONException buildInvalidInputException() {
155 return new JSONException("Input string is not well formed JSON (invalid char " + this.c + ")");
156 }
157
158 @SuppressWarnings("unchecked")
159 private List array() throws JSONException {
160 List ret = new ArrayList();
161 Object value = this.read();
162
163 while (this.token != ARRAY_END) {
164 ret.add(value);
165
166 Object read = this.read();
167 if (read == COMMA) {
168 value = this.read();
169 } else if (read != ARRAY_END) {
170 throw buildInvalidInputException();
171 }
172 }
173
174 return ret;
175 }
176
177 private Object number() {
178 this.buf.setLength(0);
179
180 if (this.c == '-') {
181 this.add();
182 }
183
184 this.addDigits();
185
186 if (this.c == '.') {
187 this.add();
188 this.addDigits();
189 }
190
191 if ((this.c == 'e') || (this.c == 'E')) {
192 this.add();
193
194 if ((this.c == '+') || (this.c == '-')) {
195 this.add();
196 }
197
198 this.addDigits();
199 }
200
201 return (this.buf.indexOf(".") >= 0) ? (Object) Double.parseDouble(this.buf.toString())
202 : (Object) Long.parseLong(this.buf.toString());
203 }
204
205 private Object string(char quote) {
206 this.buf.setLength(0);
207
208 while ((this.c != quote) && (this.c != CharacterIterator.DONE)) {
209 if (this.c == '//') {
210 this.next();
211
212 if (this.c == 'u') {
213 this.add(this.unicode());
214 } else {
215 Object value = escapes.get(new Character(this.c));
216
217 if (value != null) {
218 this.add(((Character) value).charValue());
219 }
220 }
221 } else {
222 this.add();
223 }
224 }
225
226 this.next();
227
228 return this.buf.toString();
229 }
230
231 private void add(char cc) {
232 this.buf.append(cc);
233 this.next();
234 }
235
236 private void add() {
237 this.add(this.c);
238 }
239
240 private void addDigits() {
241 while (Character.isDigit(this.c)) {
242 this.add();
243 }
244 }
245
246 private char unicode() {
247 int value = 0;
248
249 for (int i = 0; i < 4; ++i) {
250 switch (this.next()) {
251 case '0':
252 case '1':
253 case '2':
254 case '3':
255 case '4':
256 case '5':
257 case '6':
258 case '7':
259 case '8':
260 case '9':
261 value = (value << 4) + (this.c - '0');
262
263 break;
264
265 case 'a':
266 case 'b':
267 case 'c':
268 case 'd':
269 case 'e':
270 case 'f':
271 value = (value << 4) + (this.c - 'W');
272
273 break;
274
275 case 'A':
276 case 'B':
277 case 'C':
278 case 'D':
279 case 'E':
280 case 'F':
281 value = (value << 4) + (this.c - '7');
282
283 break;
284 }
285 }
286
287 return (char) value;
288 }
289 }