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 }