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