001 // Copyright 2007-2008 Waterken Inc. under the terms of the MIT X license
002 // found at http://www.opensource.org/licenses/mit-license.html
003 package org.waterken.syntax.json;
004
005 import static org.ref_send.promise.Eventual.ref;
006 import static org.waterken.syntax.WrongToken.require;
007
008 import java.io.IOException;
009 import java.io.Reader;
010 import java.lang.annotation.Annotation;
011 import java.lang.reflect.Array;
012 import java.lang.reflect.Constructor;
013 import java.lang.reflect.GenericArrayType;
014 import java.lang.reflect.Type;
015 import java.lang.reflect.TypeVariable;
016 import java.math.BigDecimal;
017 import java.math.BigInteger;
018
019 import org.joe_e.array.BooleanArray;
020 import org.joe_e.array.ByteArray;
021 import org.joe_e.array.CharArray;
022 import org.joe_e.array.ConstArray;
023 import org.joe_e.array.DoubleArray;
024 import org.joe_e.array.FloatArray;
025 import org.joe_e.array.ImmutableArray;
026 import org.joe_e.array.IntArray;
027 import org.joe_e.array.LongArray;
028 import org.joe_e.array.PowerlessArray;
029 import org.joe_e.array.ShortArray;
030 import org.joe_e.reflect.Reflection;
031 import org.ref_send.name;
032 import org.ref_send.promise.Eventual;
033 import org.ref_send.promise.Promise;
034 import org.ref_send.scope.Layout;
035 import org.ref_send.scope.Scope;
036 import org.ref_send.scope.Unavailable;
037 import org.ref_send.type.Typedef;
038 import org.waterken.syntax.BadSyntax;
039 import org.waterken.syntax.Importer;
040 import org.waterken.syntax.Syntax;
041 import org.waterken.syntax.WrongToken;
042
043 /**
044 * Deserializes a JSON text stream.
045 */
046 public final class
047 JSONParser {
048
049 private final String base;
050 private final Importer connect;
051 private final ClassLoader code;
052 private final JSONLexer lexer;
053
054 /**
055 * Constructs an instance.
056 * @param base base URL
057 * @param connect reference importer
058 * @param code class loader
059 * @param text UTF-8 JSON text stream
060 */
061 public
062 JSONParser(final String base, final Importer connect,
063 final ClassLoader code, final Reader text) {
064 this.base = base;
065 this.connect = connect;
066 this.code = code;
067 lexer = new JSONLexer(text);
068 }
069
070 /**
071 * Deserializes an array of objects.
072 * @param parameters each expected type
073 * @return parsed argument list
074 * @throws IOException any I/O error
075 * @throws BadSyntax invalid JSON text
076 */
077 public ConstArray<?>
078 readTuple(final Type... parameters) throws IOException, BadSyntax {
079 try {
080 // Check for varargs.
081 final Type vparam;
082 final Class<?> vclass;
083 if (0 == parameters.length) {
084 vparam = vclass = null;
085 } else {
086 final Type last = parameters[parameters.length - 1];
087 if (last instanceof GenericArrayType) {
088 vparam = ((GenericArrayType)last).getGenericComponentType();
089 vclass = Typedef.raw(vparam);
090 } else if (last instanceof Class && ((Class<?>)last).isArray()){
091 vparam = vclass = ((Class<?>)last).getComponentType();
092 } else {
093 vparam = vclass = null;
094 }
095 }
096
097 // Parse the received data.
098 final ConstArray.Builder<Object> r =
099 ConstArray.builder(parameters.length);
100 require("[", lexer.next());
101 if (!"]".equals(lexer.next())) {
102 while (true) {
103 if (null != vclass && r.length() == parameters.length - 1) {
104 Object vargs = Array.newInstance(vclass, 1);
105 for (int j = 0; true; ++j) {
106 Array.set(vargs, j, parseValue(vparam));
107 if ("]".equals(lexer.next())) { break; }
108 require(",", lexer.getHead());
109 lexer.next();
110 System.arraycopy(vargs, 0,
111 vargs = Array.newInstance(vclass, j + 2), 0,
112 j + 1);
113 }
114 r.append(vargs);
115 break;
116 }
117 if (r.length() < parameters.length) {
118 r.append(parseValue(parameters[r.length()]));
119 } else {
120 // Discard extra arguments.
121 parseValue(Object.class);
122 }
123 if ("]".equals(lexer.next())) { break; }
124 require(",", lexer.getHead());
125 lexer.next();
126 }
127 }
128 lexer.close();
129
130 // Fill out any remaining parameters with default values.
131 while (r.length() < parameters.length) {
132 if (null != vclass && r.length() == parameters.length - 1) {
133 r.append(Array.newInstance(vclass, 0));
134 } else {
135 r.append(Syntax.defaultValue(parameters[r.length()]));
136 }
137 }
138 return r.snapshot();
139 } catch (final IOException e) {
140 try { lexer.close(); } catch (final Exception e2) {}
141 throw e;
142 } catch (final Exception e) {
143 try { lexer.close(); } catch (final Exception e2) {}
144 throw new BadSyntax(base, PowerlessArray.array(
145 IntArray.array(lexer.getLine(), lexer.getColumn())), e);
146 }
147 }
148
149 /**
150 * Deserializes an object.
151 * @param type expected type
152 * @return parsed object
153 * @throws IOException any I/O error
154 * @throws BadSyntax invalid JSON text
155 */
156 public Object
157 readValue(final Type type) throws IOException, BadSyntax {
158 try {
159 lexer.next();
160 final Object r = parseValue(type);
161 lexer.close();
162 return r;
163 } catch (final IOException e) {
164 try { lexer.close(); } catch (final Exception e2) {}
165 throw e;
166 } catch (final Exception e) {
167 try { lexer.close(); } catch (final Exception e2) {}
168 throw new BadSyntax(base, PowerlessArray.array(
169 IntArray.array(lexer.getLine(), lexer.getColumn())), e);
170 }
171 }
172
173 static private final TypeVariable<?> R = Typedef.var(Promise.class, "T");
174 static private final TypeVariable<?> T = Typedef.var(Iterable.class, "T");
175
176 private Object
177 parseValue(final Type required) throws Exception {
178 final String token = lexer.getHead();
179 return "[".equals(token) ? parseArray(required)
180 : "{".equals(token) ? parseObject(required)
181 : token.startsWith("\"") ? parseString(required)
182 : parseKeyword(required);
183 }
184
185 private Object
186 parseKeyword(final Type required) throws Exception {
187 final String token = lexer.getHead();
188 final Type promised = Typedef.value(R, required);
189 final Type expected = null != promised ? promised : required;
190 final Object value;
191 if ("null".equals(token)) {
192 value = null;
193 } else if ("false".equals(token)) {
194 value = Boolean.FALSE; // intern value
195 } else if ("true".equals(token)) {
196 value = Boolean.TRUE; // intern value
197 } else if (int.class == expected || Integer.class == expected) {
198 value = Integer.valueOf(Integer.parseInt(token)); // intern value
199 } else if (long.class == expected || Long.class == expected) {
200 value = Long.valueOf(Long.parseLong(token)); // intern value
201 } else if (byte.class == expected || Byte.class == expected) {
202 value = Byte.valueOf(Byte.parseByte(token)); // intern value
203 } else if (short.class == expected || Short.class == expected) {
204 value = Short.valueOf(Short.parseShort(token)); // intern value
205 } else if (BigInteger.class == expected) {
206 value = new BigInteger(token);
207 } else if (double.class == expected || Double.class == expected) {
208 value = Double.valueOf(token); // accepts a superset of JSON
209 } else if (float.class == expected || Float.class == expected) {
210 value = Float.valueOf(token); // accepts a superset of JSON
211 } else if (BigDecimal.class == expected) {
212 value = new BigDecimal(token);
213 } else if (token.indexOf('.') != -1 ||
214 token.indexOf('e') != -1 || token.indexOf('E') != -1) {
215 value = Double.valueOf(token); // accepts a superset of JSON
216 } else {
217 final BigInteger x = new BigInteger(token);
218 int bits = x.bitLength();
219 if (x.signum() > 0) { ++bits; }
220 if (bits <= Integer.SIZE) {
221 value = Integer.valueOf(x.intValue()); // intern value
222 } else if (bits <= Long.SIZE) {
223 value = Long.valueOf(x.longValue()); // intern value
224 } else {
225 value = x;
226 }
227 }
228 return null != promised ? ref(value) : value;
229 }
230
231 private Object
232 parseString(final Type required) throws Exception {
233 final String text = string(lexer.getHead());
234 final Type promised = Typedef.value(R, required);
235 final Type expected = null != promised ? promised : required;
236 final Object value;
237 if (char.class == expected || Character.class == expected) {
238 if (1 != text.length()) { throw new WrongToken("\""); }
239 value = Character.valueOf(text.charAt(0)); // intern value
240 } else {
241 value = text;
242 }
243 return null != promised ? ref(value) : value;
244 }
245
246 private Object
247 parseArray(final Type required) throws Exception {
248 require("[", lexer.getHead());
249 final Type promised = Typedef.value(R, required);
250 final Type expected = null != promised ? promised : required;
251 final Class<?> rawExpected = Typedef.raw(expected);
252 final @SuppressWarnings({ "rawtypes", "unchecked" })
253 ConstArray.Builder<Object> builder = (ConstArray.Builder)(
254 BooleanArray.class == rawExpected ? BooleanArray.builder() :
255 CharArray.class == rawExpected ? CharArray.builder() :
256 ByteArray.class == rawExpected ? ByteArray.builder() :
257 ShortArray.class == rawExpected ? ShortArray.builder() :
258 IntArray.class == rawExpected ? IntArray.builder() :
259 LongArray.class == rawExpected ? LongArray.builder() :
260 FloatArray.class == rawExpected ? FloatArray.builder() :
261 DoubleArray.class == rawExpected ? DoubleArray.builder() :
262 PowerlessArray.class.isAssignableFrom(rawExpected) ?
263 PowerlessArray.builder() :
264 ImmutableArray.class.isAssignableFrom(rawExpected) ?
265 ImmutableArray.builder() :
266 ConstArray.builder());
267 if (!"]".equals(lexer.next())) {
268 final Type elementT = Typedef.value(T, expected);
269 while (true) {
270 builder.append(parseValue(elementT));
271 if ("]".equals(lexer.next())) { break; }
272 require(",", lexer.getHead());
273 lexer.next();
274 }
275 }
276 final ConstArray<?> value = builder.snapshot();
277 return null != promised ? ref(value) : value;
278 }
279
280 private Object
281 parseObject(final Type required) throws Exception {
282 require("{", lexer.getHead());
283 lexer.next(); // skip past the opening curly
284
285 /*
286 * SECURITY CLAIM: The held ClassLoader will only be used to construct
287 * pass-by-construction Joe-E objects. Although any class may be loaded
288 * via the ClassLoader, the class will only be used to access a Joe-E
289 * constructor. If the loaded class is not a Joe-E class, no Joe-E
290 * constructor will be found and deserialization will fail.
291 *
292 * A "pass-by-construction" Joe-E class is a Joe-E class which either:
293 * - declares a public constructor with the deserializer annotation
294 * - is a subclass of Throwable and declares a public no-arg constructor
295 */
296
297 final PowerlessArray.Builder<String> names = PowerlessArray.builder(1);
298 final ConstArray.Builder<Object> values = ConstArray.builder(1);
299
300 // load any explicit type information
301 final Type promised = Typedef.value(R, required);
302 final Type expected = null != promised ? promised : required;
303 Class<?>[] types = new Class<?>[] { Typedef.raw(expected) };
304 if (null != code && "\"class\"".equals(lexer.getHead())) {
305 require(":", lexer.next());
306 lexer.next();
307 final Object value = parseValue(PowerlessArray.class);
308 names.append("class");
309 values.append(value);
310 if (value instanceof PowerlessArray<?>) {
311 for (final Object typename : (PowerlessArray<?>)value) {
312 try {
313 final Class<?> type = JSON.load(code, (String)typename);
314 boolean implied = false;
315 for (int i = 0; i != types.length; i += 1) {
316 if (type.isAssignableFrom(types[i])) {
317 implied = true;
318 break;
319 }
320 if (types[i].isAssignableFrom(type)) {
321 types[i] = type;
322 implied = true;
323 break;
324 }
325 }
326 if (!implied) {
327 final int n = types.length;
328 System.arraycopy(
329 types, 0, types = new Class<?>[n + 1], 0, n);
330 types[n] = type;
331 }
332 } catch (final ClassCastException e) {
333 // Skip non-string typename in source.
334 } catch (final ClassNotFoundException e) {
335 // Skip unknown type.
336 }
337 }
338 }
339 if (",".equals(lexer.next())) { lexer.next(); }
340 }
341 final Constructor<?> make; {
342 Constructor<?> c = null;
343 for (final Class<?> type : types) {
344 c = Syntax.deserializer(type);
345 if (null != c) { break; }
346 }
347 make = c;
348 }
349 final String[] namev;
350 final Type[] paramv;
351 final Object[] argv;
352 final boolean[] donev;
353 if (null == make) {
354 namev = new String[] {};
355 paramv = new Type[] {};
356 argv = new Object[] {};
357 donev = new boolean[] {};
358 } else {
359 paramv = make.getGenericParameterTypes(); {
360 int i = 0;
361 for (final Type p : paramv) {
362 paramv[i++] = Typedef.bound(p, expected);
363 }
364 }
365 namev = new String[paramv.length]; {
366 int i = 0;
367 for (final Annotation[] as : make.getParameterAnnotations()) {
368 for (final Annotation a : as) {
369 if (a instanceof name) {
370 namev[i] = ((name)a).value();
371 break;
372 }
373 }
374 ++i;
375 }
376 }
377 argv = new Object[paramv.length];
378 donev = new boolean[paramv.length];
379 }
380 if (!"}".equals(lexer.getHead())) {
381 while (true) {
382 final String name = string(lexer.getHead());
383 require(":", lexer.next());
384 lexer.next();
385 int slot = namev.length;
386 while (0 != slot-- && !name.equals(namev[slot])) {}
387 final Type type = -1 != slot ?
388 paramv[slot] :
389 "=".equals(name) ?
390 required :
391 "!".equals(name) ?
392 Exception.class :
393 Object.class;
394 final Object value = parseValue(type);
395 if (-1 != slot) {
396 if (donev[slot]) { throw new Unavailable(name); }
397 donev[slot] = true;
398 argv[slot] = value;
399 } else {
400 names.append(name);
401 values.append(value);
402 }
403 if ("}".equals(lexer.next())) { break; }
404 require(",", lexer.getHead());
405 lexer.next();
406 }
407 }
408
409 final PowerlessArray<String> undeclared = names.snapshot();
410 final int rejected = find("!", undeclared);
411 if (-1 != rejected) {
412 final Object reason = values.snapshot().get(rejected);
413 final Exception e = reason instanceof Exception ?
414 (Exception)reason :
415 reason instanceof String ?
416 new RuntimeException((String)reason) :
417 new RuntimeException();
418 try {
419 return Eventual.cast(Typedef.raw(required), Eventual.reject(e));
420 } catch (final ClassCastException ignored) {
421 throw e;
422 }
423 }
424
425 final int remote = find("@", undeclared);
426 if (-1 != remote) {
427 final String href = (String)values.snapshot().get(remote);
428 final Object r = null != promised ?
429 connect.apply(href, base, required) :
430 connect.apply(href, base, types);
431 return null != promised ? ref(r) : r;
432 }
433
434 final int replacement = find("=", undeclared);
435 if (-1 != replacement) { return values.snapshot().get(replacement); }
436
437 final Object r;
438 if (null == make) {
439 r = Scope.make(new Layout<Object>(undeclared), values.snapshot());
440 } else {
441 for (int i = donev.length; 0 != i--;) {
442 if (!donev[i]) {
443 donev[i] = true;
444 argv[i] = Syntax.defaultValue(paramv[i]);
445 }
446 }
447 r = Reflection.construct(make, argv);
448 if (r instanceof Throwable) {
449 Reflection.clearStackTrace((Throwable)r);
450 }
451 }
452 return null != promised ? ref(r) : r;
453 }
454
455 static private int
456 find(final String name, final PowerlessArray<String> names) {
457 int r = names.length();
458 while (0 != r-- && !name.equals(names.get(r))) {}
459 return r;
460 }
461
462 static private String
463 string(final String token) throws Exception {
464 if (!token.startsWith("\"")) { throw new WrongToken("\""); }
465 return token.substring(1, token.length() - 1);
466 }
467
468 /*
469 * This implementation provides few security properties. In particular,
470 * clients of this class should assume:
471 * - repeated deserialization of a JSON text may produce different Java
472 * object graphs
473 * - deserialized application objects can detect the number of times they
474 * have been deserialized
475 * - via the Importer, application objects may gain access to any reference
476 * they are able to name
477 * - an object of any pass-by-construction Joe-E class may be constructed
478 * from whole cloth
479 */
480 }