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