altoslib: Add Ms5607 constructor that just takes config_data
[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         Writer quote(Writer writer, String a) throws IOException {
29                 writer.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                                 writer.append('\\').append(c);
37                                 break;
38                         case '\n':
39                                 writer.append("\\n");
40                                 break;
41                         default:
42                                 writer.append(c);
43                                 break;
44                         }
45                 }
46                 writer.append("\"");
47                 return writer;
48         }
49
50         Writer append(Writer result, AltosJson value, int indent, boolean pretty) throws IOException {
51                 value.append(result, indent, pretty);
52                 return result;
53         }
54
55         Writer append(Writer result, String string) throws IOException {
56                 result.append(string);
57                 return result;
58         }
59
60         Writer indent(Writer result, int indent) throws IOException {
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(Writer result, int indent, boolean pretty) throws IOException {
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(Writer result, int indent, boolean pretty) throws IOException {
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, Writer bval) {
249                 this(token, bval.toString());
250         }
251 }
252
253 /*
254  * Lexer for json
255  */
256 class JsonLexer extends JsonUtil {
257         Reader          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                                         Writer bval = new StringWriter();
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.write(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         JsonLexer(Reader f) {
447                 this.f = f;
448                 line = 1;
449                 token = null;
450         }
451 }
452
453 /*
454  * Parse a json string into a AltosJson object
455  */
456 class JsonParse {
457         JsonLexer       lexer;
458
459         void parse_error(String format, Object ... arguments) {
460                 throw new IllegalArgumentException(String.format("line %d: JSON parse error %s\n",
461                                                                  lexer.line,
462                                                                  String.format(format, arguments)));
463         }
464
465         /* Hashes are { string: value ... } */
466         JsonHash hash() {
467                 JsonHash        hash = new JsonHash();
468
469                 /* skip the open brace */
470                 lexer.next();
471                 for (;;) {
472                         /* Allow for empty hashes */
473                         if (lexer.token.token == JsonToken._cc) {
474                                 lexer.next();
475                                 return hash;
476                         }
477
478                         /* string : value */
479                         String key = lexer.expect(JsonToken._string).sval;
480                         lexer.expect(JsonToken._colon);
481                         AltosJson value = value();
482                         hash.put(key, value);
483
484                         switch (lexer.token.token) {
485                         case JsonToken._comma:
486                                 lexer.next();
487                                 break;
488                         case JsonToken._cc:
489                                 lexer.next();
490                                 return hash;
491                         default:
492                                 parse_error("got %s expect \",\" or \"}\"", lexer.token.token_name());
493                                 return null;
494                         }
495                 }
496         }
497
498         /* Arrays are [ value ... ] */
499         JsonArray array() {
500                 JsonArray       array = new JsonArray();
501
502                 lexer.next();
503                 for (int i = 0;; i++) {
504                         /* Allow for empty arrays */
505                         if (lexer.token.token == JsonToken._cs) {
506                                 lexer.next();
507                                 return array;
508                         }
509
510                         AltosJson value = value();
511                         array.put(i, value);
512                         switch (lexer.token.token) {
513                         case JsonToken._comma:
514                                 lexer.next();
515                                 break;
516                         case JsonToken._cs:
517                                 lexer.next();
518                                 return array;
519                         default:
520                                 parse_error("got %s expect \",\" or \"]\"", lexer.token.token_name());
521                                 return null;
522                         }
523                 }
524         }
525
526         /* Json is a simple LL language; one token is sufficient to
527          * identify the next object in the input
528          */
529         AltosJson value() {
530                 switch (lexer.token.token) {
531                 case JsonToken._oc:
532                         return new AltosJson(hash());
533                 case JsonToken._os:
534                         return new AltosJson(array());
535                 case JsonToken._double:
536                         double dval = lexer.token.dval;
537                         lexer.next();
538                         return new AltosJson(dval);
539                 case JsonToken._long:
540                         long lval = lexer.token.lval;
541                         lexer.next();
542                         return new AltosJson(lval);
543                 case JsonToken._string:
544                         String sval = lexer.token.sval;
545                         lexer.next();
546                         return new AltosJson(sval);
547                 case JsonToken._boolean:
548                         boolean bval = lexer.token.bval;
549                         lexer.next();
550                         return new AltosJson(bval);
551                 default:
552                         parse_error("Unexpected token \"%s\"", lexer.token.token_name());
553                 }
554                 return null;
555         }
556
557         AltosJson parse() {
558                 lexer.next();
559                 return value();
560         }
561
562         JsonParse(String s) {
563                 lexer = new JsonLexer(s);
564         }
565
566         JsonParse(Reader f) {
567                 lexer = new JsonLexer(f);
568         }
569 }
570
571 public class AltosJson extends JsonUtil {
572         private static final int        type_none = 0;
573         private static final int        type_hash = 1;
574         private static final int        type_array = 2;
575         private static final int        type_double = 3;
576         private static final int        type_long = 4;
577         private static final int        type_string = 5;
578         private static final int        type_boolean = 6;
579
580         private int             type;
581
582         private JsonHash        hash;
583         private JsonArray       array;
584         private double          d_number;
585         private long            l_number;
586         private String          string;
587         private boolean         bool;
588
589         /* Generate string representation of the value
590          */
591         Writer append(Writer result, int indent, boolean pretty) throws IOException {
592                 switch (type) {
593                 case type_hash:
594                         hash.append_hash(result, indent, pretty);
595                         break;
596                 case type_array:
597                         array.append_array(result, indent, pretty);
598                         break;
599                 case type_double:
600                         if (Double.isInfinite(d_number)) {
601                                 if (d_number < 0)
602                                         result.append("NegInfinity");
603                                 else
604                                         result.append("Infinity");
605                         } else if (Double.isNaN(d_number)) {
606                                 result.append("NaN");
607                         } else {
608                                 String dval = nf_json.format(d_number);
609                                 if (dval.equals("-0"))
610                                         dval = "0";
611                                 result.append(dval);
612                         }
613                         break;
614                 case type_long:
615                         result.append(new Long(l_number).toString());
616                         break;
617                 case type_string:
618                         quote(result, string);
619                         break;
620                 case type_boolean:
621                         result.append(bool ? "true" : "false");
622                         break;
623                 }
624                 return result;
625         }
626
627         private String toString(int indent, boolean pretty) {
628                 try {
629                         Writer result = new StringWriter();
630                         append(result, indent, pretty);
631                         return result.toString();
632                 } catch (Exception e) {
633                         return null;
634                 }
635         }
636
637         public String toString() {
638                 return toString(0, false);
639         }
640
641         public String toPrettyString() {
642                 return toString(0, true);
643         }
644
645         public void write(Writer w, int indent, boolean pretty) throws IOException {
646                 append(w, indent, pretty);
647         }
648
649         public void write(Writer w) throws IOException {
650                 write(w, 0, true);
651         }
652
653         /* Parse string representation to a value
654          */
655
656         public static AltosJson fromString(String string) {
657                 JsonParse       parse = new JsonParse(string);
658                 try {
659                         return parse.parse();
660                 } catch (IllegalArgumentException ie) {
661                         System.out.printf("json:\n%s\n%s\n", string, ie.getMessage());
662                         return null;
663                 }
664         }
665
666         public static AltosJson fromReader(Reader f) {
667                 JsonParse       parse = new JsonParse(f);
668                 try {
669                         return parse.parse();
670                 } catch (IllegalArgumentException ie) {
671                         System.out.printf("json:\n%s\n", ie.getMessage());
672                         return null;
673                 }
674         }
675
676         /* Accessor functions
677          */
678         private boolean assert_type(boolean setting, int type, int other_type, String error) {
679                 if (setting && this.type == type_none) {
680                         this.type = type;
681                         return false;
682                 }
683                 if (this.type != type && this.type != other_type)
684                         throw new IllegalArgumentException(error);
685                 return true;
686         }
687
688         private boolean assert_type(boolean setting, int type, String error) {
689                 return assert_type(setting, type, type, error);
690         }
691
692         private void assert_hash(boolean setting) {
693                 if (!assert_type(setting, type_hash, "not a hash"))
694                         hash = new JsonHash();
695         }
696
697         private void assert_array(boolean setting) {
698                 if (!assert_type(setting, type_array, "not an array"))
699                         array = new JsonArray();
700         }
701
702         private void assert_number() {
703                 assert_type(false, type_double, type_long, "not a number");
704         }
705
706         private void assert_double() {
707                 assert_type(true, type_double, type_long, "not a number");
708         }
709
710         private void assert_long() {
711                 assert_type(true, type_long, type_double, "not a number");
712         }
713
714         private void assert_string(boolean setting) {
715                 assert_type(setting, type_string, "not a string");
716         }
717
718         private void assert_boolean(boolean setting) {
719                 assert_type(setting, type_boolean, "not a boolean");
720         }
721
722         /* Primitive accessors
723          */
724         public double number() {
725                 assert_number();
726                 if (type == type_double)
727                         return d_number;
728                 else
729                         return (double) l_number;
730         }
731
732         public long l_number() {
733                 assert_number();
734                 if (type == type_double)
735                         return (long) d_number;
736                 else
737                         return l_number;
738         }
739
740         public String string() {
741                 assert_string(false);
742                 return string;
743         }
744
745         public boolean bool() {
746                 assert_boolean(false);
747                 return bool;
748         }
749
750         public AltosJson get(int index) {
751                 assert_array(false);
752                 return array.get(index);
753         }
754
755         public AltosJson get(String key) {
756                 assert_hash(false);
757                 return hash.get(key);
758         }
759
760         public int size() {
761                 assert_array(false);
762                 return array.size();
763         }
764
765         /* Typed accessors with defaulting
766          */
767         public double get_double(String key, double def) {
768                 AltosJson value = get(key);
769                 if (value != null) {
770                         return value.number();
771                 }
772                 return def;
773         }
774
775         public long get_long(String key, long def) {
776                 AltosJson value = get(key);
777                 if (value != null)
778                         return value.l_number();
779                 return def;
780         }
781
782         public int get_int(String key, int def) {
783                 AltosJson value = get(key);
784                 if (value != null)
785                         return (int) value.l_number();
786                 return def;
787         }
788
789         public String get_string(String key, String def) {
790                 AltosJson value = get(key);
791                 if (value != null)
792                         return value.string();
793                 return def;
794         }
795
796         public boolean get_boolean(String key, boolean def) {
797                 AltosJson value = get(key);
798                 if (value != null)
799                         return value.bool();
800                 return def;
801         }
802
803         public double get_double(int index, double def) {
804                 AltosJson value = get(index);
805                 if (value != null)
806                         return value.number();
807                 return def;
808         }
809
810         public long get_long(int index, long def) {
811                 AltosJson value = get(index);
812                 if (value != null)
813                         return value.l_number();
814                 return def;
815         }
816
817         public int get_int(int index, int def) {
818                 AltosJson value = get(index);
819                 if (value != null)
820                         return (int) value.l_number();
821                 return def;
822         }
823
824         public String get_string(int index, String def) {
825                 AltosJson value = get(index);
826                 if (value != null)
827                         return value.string();
828                 return def;
829         }
830
831         public boolean get_boolean(int index, boolean def) {
832                 AltosJson value = get(index);
833                 if (value != null)
834                         return value.bool();
835                 return def;
836         }
837
838         public double[] get_double_array(String key, double[] def) {
839                 AltosJson value = get(key);
840                 if (value != null) {
841                         double[] ret = new double[value.size()];
842                         for (int i = 0; i < value.size(); i++)
843                                 ret[i] = value.get_double(i, def == null ? 0 : def[i]);
844                         return ret;
845                 }
846                 return def;
847         }
848
849         public int[] get_int_array(String key, int[] def) {
850                 AltosJson value = get(key);
851                 if (value != null) {
852                         int[] ret = new int[value.size()];
853                         for (int i = 0; i < value.size(); i++)
854                                 ret[i] = value.get_int(i, def == null ? 0 : def[i]);
855                         return ret;
856                 }
857                 return def;
858         }
859
860         /* Array setter functions
861          */
862         public AltosJson put(int index, AltosJson value) {
863                 assert_array(true);
864                 array.put(index, value);
865                 return value;
866         }
867
868         public Object put(int index, Object value) {
869                 assert_array(true);
870                 if (value != null)
871                         array.put(index, new AltosJson(value));
872                 return value;
873         }
874
875         public double put(int index, double value) {
876                 assert_array(true);
877                 array.put(index, new AltosJson(value));
878                 return value;
879         }
880
881         public AltosJson put(int index, double[] value) {
882                 if (value != null) {
883                         assert_array(true);
884                         array.put(index, new AltosJson(value));
885                 }
886                 return this;
887         }
888
889         public int[] put(int index, int[] value) {
890                 if (value != null) {
891                         assert_array(true);
892                         array.put(index, new AltosJson(value));
893                 }
894                 return value;
895         }
896
897         public String put(int index, String value) {
898                 if (value != null) {
899                         assert_array(true);
900                         array.put(index, new AltosJson(value));
901                 }
902                 return value;
903         }
904
905         public boolean put(int index, boolean value) {
906                 assert_array(true);
907                 array.put(index, new AltosJson(value));
908                 return value;
909         }
910
911         /* Hash setter functions
912          */
913         public AltosJson put(String key, AltosJson value) {
914                 assert_hash(true);
915                 hash.put(key, value);
916                 return value;
917         }
918
919         public Object put(String key, Object value) {
920                 assert_hash(true);
921                 if (value != null)
922                         hash.put(key, new AltosJson(value));
923                 return value;
924         }
925
926         public double put(String key, double value) {
927                 assert_hash(true);
928                 hash.put(key, new AltosJson(value));
929                 return value;
930         }
931
932         public String put(String key, String value) {
933                 if (value != null) {
934                         assert_hash(true);
935                         hash.put(key, new AltosJson(value));
936                 }
937                 return value;
938         }
939
940         public boolean put(String key, boolean value) {
941                 assert_hash(true);
942                 hash.put(key, new AltosJson(value));
943                 return value;
944         }
945
946         public AltosJson[] put(String key, AltosJson[] value) {
947                 if (value != null) {
948                         assert_hash(true);
949                         hash.put(key, new AltosJson(value));
950                 }
951                 return value;
952         }
953
954         public double[] put(String key, double[] value) {
955                 if (value != null) {
956                         assert_hash(true);
957                         hash.put(key, new AltosJson(value));
958                 }
959                 return value;
960         }
961
962         public int[] put(String key, int[] value) {
963                 if (value != null) {
964                         assert_hash(true);
965                         hash.put(key, new AltosJson(value));
966                 }
967                 return value;
968         }
969
970         /* Primitive setter functions
971          */
972         public double put(double value) {
973                 assert_double();
974                 d_number = value;
975                 return value;
976         }
977
978         public byte put(byte value) {
979                 assert_long();
980                 l_number = value;
981                 return value;
982         }
983
984         public char put(char value) {
985                 assert_long();
986                 l_number = value;
987                 return value;
988         }
989
990         public int put(int value) {
991                 assert_long();
992                 l_number = value;
993                 return value;
994         }
995
996         public long put(long value) {
997                 assert_long();
998                 l_number = value;
999                 return value;
1000         }
1001
1002         public String put(String value) {
1003                 assert_string(true);
1004                 string = value;
1005                 return value;
1006         }
1007
1008         public boolean put(boolean value) {
1009                 assert_boolean(true);
1010                 bool = value;
1011                 return value;
1012         }
1013
1014         private boolean isInnerClass(Class c) {
1015                 for (Field field : c.getDeclaredFields())
1016                         if (field.isSynthetic())
1017                                 return true;
1018                 return false;
1019         }
1020
1021         /* Construct an object of the specified class from the JSON
1022          * representation.
1023          *
1024          * This works as long as the structure is non-recursive, and
1025          * all inner classes are only members of their immediate outer
1026          * class
1027          */
1028         private Object make(Class c, Class enclosing_class, Object enclosing_object) {
1029                 Object  ret;
1030                 if (c == Boolean.TYPE) {
1031                         ret = bool();
1032                 } else if (c == Byte.TYPE) {
1033                         ret = (Byte) (byte) l_number();
1034                 } else if (c == Character.TYPE) {
1035                         ret = (Character) (char) l_number();
1036                 } else if (c == Integer.TYPE) {
1037                         ret = (Integer) (int) l_number();
1038                 } else if (c == Long.TYPE) {
1039                         ret = l_number();
1040                 } else if (c == Double.TYPE) {
1041                         ret = number();
1042                 } else if (c == String.class) {
1043                         ret = string();
1044                 } else if (c.isArray()) {
1045                         assert_array(false);
1046
1047                         Class element_class = c.getComponentType();
1048                         if (element_class == Boolean.TYPE) {
1049                                 boolean[] array = (boolean[]) Array.newInstance(element_class, size());
1050                                 for (int i = 0; i < array.length; i++)
1051                                         array[i] = (Boolean) get(i).make(element_class);
1052                                 ret = array;
1053                         } else if (element_class == Byte.TYPE) {
1054                                 byte[] array = (byte[]) Array.newInstance(element_class, size());
1055                                 for (int i = 0; i < array.length; i++)
1056                                         array[i] = (Byte) get(i).make(element_class);
1057                                 ret = array;
1058                         } else if (element_class == Character.TYPE) {
1059                                 char[] array = (char[]) Array.newInstance(element_class, size());
1060                                 for (int i = 0; i < array.length; i++)
1061                                         array[i] = (Character) get(i).make(element_class);
1062                                 ret = array;
1063                         } else if (element_class == Integer.TYPE) {
1064                                 int[] array = (int[]) Array.newInstance(element_class, size());
1065                                 for (int i = 0; i < array.length; i++)
1066                                         array[i] = (Integer) get(i).make(element_class);
1067                                 ret = array;
1068                         } else if (element_class == Long.TYPE) {
1069                                 long[] array = (long[]) Array.newInstance(element_class, size());
1070                                 for (int i = 0; i < array.length; i++)
1071                                         array[i] = (Long) get(i).make(element_class);
1072                                 ret = array;
1073                         } else if (element_class == Double.TYPE) {
1074                                 double[] array = (double[]) Array.newInstance(element_class, size());
1075                                 for (int i = 0; i < array.length; i++)
1076                                         array[i] = (Double) get(i).make(element_class);
1077                                 ret = array;
1078                         } else {
1079                                 Object[] array = (Object[]) Array.newInstance(element_class, size());
1080                                 for (int i = 0; i < array.length; i++)
1081                                         array[i] = get(i).make(element_class);
1082                                 ret = array;
1083                         }
1084                 } else {
1085                         assert_hash(false);
1086                         Object object = null;
1087                         try {
1088                                 /* Inner classes have a hidden extra parameter
1089                                  * to the constructor. Assume that the enclosing object is
1090                                  * of the enclosing class and construct the object
1091                                  * based on that.
1092                                  */
1093                                 if (enclosing_class != null && isInnerClass(c)) {
1094                                         Constructor<?> ctor = ((Class<?>)c).getDeclaredConstructor((Class<?>) enclosing_class);
1095                                         object = ctor.newInstance(enclosing_object);
1096                                 } else {
1097                                         object = c.newInstance();
1098                                 }
1099                                 for (; c != Object.class; c = c.getSuperclass()) {
1100                                         for (Field field : c.getDeclaredFields()) {
1101                                                 String  fieldName = field.getName();
1102                                                 Class   fieldClass = field.getType();
1103
1104                                                 if (Modifier.isStatic(field.getModifiers()))
1105                                                         continue;
1106                                                 if (field.isSynthetic())
1107                                                         continue;
1108                                                 try {
1109                                                         AltosJson json = get(fieldName);
1110                                                         if (json != null) {
1111                                                                 Object val = json.make(fieldClass, c, object);
1112                                                                 field.setAccessible(true);
1113                                                                 field.set(object, val);
1114                                                         }
1115                                                 } catch (IllegalAccessException ie) {
1116                                                         System.out.printf("%s:%s %s\n",
1117                                                                           c.getName(), fieldName, ie.toString());
1118                                                 }
1119                                         }
1120                                 }
1121                                 ret = object;
1122                         } catch (InvocationTargetException ie) {
1123                                 System.out.printf("%s: %s\n",
1124                                                   c.getName(), ie.toString());
1125                                 ret = null;
1126                         } catch (NoSuchMethodException ie) {
1127                                 System.out.printf("%s: %s\n",
1128                                                   c.getName(), ie.toString());
1129                                 ret = null;
1130                         } catch (InstantiationException ie) {
1131                                 System.out.printf("%s: %s\n",
1132                                                   c.getName(), ie.toString());
1133                                 ret = null;
1134                         } catch (IllegalAccessException ie) {
1135                                 System.out.printf("%s: %s\n",
1136                                                   c.getName(), ie.toString());
1137                                 ret = null;
1138                         }
1139                 }
1140                 return ret;
1141         }
1142
1143         /* This is the public API for the
1144          * above function which doesn't handle
1145          * inner classes
1146          */
1147         public Object make(Class c) {
1148                 return make(c, null, null);
1149         }
1150
1151         /* Constructors, one for each primitive type, String and Object */
1152         public AltosJson(boolean bool) {
1153                 type = type_boolean;
1154                 this.bool = bool;
1155         }
1156
1157         public AltosJson(byte number) {
1158                 type = type_long;
1159                 this.l_number = number;
1160         }
1161
1162         public AltosJson(char number) {
1163                 type = type_long;
1164                 this.l_number = number;
1165         }
1166
1167         public AltosJson(int number) {
1168                 type = type_long;
1169                 this.l_number = number;
1170         }
1171
1172         public AltosJson(long number) {
1173                 type = type_long;
1174                 this.l_number = number;
1175         }
1176
1177         public AltosJson(double number) {
1178                 type = type_double;
1179                 this.d_number = number;
1180         }
1181
1182         public AltosJson(String string) {
1183                 type = type_string;
1184                 this.string = string;
1185         }
1186
1187         public AltosJson(Object object) {
1188                 if (object instanceof Boolean) {
1189                         type = type_boolean;
1190                         bool = (Boolean) object;
1191                 } else if (object instanceof Byte) {
1192                         type = type_long;
1193                         l_number = (Byte) object;
1194                 } else if (object instanceof Character) {
1195                         type = type_long;
1196                         l_number = (Character) object;
1197                 } else if (object instanceof Integer) {
1198                         type = type_long;
1199                         l_number = (Integer) object;
1200                 } else if (object instanceof Long) {
1201                         type = type_long;
1202                         l_number = (Long) object;
1203                 } else if (object instanceof Double) {
1204                         type = type_double;
1205                         d_number = (Double) object;
1206                 } else if (object instanceof String) {
1207                         type = type_string;
1208                         string = (String) object;
1209                 } else if (object.getClass().isArray()) {
1210                         assert_array(true);
1211
1212                         Class component_class = object.getClass().getComponentType();
1213                         if (component_class == Boolean.TYPE) {
1214                                 boolean[] array = (boolean[]) object;
1215                                 for (int i = 0; i < array.length; i++)
1216                                         put(i, new AltosJson(array[i]));
1217                         } else if (component_class == Byte.TYPE) {
1218                                 byte[] array = (byte[]) object;
1219                                 for (int i = 0; i < array.length; i++)
1220                                         put(i, new AltosJson(array[i]));
1221                         } else if (component_class == Character.TYPE) {
1222                                 char[] array = (char[]) object;
1223                                 for (int i = 0; i < array.length; i++)
1224                                         put(i, new AltosJson(array[i]));
1225                         } else if (component_class == Integer.TYPE) {
1226                                 int[] array = (int[]) object;
1227                                 for (int i = 0; i < array.length; i++)
1228                                         put(i, new AltosJson(array[i]));
1229                         } else if (component_class == Long.TYPE) {
1230                                 long[] array = (long[]) object;
1231                                 for (int i = 0; i < array.length; i++)
1232                                         put(i, new AltosJson(array[i]));
1233                         } else if (component_class == Double.TYPE) {
1234                                 double[] array = (double[]) object;
1235                                 for (int i = 0; i < array.length; i++)
1236                                         put(i, new AltosJson(array[i]));
1237                         } else {
1238                                 Object[] array = (Object[]) object;
1239                                 for (int i = 0; i < array.length; i++)
1240                                         put(i, new AltosJson(array[i]));
1241                         }
1242                 } else {
1243                         assert_hash(true);
1244                         for (Class c = object.getClass(); c != Object.class; c = c.getSuperclass()) {
1245                                 for (Field field : c.getDeclaredFields()) {
1246                                         String  fieldName = field.getName();
1247
1248                                         /* XXX hack to allow fields to be not converted */
1249                                         if (fieldName.startsWith("__"))
1250                                                 continue;
1251
1252                                         /* Skip static fields */
1253                                         if (Modifier.isStatic(field.getModifiers()))
1254                                                 continue;
1255
1256                                         /* Skip synthetic fields. We're assuming
1257                                          * those are always an inner class reference
1258                                          * to the outer class object
1259                                          */
1260                                         if (field.isSynthetic())
1261                                                 continue;
1262                                         try {
1263                                                 /* We may need to force the field to be accessible if
1264                                                  * it is private
1265                                                  */
1266                                                 field.setAccessible(true);
1267                                                 Object val = field.get(object);
1268                                                 if (val != null) {
1269                                                         AltosJson json = new AltosJson(val);
1270                                                         put(fieldName, json);
1271                                                 }
1272                                         } catch (IllegalAccessException ie) {
1273                                                 System.out.printf("%s:%s %s\n",
1274                                                                   c.getName(), fieldName, ie.toString());
1275                                         }
1276                                 }
1277                         }
1278                 }
1279         }
1280
1281         /* Array constructors, one for each primitive type, String and Object */
1282         public AltosJson(boolean[] bools) {
1283                 assert_array(true);
1284                 for(int i = 0; i < bools.length; i++)
1285                         put(i, new AltosJson(bools[i]));
1286         }
1287
1288         public AltosJson(byte[] numbers) {
1289                 assert_array(true);
1290                 for(int i = 0; i < numbers.length; i++)
1291                         put(i, new AltosJson(numbers[i]));
1292         }
1293
1294         public AltosJson(char[] numbers) {
1295                 assert_array(true);
1296                 for(int i = 0; i < numbers.length; i++)
1297                         put(i, new AltosJson(numbers[i]));
1298         }
1299
1300         public AltosJson(int[] numbers) {
1301                 assert_array(true);
1302                 for(int i = 0; i < numbers.length; i++)
1303                         put(i, new AltosJson(numbers[i]));
1304         }
1305
1306         public AltosJson(long[] numbers) {
1307                 assert_array(true);
1308                 for(int i = 0; i < numbers.length; i++)
1309                         put(i, new AltosJson(numbers[i]));
1310         }
1311
1312         public AltosJson(double[] numbers) {
1313                 assert_array(true);
1314                 for(int i = 0; i < numbers.length; i++)
1315                         put(i, new AltosJson(numbers[i]));
1316         }
1317
1318         public AltosJson(String[] strings) {
1319                 assert_array(true);
1320                 for(int i = 0; i < strings.length; i++)
1321                         put(i, new AltosJson(strings[i]));
1322         }
1323
1324         public AltosJson(AltosJson[] jsons) {
1325                 assert_array(true);
1326                 for (int i = 0; i < jsons.length; i++)
1327                         put(i, jsons[i]);
1328         }
1329
1330         public AltosJson(Object[] array) {
1331                 assert_array(true);
1332                 for (int i = 0; i < array.length; i++)
1333                         put(i, new AltosJson(array[i]));
1334         }
1335
1336         /* Empty constructor
1337          */
1338         public AltosJson() {
1339                 type = type_none;
1340         }
1341
1342         public AltosJson(JsonHash hash) {
1343                 type = type_hash;
1344                 this.hash = hash;
1345         }
1346
1347         public AltosJson(JsonArray array) {
1348                 type = type_array;
1349                 this.array = array;
1350         }
1351 }