Mercurial > dive4elements > framework
comparison artifacts-common/src/main/java/org/dive4elements/artifacts/common/utils/JSON.java @ 472:783cc1b6b615
Moved directories to org.dive4elements
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 10:53:15 +0200 |
parents | artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java@c40729bfe06d |
children | 415df0fc4fa1 |
comparison
equal
deleted
inserted
replaced
471:1a87cb24a446 | 472:783cc1b6b615 |
---|---|
1 package de.intevation.artifacts.common.utils; | |
2 | |
3 import java.util.Map; | |
4 import java.util.LinkedHashMap; | |
5 import java.util.List; | |
6 import java.util.ArrayList; | |
7 import java.util.Iterator; | |
8 | |
9 import java.io.IOException; | |
10 import java.io.PushbackInputStream; | |
11 import java.io.InputStream; | |
12 import java.io.PrintWriter; | |
13 import java.io.ByteArrayInputStream; | |
14 import java.io.StringWriter; | |
15 | |
16 import java.nio.charset.Charset; | |
17 import java.nio.charset.UnsupportedCharsetException; | |
18 | |
19 public final class JSON | |
20 { | |
21 private JSON() { | |
22 } | |
23 | |
24 private static final boolean isDigit(int c) { | |
25 return c >= '0' && c <= '9'; | |
26 } | |
27 | |
28 public static final boolean isWhitespace(int c) { | |
29 return c == ' ' || c == '\n' || c == '\r' | |
30 || c == '\t' || c == '\f'; | |
31 } | |
32 | |
33 private static final void match(int c, int x) throws IOException { | |
34 if (c != x) { | |
35 throw new IOException( | |
36 "Expecting '" + (char)c + "' found '" + (char)x + "'"); | |
37 } | |
38 } | |
39 | |
40 private static final int eof(InputStream in) | |
41 throws IOException | |
42 { | |
43 int c = in.read(); | |
44 if (c == -1) { | |
45 throw new IOException("EOF unexpected."); | |
46 } | |
47 return c; | |
48 } | |
49 | |
50 private static final int whitespace(InputStream in) | |
51 throws IOException | |
52 { | |
53 int c; | |
54 while (isWhitespace(c = eof(in))); | |
55 return c; | |
56 } | |
57 | |
58 private static final int parseHex(String hex) throws IOException { | |
59 try { | |
60 return Integer.parseInt(hex, 16); | |
61 } | |
62 catch (NumberFormatException nfe) { | |
63 throw new IOException("'" + hex + "' is not a hex string."); | |
64 } | |
65 } | |
66 | |
67 public static final String jsonString(String string) { | |
68 StringBuilder sb = new StringBuilder(string.length()+2); | |
69 | |
70 sb.append('"'); | |
71 | |
72 for (int i = 0, N = string.length(); i < N; ++i) { | |
73 char c = string.charAt(i); | |
74 switch (c) { | |
75 case '"': sb.append("\\\""); break; | |
76 case '\t': sb.append("\\t"); break; | |
77 case '\r': sb.append("\\r"); break; | |
78 case '\n': sb.append("\\n"); break; | |
79 case '\b': sb.append("\\b"); break; | |
80 case '\f': sb.append("\\f"); break; | |
81 default: | |
82 if (c >= 128) { | |
83 sb.append("\\u"); | |
84 String hex = Integer.toHexString((int)c); | |
85 for (int j = 4-hex.length(); j > 0; --j) { | |
86 sb.append('0'); | |
87 } | |
88 sb.append(hex); | |
89 } | |
90 else { | |
91 sb.append(c); | |
92 } | |
93 } | |
94 } | |
95 | |
96 sb.append('"'); | |
97 | |
98 return sb.toString(); | |
99 } | |
100 | |
101 public static String toJSONString(Map<String, Object> map) { | |
102 StringWriter sw = new StringWriter(); | |
103 PrintWriter pw = new PrintWriter(sw); | |
104 write(pw, map); | |
105 pw.flush(); | |
106 return sw.toString(); | |
107 } | |
108 | |
109 | |
110 public static void write(PrintWriter out, Map<String, Object> map) { | |
111 writeObject(out, map); | |
112 } | |
113 | |
114 private static void writeValue(PrintWriter out, Object value) { | |
115 if (value instanceof Map) { | |
116 writeObject(out, (Map)value); | |
117 } | |
118 else if (value instanceof List) { | |
119 writeList(out, (List)value); | |
120 } | |
121 else if (value instanceof Number) { | |
122 out.print(value); | |
123 } | |
124 else if (value instanceof Boolean) { | |
125 out.print(((Boolean)value) ? "true" : "false"); | |
126 } | |
127 else if (value == null) { | |
128 out.print("null"); | |
129 } | |
130 else { | |
131 out.print(jsonString(value.toString())); | |
132 } | |
133 } | |
134 | |
135 private static void writeObject(PrintWriter out, Map map) { | |
136 | |
137 out.print('{'); | |
138 Iterator iter = map.entrySet().iterator(); | |
139 while (iter.hasNext()) { | |
140 Map.Entry entry = (Map.Entry)iter.next(); | |
141 out.print(jsonString(entry.getKey().toString())); | |
142 out.print(':'); | |
143 writeValue(out, entry.getValue()); | |
144 if (iter.hasNext()) { | |
145 out.print(','); | |
146 } | |
147 } | |
148 out.print('}'); | |
149 } | |
150 | |
151 private static void writeList(PrintWriter out, List list) { | |
152 out.print('['); | |
153 Iterator iter = list.iterator(); | |
154 while (iter.hasNext()) { | |
155 writeValue(out, iter.next()); | |
156 if (iter.hasNext()) { | |
157 out.print(','); | |
158 } | |
159 } | |
160 out.print(']'); | |
161 } | |
162 | |
163 public static Map<String, Object> parse(String in) | |
164 throws IOException | |
165 { | |
166 return parse(asInputStream(in)); | |
167 } | |
168 | |
169 private static InputStream asInputStream(String in) { | |
170 byte [] bytes; | |
171 try { | |
172 bytes = in.getBytes(Charset.forName("US-ASCII")); | |
173 } | |
174 catch (UnsupportedCharsetException uce) { | |
175 // Should not happen. | |
176 bytes = in.getBytes(); | |
177 } | |
178 return new ByteArrayInputStream(bytes); | |
179 } | |
180 | |
181 public static Map<String, Object> parse(InputStream in) | |
182 throws IOException | |
183 { | |
184 return parseObject(new PushbackInputStream(in, 1)); | |
185 } | |
186 | |
187 public static Map<String, Object> parse(PushbackInputStream in) | |
188 throws IOException | |
189 { | |
190 return parseObject(in); | |
191 } | |
192 | |
193 private static final String parseString( | |
194 PushbackInputStream in | |
195 ) | |
196 throws IOException | |
197 { | |
198 StringBuilder sb = new StringBuilder(); | |
199 | |
200 int mode = 0; | |
201 | |
202 char [] hex = new char[4]; | |
203 | |
204 match('"', eof(in)); | |
205 | |
206 OUT: for (int c = eof(in);; c = eof(in)) { | |
207 | |
208 switch (mode) { | |
209 case 0: | |
210 if (c == '"') { | |
211 break OUT; | |
212 } | |
213 if (c == '\\') { | |
214 mode = 1; | |
215 } | |
216 else { | |
217 sb.append((char)c); | |
218 } | |
219 break; | |
220 case 1: | |
221 switch (c) { | |
222 case 'u': | |
223 mode = 2; | |
224 continue; | |
225 case 'b': | |
226 sb.append('\b'); | |
227 break; | |
228 case 'f': | |
229 sb.append('\f'); | |
230 break; | |
231 case 'n': | |
232 sb.append('\n'); | |
233 break; | |
234 case 'r': | |
235 sb.append('\r'); | |
236 break; | |
237 case 't': | |
238 sb.append('\t'); | |
239 break; | |
240 default: | |
241 sb.append((char)c); | |
242 } | |
243 mode = 0; | |
244 break; | |
245 case 2: | |
246 hex[0] = (char)c; | |
247 mode = 3; | |
248 break; | |
249 case 3: | |
250 hex[1] = (char)c; | |
251 mode = 4; | |
252 break; | |
253 case 4: | |
254 hex[2] = (char)c; | |
255 mode = 5; | |
256 break; | |
257 case 5: | |
258 hex[3] = (char)c; | |
259 sb.append((char)parseHex(new String(hex))); | |
260 mode = 0; | |
261 break; | |
262 } | |
263 } | |
264 return sb.toString(); | |
265 } | |
266 | |
267 private static final Boolean parseTrue(InputStream in) | |
268 throws IOException | |
269 { | |
270 match('t', eof(in)); | |
271 match('r', eof(in)); | |
272 match('u', eof(in)); | |
273 match('e', eof(in)); | |
274 return Boolean.TRUE; | |
275 } | |
276 | |
277 private static final Boolean parseFalse(InputStream in) | |
278 throws IOException | |
279 { | |
280 match('f', eof(in)); | |
281 match('a', eof(in)); | |
282 match('l', eof(in)); | |
283 match('s', eof(in)); | |
284 match('e', eof(in)); | |
285 return Boolean.FALSE; | |
286 } | |
287 | |
288 private static final Object parseNull(InputStream in) | |
289 throws IOException | |
290 { | |
291 match('n', eof(in)); | |
292 match('u', eof(in)); | |
293 match('l', eof(in)); | |
294 match('l', eof(in)); | |
295 return null; | |
296 } | |
297 | |
298 private static final Number parseNumber(PushbackInputStream in) | |
299 throws IOException | |
300 { | |
301 StringBuilder sb = new StringBuilder(); | |
302 | |
303 boolean isInteger = true; | |
304 | |
305 int c; | |
306 OUT: for (;;) { | |
307 switch (c = eof(in)) { | |
308 case '0': case '1': case '2': case '3': case '4': | |
309 case '5': case '6': case '7': case '8': case '9': | |
310 case '-': case '+': | |
311 sb.append((char)c); | |
312 break; | |
313 case '.': case 'e': case 'E': | |
314 isInteger = false; | |
315 sb.append((char)c); | |
316 break; | |
317 default: | |
318 in.unread(c); | |
319 break OUT; | |
320 } | |
321 } | |
322 | |
323 try { | |
324 if (isInteger) { | |
325 return sb.length() > 9 | |
326 ? (Number)Long .valueOf(sb.toString()) | |
327 : (Number)Integer.valueOf(sb.toString()); | |
328 } | |
329 return (Number)Double.valueOf(sb.toString()); | |
330 } | |
331 catch (NumberFormatException nfe) { | |
332 throw new IOException("Not a number '" + sb + "'"); | |
333 } | |
334 } | |
335 | |
336 private static List<Object> parseList(PushbackInputStream in) | |
337 throws IOException | |
338 { | |
339 List<Object> list = new ArrayList<Object>(); | |
340 match('[', whitespace(in)); | |
341 int c = whitespace(in); | |
342 if (c == ']') { | |
343 return list; | |
344 } | |
345 | |
346 for (;; c = whitespace(in)) { | |
347 Object value; | |
348 in.unread(c); | |
349 switch (c) { | |
350 case '{': | |
351 value = parseObject(in); | |
352 break; | |
353 case '[': | |
354 value = parseList(in); | |
355 break; | |
356 case '"': | |
357 value = parseString(in); | |
358 break; | |
359 case 't': | |
360 value = parseTrue(in); | |
361 break; | |
362 case 'f': | |
363 value = parseFalse(in); | |
364 break; | |
365 case 'n': | |
366 value = parseNull(in); | |
367 break; | |
368 default: | |
369 value = parseNumber(in); | |
370 } | |
371 list.add(value); | |
372 | |
373 if ((c = whitespace(in)) == ']') break; | |
374 match(',', c); | |
375 } | |
376 return list; | |
377 } | |
378 | |
379 private static void parsePair( | |
380 PushbackInputStream in, | |
381 Map<String, Object> pairs | |
382 ) | |
383 throws IOException | |
384 { | |
385 in.unread(whitespace(in)); | |
386 String string = parseString(in); | |
387 match(':', whitespace(in)); | |
388 | |
389 Object value; | |
390 | |
391 int c = whitespace(in); | |
392 in.unread(c); | |
393 switch (c) { | |
394 case '{': | |
395 value = parseObject(in); | |
396 break; | |
397 case '[': | |
398 value = parseList(in); | |
399 break; | |
400 case '"': | |
401 value = parseString(in); | |
402 break; | |
403 case 't': | |
404 value = parseTrue(in); | |
405 break; | |
406 case 'f': | |
407 value = parseFalse(in); | |
408 break; | |
409 case 'n': | |
410 value = parseNull(in); | |
411 break; | |
412 default: | |
413 value = parseNumber(in); | |
414 } | |
415 pairs.put(string, value); | |
416 } | |
417 | |
418 private static Map<String, Object> parseObject(PushbackInputStream in) | |
419 throws IOException | |
420 { | |
421 Map<String, Object> pairs = new LinkedHashMap<String, Object>(); | |
422 | |
423 int c = whitespace(in); | |
424 match('{', c); | |
425 | |
426 if ((c = whitespace(in)) == '}') { | |
427 return pairs; | |
428 } | |
429 | |
430 in.unread(c); | |
431 | |
432 for (;;) { | |
433 parsePair(in, pairs); | |
434 | |
435 if ((c = whitespace(in)) == '}') { | |
436 break; | |
437 } | |
438 | |
439 if (c == '}') break; | |
440 match(',', c); | |
441 } | |
442 | |
443 return pairs; | |
444 } | |
445 } |