altos/lisp: Fix uninitialized values in ao_lisp_make_const
[fw/altos] / altoslib / AltosJson.java
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package org.altusmetrum.altoslib_11;
20
21 import java.io.*;
22 import java.util.*;
23 import java.text.*;
24 import java.lang.*;
25 import java.lang.reflect.*;
26
27 class JsonUtil {
28         StringBuffer quote(StringBuffer result, String a) {
29                 result.append("\"");
30                 for (int i = 0; i < a.length(); i++) {
31                         char c = a.charAt(i);
32
33                         switch (c) {
34                         case '"':
35                         case '\\':
36                                 result.append('\\').append(c);
37                                 break;
38                         case '\n':
39                                 result.append("\\n");
40                                 break;
41                         default:
42                                 result.append(c);
43                                 break;
44                         }
45                 }
46                 result.append("\"");
47                 return result;
48         }
49
50         StringBuffer append(StringBuffer result, AltosJson value, int indent, boolean pretty) {
51                 value.append(result, indent, pretty);
52                 return result;
53         }
54
55         StringBuffer append(StringBuffer result, String string) {
56                 result.append(string);
57                 return result;
58         }
59
60         StringBuffer indent(StringBuffer result, int indent) {
61                 result.append("\n");
62                 for (int i = 0; i < indent; i++)
63                         result.append("\t");
64                 return result;
65         }
66         static NumberFormat get_nf_json() {
67                 DecimalFormat nf = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ROOT);
68                 nf.setParseIntegerOnly(false);
69                 nf.setGroupingUsed(false);
70                 nf.setMaximumFractionDigits(17);
71                 nf.setMinimumFractionDigits(0);
72                 nf.setMinimumIntegerDigits(1);
73                 nf.setDecimalSeparatorAlwaysShown(false);
74                 return nf;
75         }
76
77         static NumberFormat nf_json = get_nf_json();
78 }
79
80 class JsonHash extends JsonUtil {
81         Hashtable<String,AltosJson> hash;
82
83         void append_hash(StringBuffer result, int indent, boolean pretty) {
84                 boolean         first = true;
85
86                 result.append("{");
87
88                 ArrayList<String> key_list = new ArrayList<String>(hash.keySet());
89
90                 Collections.sort(key_list, new Comparator<String>() {
91                                 @Override
92                                 public int compare(String a, String b) { return a.compareTo(b); }
93                         });
94
95                 for (String key : key_list) {
96                         AltosJson       value = hash.get(key);
97
98                         if (!first)
99                                 result.append(",");
100                         first = false;
101                         if (pretty)
102                                 indent(result, indent+1);
103                         quote(result, key);
104                         append(result, ": ");
105                         append(result, value, indent+1, pretty);
106                 }
107                 if (pretty)
108                         indent(result, indent);
109                 append(result, "}");
110         }
111
112         void put(String key, AltosJson value) {
113                 hash.put(key, value);
114         }
115
116         AltosJson get(String key) {
117                 return hash.get(key);
118         }
119
120         JsonHash() {
121                 hash = new Hashtable<String,AltosJson>();
122         }
123 }
124
125 class JsonArray extends JsonUtil {
126         ArrayList<AltosJson> array;
127
128         void append_array(StringBuffer result, int indent, boolean pretty) {
129                 boolean first = true;
130
131                 append(result, "[");
132                 for (int i = 0; i < array.size(); i++) {
133                         AltosJson       value = array.get(i);
134
135                         if (!first)
136                                 append(result, ",");
137                         first = false;
138                         if (pretty)
139                                 indent(result, indent+1);
140                         append(result, value, indent+1, pretty);
141                 }
142                 if (pretty)
143                         indent(result, indent);
144                 append(result, "]");
145         }
146
147         void put(int index, AltosJson value) {
148                 if (index >= array.size())
149                         array.add(index, value);
150                 else
151                         array.set(index, value);
152         }
153
154         AltosJson get(int index) {
155                 if (index < 0 || index > array.size())
156                         return null;
157                 return array.get(index);
158         }
159
160         int size() {
161                 return array.size();
162         }
163
164         JsonArray() {
165                 array = new ArrayList<AltosJson>();
166         }
167 }
168
169 class JsonToken {
170         double  dval;
171         long    lval;
172         String  sval;
173         boolean bval;
174         int     token;
175
176         static final int _string = 0;
177         static final int _double = 1;
178         static final int _long = 2;
179         static final int _boolean = 3;
180         static final int _oc = 4;
181         static final int _cc = 5;
182         static final int _os = 6;
183         static final int _cs = 7;
184         static final int _comma = 8;
185         static final int _colon = 9;
186         static final int _end = 10;
187         static final int _error = 11;
188
189         static String token_name(int token) {
190                 switch (token) {
191                 case _string:
192                         return "string";
193                 case _double:
194                         return "number";
195                 case _long:
196                         return "number";
197                 case _boolean:
198                         return "boolean";
199                 case _oc:
200                         return "{";
201                 case _cc:
202                         return "}";
203                 case _os:
204                         return "[";
205                 case _cs:
206                         return "]";
207                 case _comma:
208                         return ",";
209                 case _colon:
210                         return ":";
211                 case _end:
212                         return "<EOF>";
213                 case _error:
214                         return "<ERROR>";
215                 default:
216                         return "<UNKNOWN>";
217                 }
218         }
219
220         String token_name() {
221                 return token_name(token);
222         }
223
224         JsonToken(int token) {
225                 this.token = token;
226         }
227
228         JsonToken(int token, boolean bval) {
229                 this.token = token;
230                 this.bval = bval;
231         }
232
233         JsonToken(int token, double dval) {
234                 this.token = token;
235                 this.dval = dval;
236         }
237
238         JsonToken(int token, long lval) {
239                 this.token = token;
240                 this.lval = lval;
241         }
242
243         JsonToken(int token, String sval) {
244                 this.token = token;
245                 this.sval = sval;
246         }
247
248         JsonToken(int token, StringBuffer bval) {
249                 this(token, bval.toString());
250         }
251 }
252
253 /*
254  * Lexer for json
255  */
256 class JsonLexer extends JsonUtil {
257         StringReader    f;
258         int             line;
259         int             ungot = -2;
260         StringBuffer    pending_token;
261         JsonToken       token;
262
263         static class keyword {
264                 String          word;
265                 JsonToken       token;
266
267                 JsonToken match(String value) {
268                         if (word.equals(value))
269                                 return token;
270                         return null;
271                 }
272
273                 keyword(String word, JsonToken token) {
274                         this.word = word;
275                         this.token = token;
276                 }
277         }
278
279         /* boolean values are the only keywords in json
280          */
281         static keyword[] keywords = {
282                 new keyword("true", new JsonToken(JsonToken._boolean, true)),
283                 new keyword("false", new JsonToken(JsonToken._boolean, false)),
284                 new keyword("NegInfinity", new JsonToken(JsonToken._double, Double.NEGATIVE_INFINITY)),
285                 new keyword("Infinity", new JsonToken(JsonToken._double, Double.POSITIVE_INFINITY)),
286                 new keyword("NaN", new JsonToken(JsonToken._double, Double.NaN))
287         };
288
289         static JsonToken keyword(String word) {
290                 for (int i = 0; i < keywords.length; i++) {
291                         JsonToken token = keywords[i].match(word);
292                         if (token != null)
293                                 return token;
294                 }
295                 return null;
296         }
297
298         /* Get the next char (-1 for EOF) */
299         int ch() throws IOException {
300                 int c;
301                 if (ungot != -2) {
302                         c = ungot;
303                         ungot = -2;
304                 } else
305                         c = f.read();
306                 if (c != -1)
307                         pending_token.append((char) c);
308                 if (c == '\n')
309                         ++line;
310                 return c;
311         }
312
313         void unch(int c) {
314                 if (ungot != -2)
315                         throw new IllegalArgumentException("ungot buffer full");
316                 pending_token.deleteCharAt( pending_token.length()-1);
317                 if (c == '\n')
318                         --line;
319                 ungot = c;
320         }
321
322         String last_token_string() {
323                 if (pending_token == null)
324                         return null;
325
326                 return pending_token.toString();
327         }
328
329         static boolean is_long_range(double d) {
330                 return -9223372036854775808.0 <= d && d <= 9223372036854775807.0;
331         }
332
333         JsonToken lex() {
334                 pending_token = new StringBuffer();
335
336                 try {
337                         for (;;) {
338                                 int c = ch();
339
340                                 switch (c) {
341                                 case -1:
342                                         return new JsonToken(JsonToken._end);
343                                 case '\n':
344                                 case ' ':
345                                 case '\t':
346                                         continue;
347                                 case '{':
348                                         return new JsonToken(JsonToken._oc);
349                                 case '}':
350                                         return new JsonToken(JsonToken._cc);
351                                 case '[':
352                                         return new JsonToken(JsonToken._os);
353                                 case ']':
354                                         return new JsonToken(JsonToken._cs);
355                                 case ',':
356                                         return new JsonToken(JsonToken._comma);
357                                 case ':':
358                                         return new JsonToken(JsonToken._colon);
359                                 case '0': case '1': case '2': case '3': case '4':
360                                 case '5': case '6': case '7': case '8': case '9':
361                                 case '.': case '-': case '+':
362                                         StringBuffer dbuf = new StringBuffer();
363                                         boolean is_double = false;
364                                         while (Character.isDigit(c) || c == '.' || c == '+' || c == '-' || c == 'e' || c == 'E') {
365                                                 if (c == '.' || c == 'E')
366                                                         is_double = true;
367                                                 dbuf.appendCodePoint(c);
368                                                 c = ch();
369                                         }
370                                         unch(c);
371                                         String dstr = dbuf.toString();
372                                         double dval;
373                                         try {
374                                                 dval = nf_json.parse(dstr).doubleValue();
375                                         } catch (ParseException pe) {
376                                                 return new JsonToken(JsonToken._error, dstr);
377                                         }
378                                         if (is_double || !is_long_range(dval))
379                                                 return new JsonToken(JsonToken._double, dval);
380                                         else {
381                                                 long lval = Long.parseLong(dstr);
382                                                 return new JsonToken(JsonToken._long, lval);
383                                         }
384                                 case '"':
385                                         StringBuffer bval = new StringBuffer();
386                                         for (;;) {
387                                                 c = ch();
388                                                 if (c == '"')
389                                                         break;
390                                                 if (c == '\\') {
391                                                         c = ch();
392                                                         switch (c) {
393                                                         case 'n':
394                                                                 c = '\n';
395                                                                 break;
396                                                         case 't':
397                                                                 c = '\t';
398                                                                 break;
399                                                         default:
400                                                                 break;
401                                                         }
402                                                 }
403                                                 bval.appendCodePoint(c);
404                                         }
405                                         return new JsonToken(JsonToken._string, bval);
406                                 default:
407                                         if (Character.isLetter(c)) {
408                                                 StringBuffer tbuf = new StringBuffer();
409                                                 do {
410                                                         tbuf.appendCodePoint(c);
411                                                         c = ch();
412                                                 } while (Character.isLetter(c));
413                                                 unch(c);
414                                                 JsonToken token = keyword(tbuf.toString());
415                                                 if (token != null)
416                                                         return token;
417                                         }
418                                         break;
419                                 }
420                         }
421                 } catch (IOException ie) {
422                         return new JsonToken(JsonToken._error, "<EIO>");
423                 }
424         }
425
426         void next() {
427                 token = lex();
428         }
429
430         JsonToken expect(int e) {
431                 JsonToken t = token;
432                 if (t.token != e)
433                         throw new IllegalArgumentException(String.format("got \"%s\" while expecting \"%s\"",
434                                                                          token.token_name(),
435                                                                          JsonToken.token_name(e)));
436                 next();
437                 return t;
438         }
439
440         JsonLexer(String s) {
441                 f = new StringReader(s);
442                 line = 1;
443                 token = null;
444         }
445 }
446
447 /*
448  * Parse a json string into a AltosJson object
449  */
450 class JsonParse {
451         JsonLexer       lexer;
452
453         void parse_error(String format, Object ... arguments) {
454                 throw new IllegalArgumentException(String.format("line %d: JSON parse error %s\n",
455                                                                  lexer.line,
456                                                                  String.format(format, arguments)));
457         }
458
459         /* Hashes are { string: value ... } */
460         JsonHash hash() {
461                 JsonHash        hash = new JsonHash();
462
463                 /* skip the open brace */
464                 lexer.next();
465                 for (;;) {
466                         /* Allow for empty hashes */
467                         if (lexer.token.token == JsonToken._cc) {
468                                 lexer.next();
469                                 return hash;
470                         }
471
472                         /* string : value */
473                         String key = lexer.expect(JsonToken._string).sval;
474                         lexer.expect(JsonToken._colon);
475                         AltosJson value = value();
476                         hash.put(key, value);
477
478                         switch (lexer.token.token) {
479                         case JsonToken._comma:
480                                 lexer.next();
481                                 break;
482                         case JsonToken._cc:
483                                 lexer.next();
484                                 return hash;
485                         default:
486                                 parse_error("got %s expect \",\" or \"}\"", lexer.token.token_name());
487                                 return null;
488                         }
489                 }
490         }
491
492         /* Arrays are [ value ... ] */
493         JsonArray array() {
494                 JsonArray       array = new JsonArray();
495
496                 lexer.next();
497                 for (int i = 0;; i++) {
498                         /* Allow for empty arrays */
499                         if (lexer.token.token == JsonToken._cs) {
500                                 lexer.next();
501                                 return array;
502                         }
503
504                         AltosJson value = value();
505                         array.put(i, value);
506                         switch (lexer.token.token) {
507                         case JsonToken._comma:
508                                 lexer.next();
509                                 break;
510                         case JsonToken._cs:
511                                 lexer.next();
512                                 return array;
513                         default:
514                                 parse_error("got %s expect \",\" or \"]\"", lexer.token.token_name());
515                                 return null;
516                         }
517                 }
518         }
519
520         /* Json is a simple LL language; one token is sufficient to
521          * identify the next object in the input
522          */
523         AltosJson value() {
524                 switch (lexer.token.token) {
525                 case JsonToken._oc:
526                         return new AltosJson(hash());
527                 case JsonToken._os:
528                         return new AltosJson(array());
529                 case JsonToken._double:
530                         double dval = lexer.token.dval;
531                         lexer.next();
532                         return new AltosJson(dval);
533                 case JsonToken._long:
534                         long lval = lexer.token.lval;
535                         lexer.next();
536                         return new AltosJson(lval);
537                 case JsonToken._string:
538                         String sval = lexer.token.sval;
539                         lexer.next();
540                         return new AltosJson(sval);
541                 case JsonToken._boolean:
542                         boolean bval = lexer.token.bval;
543                         lexer.next();
544                         return new AltosJson(bval);
545                 default:
546                         parse_error("Unexpected token \"%s\"", lexer.token.token_name());
547                 }
548                 return null;
549         }
550
551         AltosJson parse() {
552                 lexer.next();
553                 return value();
554         }
555
556         JsonParse(String s) {
557                 lexer = new JsonLexer(s);
558         }
559 }
560
561 public class AltosJson extends JsonUtil {
562         private static final int        type_none = 0;
563         private static final int        type_hash = 1;
564         private static final int        type_array = 2;
565         private static final int        type_double = 3;
566         private static final int        type_long = 4;
567         private static final int        type_string = 5;
568         private static final int        type_boolean = 6;
569
570         private int             type;
571
572         private JsonHash        hash;
573         private JsonArray       array;
574         private double          d_number;
575         private long            l_number;
576         private String          string;
577         private boolean         bool;
578
579         /* Generate string representation of the value
580          */
581         StringBuffer append(StringBuffer result, int indent, boolean pretty) {
582                 switch (type) {
583                 case type_hash:
584                         hash.append_hash(result, indent, pretty);
585                         break;
586                 case type_array:
587                         array.append_array(result, indent, pretty);
588                         break;
589                 case type_double:
590                         if (Double.isInfinite(d_number)) {
591                                 if (d_number < 0)
592                                         result.append("NegInfinity");
593                                 else
594                                         result.append("Infinity");
595                         } else if (Double.isNaN(d_number)) {
596                                 result.append("NaN");
597                         } else {
598                                 String dval = nf_json.format(d_number);
599                                 if (dval.equals("-0"))
600                                         dval = "0";
601                                 result.append(dval);
602                         }
603                         break;
604                 case type_long:
605                         result.append(new Long(l_number).toString());
606                         break;
607                 case type_string:
608                         quote(result, string);
609                         break;
610                 case type_boolean:
611                         result.append(bool ? "true" : "false");
612                         break;
613                 }
614                 return result;
615         }
616
617         private String toString(int indent, boolean pretty) {
618                 StringBuffer result = new StringBuffer();
619                 append(result, indent, pretty);
620                 return result.toString();
621         }
622
623         public String toString() {
624                 return toString(0, false);
625         }
626
627         public String toPrettyString() {
628                 return toString(0, true);
629         }
630
631         /* Parse string representation to a value
632          */
633
634         public static AltosJson fromString(String string) {
635                 JsonParse       parse = new JsonParse(string);
636                 try {
637                         return parse.parse();
638                 } catch (IllegalArgumentException ie) {
639                         System.out.printf("json:\n%s\n%s\n", string, ie.getMessage());
640                         return null;
641                 }
642         }
643
644         /* Accessor functions
645          */
646         private boolean assert_type(boolean setting, int type, int other_type, String error) {
647                 if (setting && this.type == type_none) {
648                         this.type = type;
649                         return false;
650                 }
651                 if (this.type != type && this.type != other_type)
652                         throw new IllegalArgumentException(error);
653                 return true;
654         }
655
656         private boolean assert_type(boolean setting, int type, String error) {
657                 return assert_type(setting, type, type, error);
658         }
659
660         private void assert_hash(boolean setting) {
661                 if (!assert_type(setting, type_hash, "not a hash"))
662                         hash = new JsonHash();
663         }
664
665         private void assert_array(boolean setting) {
666                 if (!assert_type(setting, type_array, "not an array"))
667                         array = new JsonArray();
668         }
669
670         private void assert_number() {
671                 assert_type(false, type_double, type_long, "not a number");
672         }
673
674         private void assert_double() {
675                 assert_type(true, type_double, type_long, "not a number");
676         }
677
678         private void assert_long() {
679                 assert_type(true, type_long, type_double, "not a number");
680         }
681
682         private void assert_string(boolean setting) {
683                 assert_type(setting, type_string, "not a string");
684         }
685
686         private void assert_boolean(boolean setting) {
687                 assert_type(setting, type_boolean, "not a boolean");
688         }
689
690         /* Primitive accessors
691          */
692         public double number() {
693                 assert_number();
694                 if (type == type_double)
695                         return d_number;
696                 else
697                         return (double) l_number;
698         }
699
700         public long l_number() {
701                 assert_number();
702                 if (type == type_double)
703                         return (long) d_number;
704                 else
705                         return l_number;
706         }
707
708         public String string() {
709                 assert_string(false);
710                 return string;
711         }
712
713         public boolean bool() {
714                 assert_boolean(false);
715                 return bool;
716         }
717
718         public AltosJson get(int index) {
719                 assert_array(false);
720                 return array.get(index);
721         }
722
723         public AltosJson get(String key) {
724                 assert_hash(false);
725                 return hash.get(key);
726         }
727
728         public int size() {
729                 assert_array(false);
730                 return array.size();
731         }
732
733         /* Typed accessors with defaulting
734          */
735         public double get_double(String key, double def) {
736                 AltosJson value = get(key);
737                 if (value != null) {
738                         return value.number();
739                 }
740                 return def;
741         }
742
743         public long get_long(String key, long def) {
744                 AltosJson value = get(key);
745                 if (value != null)
746                         return value.l_number();
747                 return def;
748         }
749
750         public int get_int(String key, int def) {
751                 AltosJson value = get(key);
752                 if (value != null)
753                         return (int) value.l_number();
754                 return def;
755         }
756
757         public String get_string(String key, String def) {
758                 AltosJson value = get(key);
759                 if (value != null)
760                         return value.string();
761                 return def;
762         }
763
764         public boolean get_boolean(String key, boolean def) {
765                 AltosJson value = get(key);
766                 if (value != null)
767                         return value.bool();
768                 return def;
769         }
770
771         public double get_double(int index, double def) {
772                 AltosJson value = get(index);
773                 if (value != null)
774                         return value.number();
775                 return def;
776         }
777
778         public long get_long(int index, long def) {
779                 AltosJson value = get(index);
780                 if (value != null)
781                         return value.l_number();
782                 return def;
783         }
784
785         public int get_int(int index, int def) {
786                 AltosJson value = get(index);
787                 if (value != null)
788                         return (int) value.l_number();
789                 return def;
790         }
791
792         public String get_string(int index, String def) {
793                 AltosJson value = get(index);
794                 if (value != null)
795                         return value.string();
796                 return def;
797         }
798
799         public boolean get_boolean(int index, boolean def) {
800                 AltosJson value = get(index);
801                 if (value != null)
802                         return value.bool();
803                 return def;
804         }
805
806         public double[] get_double_array(String key, double[] def) {
807                 AltosJson value = get(key);
808                 if (value != null) {
809                         double[] ret = new double[value.size()];
810                         for (int i = 0; i < value.size(); i++)
811                                 ret[i] = value.get_double(i, def == null ? 0 : def[i]);
812                         return ret;
813                 }
814                 return def;
815         }
816
817         public int[] get_int_array(String key, int[] def) {
818                 AltosJson value = get(key);
819                 if (value != null) {
820                         int[] ret = new int[value.size()];
821                         for (int i = 0; i < value.size(); i++)
822                                 ret[i] = value.get_int(i, def == null ? 0 : def[i]);
823                         return ret;
824                 }
825                 return def;
826         }
827
828         /* Array setter functions
829          */
830         public AltosJson put(int index, AltosJson value) {
831                 assert_array(true);
832                 array.put(index, value);
833                 return value;
834         }
835
836         public Object put(int index, Object value) {
837                 assert_array(true);
838                 if (value != null)
839                         array.put(index, new AltosJson(value));
840                 return value;
841         }
842
843         public double put(int index, double value) {
844                 assert_array(true);
845                 array.put(index, new AltosJson(value));
846                 return value;
847         }
848
849         public AltosJson put(int index, double[] value) {
850                 if (value != null) {
851                         assert_array(true);
852                         array.put(index, new AltosJson(value));
853                 }
854                 return this;
855         }
856
857         public int[] put(int index, int[] value) {
858                 if (value != null) {
859                         assert_array(true);
860                         array.put(index, new AltosJson(value));
861                 }
862                 return value;
863         }
864
865         public String put(int index, String value) {
866                 if (value != null) {
867                         assert_array(true);
868                         array.put(index, new AltosJson(value));
869                 }
870                 return value;
871         }
872
873         public boolean put(int index, boolean value) {
874                 assert_array(true);
875                 array.put(index, new AltosJson(value));
876                 return value;
877         }
878
879         /* Hash setter functions
880          */
881         public AltosJson put(String key, AltosJson value) {
882                 assert_hash(true);
883                 hash.put(key, value);
884                 return value;
885         }
886
887         public Object put(String key, Object value) {
888                 assert_hash(true);
889                 if (value != null)
890                         hash.put(key, new AltosJson(value));
891                 return value;
892         }
893
894         public double put(String key, double value) {
895                 assert_hash(true);
896                 hash.put(key, new AltosJson(value));
897                 return value;
898         }
899
900         public String put(String key, String value) {
901                 if (value != null) {
902                         assert_hash(true);
903                         hash.put(key, new AltosJson(value));
904                 }
905                 return value;
906         }
907
908         public boolean put(String key, boolean value) {
909                 assert_hash(true);
910                 hash.put(key, new AltosJson(value));
911                 return value;
912         }
913
914         public AltosJson[] put(String key, AltosJson[] value) {
915                 if (value != null) {
916                         assert_hash(true);
917                         hash.put(key, new AltosJson(value));
918                 }
919                 return value;
920         }
921
922         public double[] put(String key, double[] value) {
923                 if (value != null) {
924                         assert_hash(true);
925                         hash.put(key, new AltosJson(value));
926                 }
927                 return value;
928         }
929
930         public int[] put(String key, int[] value) {
931                 if (value != null) {
932                         assert_hash(true);
933                         hash.put(key, new AltosJson(value));
934                 }
935                 return value;
936         }
937
938         /* Primitive setter functions
939          */
940         public double put(double value) {
941                 assert_double();
942                 d_number = value;
943                 return value;
944         }
945
946         public byte put(byte value) {
947                 assert_long();
948                 l_number = value;
949                 return value;
950         }
951
952         public char put(char value) {
953                 assert_long();
954                 l_number = value;
955                 return value;
956         }
957
958         public int put(int value) {
959                 assert_long();
960                 l_number = value;
961                 return value;
962         }
963
964         public long put(long value) {
965                 assert_long();
966                 l_number = value;
967                 return value;
968         }
969
970         public String put(String value) {
971                 assert_string(true);
972                 string = value;
973                 return value;
974         }
975
976         public boolean put(boolean value) {
977                 assert_boolean(true);
978                 bool = value;
979                 return value;
980         }
981
982         private boolean isInnerClass(Class c) {
983                 for (Field field : c.getDeclaredFields())
984                         if (field.isSynthetic())
985                                 return true;
986                 return false;
987         }
988
989         /* Construct an object of the specified class from the JSON
990          * representation.
991          *
992          * This works as long as the structure is non-recursive, and
993          * all inner classes are only members of their immediate outer
994          * class
995          */
996         private Object make(Class c, Class enclosing_class, Object enclosing_object) {
997                 Object  ret;
998                 if (c == Boolean.TYPE) {
999                         ret = bool();
1000                 } else if (c == Byte.TYPE) {
1001                         ret = (Byte) (byte) l_number();
1002                 } else if (c == Character.TYPE) {
1003                         ret = (Character) (char) l_number();
1004                 } else if (c == Integer.TYPE) {
1005                         ret = (Integer) (int) l_number();
1006                 } else if (c == Long.TYPE) {
1007                         ret = l_number();
1008                 } else if (c == Double.TYPE) {
1009                         ret = number();
1010                 } else if (c == String.class) {
1011                         ret = string();
1012                 } else if (c.isArray()) {
1013                         assert_array(false);
1014
1015                         Class element_class = c.getComponentType();
1016                         if (element_class == Boolean.TYPE) {
1017                                 boolean[] array = (boolean[]) Array.newInstance(element_class, size());
1018                                 for (int i = 0; i < array.length; i++)
1019                                         array[i] = (Boolean) get(i).make(element_class);
1020                                 ret = array;
1021                         } else if (element_class == Byte.TYPE) {
1022                                 byte[] array = (byte[]) Array.newInstance(element_class, size());
1023                                 for (int i = 0; i < array.length; i++)
1024                                         array[i] = (Byte) get(i).make(element_class);
1025                                 ret = array;
1026                         } else if (element_class == Character.TYPE) {
1027                                 char[] array = (char[]) Array.newInstance(element_class, size());
1028                                 for (int i = 0; i < array.length; i++)
1029                                         array[i] = (Character) get(i).make(element_class);
1030                                 ret = array;
1031                         } else if (element_class == Integer.TYPE) {
1032                                 int[] array = (int[]) Array.newInstance(element_class, size());
1033                                 for (int i = 0; i < array.length; i++)
1034                                         array[i] = (Integer) get(i).make(element_class);
1035                                 ret = array;
1036                         } else if (element_class == Long.TYPE) {
1037                                 long[] array = (long[]) Array.newInstance(element_class, size());
1038                                 for (int i = 0; i < array.length; i++)
1039                                         array[i] = (Long) get(i).make(element_class);
1040                                 ret = array;
1041                         } else if (element_class == Double.TYPE) {
1042                                 double[] array = (double[]) Array.newInstance(element_class, size());
1043                                 for (int i = 0; i < array.length; i++)
1044                                         array[i] = (Double) get(i).make(element_class);
1045                                 ret = array;
1046                         } else {
1047                                 Object[] array = (Object[]) Array.newInstance(element_class, size());
1048                                 for (int i = 0; i < array.length; i++)
1049                                         array[i] = get(i).make(element_class);
1050                                 ret = array;
1051                         }
1052                 } else {
1053                         assert_hash(false);
1054                         Object object = null;
1055                         try {
1056                                 /* Inner classes have a hidden extra parameter
1057                                  * to the constructor. Assume that the enclosing object is
1058                                  * of the enclosing class and construct the object
1059                                  * based on that.
1060                                  */
1061                                 if (enclosing_class != null && isInnerClass(c)) {
1062                                         Constructor<?> ctor = ((Class<?>)c).getDeclaredConstructor((Class<?>) enclosing_class);
1063                                         object = ctor.newInstance(enclosing_object);
1064                                 } else {
1065                                         object = c.newInstance();
1066                                 }
1067                                 for (; c != Object.class; c = c.getSuperclass()) {
1068                                         for (Field field : c.getDeclaredFields()) {
1069                                                 String  fieldName = field.getName();
1070                                                 Class   fieldClass = field.getType();
1071
1072                                                 if (Modifier.isStatic(field.getModifiers()))
1073                                                         continue;
1074                                                 if (field.isSynthetic())
1075                                                         continue;
1076                                                 try {
1077                                                         AltosJson json = get(fieldName);
1078                                                         if (json != null) {
1079                                                                 Object val = json.make(fieldClass, c, object);
1080                                                                 field.setAccessible(true);
1081                                                                 field.set(object, val);
1082                                                         }
1083                                                 } catch (IllegalAccessException ie) {
1084                                                         System.out.printf("%s:%s %s\n",
1085                                                                           c.getName(), fieldName, ie.toString());
1086                                                 }
1087                                         }
1088                                 }
1089                                 ret = object;
1090                         } catch (InvocationTargetException ie) {
1091                                 System.out.printf("%s: %s\n",
1092                                                   c.getName(), ie.toString());
1093                                 ret = null;
1094                         } catch (NoSuchMethodException ie) {
1095                                 System.out.printf("%s: %s\n",
1096                                                   c.getName(), ie.toString());
1097                                 ret = null;
1098                         } catch (InstantiationException ie) {
1099                                 System.out.printf("%s: %s\n",
1100                                                   c.getName(), ie.toString());
1101                                 ret = null;
1102                         } catch (IllegalAccessException ie) {
1103                                 System.out.printf("%s: %s\n",
1104                                                   c.getName(), ie.toString());
1105                                 ret = null;
1106                         }
1107                 }
1108                 return ret;
1109         }
1110
1111         /* This is the public API for the
1112          * above function which doesn't handle
1113          * inner classes
1114          */
1115         public Object make(Class c) {
1116                 return make(c, null, null);
1117         }
1118
1119         /* Constructors, one for each primitive type, String and Object */
1120         public AltosJson(boolean bool) {
1121                 type = type_boolean;
1122                 this.bool = bool;
1123         }
1124
1125         public AltosJson(byte number) {
1126                 type = type_long;
1127                 this.l_number = number;
1128         }
1129
1130         public AltosJson(char number) {
1131                 type = type_long;
1132                 this.l_number = number;
1133         }
1134
1135         public AltosJson(int number) {
1136                 type = type_long;
1137                 this.l_number = number;
1138         }
1139
1140         public AltosJson(long number) {
1141                 type = type_long;
1142                 this.l_number = number;
1143         }
1144
1145         public AltosJson(double number) {
1146                 type = type_double;
1147                 this.d_number = number;
1148         }
1149
1150         public AltosJson(String string) {
1151                 type = type_string;
1152                 this.string = string;
1153         }
1154
1155         public AltosJson(Object object) {
1156                 if (object instanceof Boolean) {
1157                         type = type_boolean;
1158                         bool = (Boolean) object;
1159                 } else if (object instanceof Byte) {
1160                         type = type_long;
1161                         l_number = (Byte) object;
1162                 } else if (object instanceof Character) {
1163                         type = type_long;
1164                         l_number = (Character) object;
1165                 } else if (object instanceof Integer) {
1166                         type = type_long;
1167                         l_number = (Integer) object;
1168                 } else if (object instanceof Long) {
1169                         type = type_long;
1170                         l_number = (Long) object;
1171                 } else if (object instanceof Double) {
1172                         type = type_double;
1173                         d_number = (Double) object;
1174                 } else if (object instanceof String) {
1175                         type = type_string;
1176                         string = (String) object;
1177                 } else if (object.getClass().isArray()) {
1178                         assert_array(true);
1179
1180                         Class component_class = object.getClass().getComponentType();
1181                         if (component_class == Boolean.TYPE) {
1182                                 boolean[] array = (boolean[]) object;
1183                                 for (int i = 0; i < array.length; i++)
1184                                         put(i, new AltosJson(array[i]));
1185                         } else if (component_class == Byte.TYPE) {
1186                                 byte[] array = (byte[]) object;
1187                                 for (int i = 0; i < array.length; i++)
1188                                         put(i, new AltosJson(array[i]));
1189                         } else if (component_class == Character.TYPE) {
1190                                 char[] array = (char[]) object;
1191                                 for (int i = 0; i < array.length; i++)
1192                                         put(i, new AltosJson(array[i]));
1193                         } else if (component_class == Integer.TYPE) {
1194                                 int[] array = (int[]) object;
1195                                 for (int i = 0; i < array.length; i++)
1196                                         put(i, new AltosJson(array[i]));
1197                         } else if (component_class == Long.TYPE) {
1198                                 long[] array = (long[]) object;
1199                                 for (int i = 0; i < array.length; i++)
1200                                         put(i, new AltosJson(array[i]));
1201                         } else if (component_class == Double.TYPE) {
1202                                 double[] array = (double[]) object;
1203                                 for (int i = 0; i < array.length; i++)
1204                                         put(i, new AltosJson(array[i]));
1205                         } else {
1206                                 Object[] array = (Object[]) object;
1207                                 for (int i = 0; i < array.length; i++)
1208                                         put(i, new AltosJson(array[i]));
1209                         }
1210                 } else {
1211                         assert_hash(true);
1212                         for (Class c = object.getClass(); c != Object.class; c = c.getSuperclass()) {
1213                                 for (Field field : c.getDeclaredFields()) {
1214                                         String  fieldName = field.getName();
1215
1216                                         /* Skip static fields */
1217                                         if (Modifier.isStatic(field.getModifiers()))
1218                                                 continue;
1219
1220                                         /* Skip synthetic fields. We're assuming
1221                                          * those are always an inner class reference
1222                                          * to the outer class object
1223                                          */
1224                                         if (field.isSynthetic())
1225                                                 continue;
1226                                         try {
1227                                                 /* We may need to force the field to be accessible if
1228                                                  * it is private
1229                                                  */
1230                                                 field.setAccessible(true);
1231                                                 Object val = field.get(object);
1232                                                 if (val != null) {
1233                                                         AltosJson json = new AltosJson(val);
1234                                                         put(fieldName, json);
1235                                                 }
1236                                         } catch (IllegalAccessException ie) {
1237                                                 System.out.printf("%s:%s %s\n",
1238                                                                   c.getName(), fieldName, ie.toString());
1239                                         }
1240                                 }
1241                         }
1242                 }
1243         }
1244
1245         /* Array constructors, one for each primitive type, String and Object */
1246         public AltosJson(boolean[] bools) {
1247                 assert_array(true);
1248                 for(int i = 0; i < bools.length; i++)
1249                         put(i, new AltosJson(bools[i]));
1250         }
1251
1252         public AltosJson(byte[] numbers) {
1253                 assert_array(true);
1254                 for(int i = 0; i < numbers.length; i++)
1255                         put(i, new AltosJson(numbers[i]));
1256         }
1257
1258         public AltosJson(char[] numbers) {
1259                 assert_array(true);
1260                 for(int i = 0; i < numbers.length; i++)
1261                         put(i, new AltosJson(numbers[i]));
1262         }
1263
1264         public AltosJson(int[] numbers) {
1265                 assert_array(true);
1266                 for(int i = 0; i < numbers.length; i++)
1267                         put(i, new AltosJson(numbers[i]));
1268         }
1269
1270         public AltosJson(long[] numbers) {
1271                 assert_array(true);
1272                 for(int i = 0; i < numbers.length; i++)
1273                         put(i, new AltosJson(numbers[i]));
1274         }
1275
1276         public AltosJson(double[] numbers) {
1277                 assert_array(true);
1278                 for(int i = 0; i < numbers.length; i++)
1279                         put(i, new AltosJson(numbers[i]));
1280         }
1281
1282         public AltosJson(String[] strings) {
1283                 assert_array(true);
1284                 for(int i = 0; i < strings.length; i++)
1285                         put(i, new AltosJson(strings[i]));
1286         }
1287
1288         public AltosJson(AltosJson[] jsons) {
1289                 assert_array(true);
1290                 for (int i = 0; i < jsons.length; i++)
1291                         put(i, jsons[i]);
1292         }
1293
1294         public AltosJson(Object[] array) {
1295                 assert_array(true);
1296                 for (int i = 0; i < array.length; i++)
1297                         put(i, new AltosJson(array[i]));
1298         }
1299
1300         /* Empty constructor
1301          */
1302         public AltosJson() {
1303                 type = type_none;
1304         }
1305
1306         public AltosJson(JsonHash hash) {
1307                 type = type_hash;
1308                 this.hash = hash;
1309         }
1310
1311         public AltosJson(JsonArray array) {
1312                 type = type_array;
1313                 this.array = array;
1314         }
1315 }