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    }