--- /dev/null
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altoslib_11;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.*;
+import java.lang.reflect.*;
+
+class JsonUtil {
+ StringBuffer quote(StringBuffer result, String a) {
+ result.append("\"");
+ for (int i = 0; i < a.length(); i++) {
+ char c = a.charAt(i);
+
+ switch (c) {
+ case '"':
+ case '\\':
+ result.append('\\').append(c);
+ break;
+ case '\n':
+ result.append("\\n");
+ break;
+ default:
+ result.append(c);
+ break;
+ }
+ }
+ result.append("\"");
+ return result;
+ }
+
+ StringBuffer append(StringBuffer result, AltosJson value, int indent, boolean pretty) {
+ value.append(result, indent, pretty);
+ return result;
+ }
+
+ StringBuffer append(StringBuffer result, String string) {
+ result.append(string);
+ return result;
+ }
+
+ StringBuffer indent(StringBuffer result, int indent) {
+ result.append("\n");
+ for (int i = 0; i < indent; i++)
+ result.append("\t");
+ return result;
+ }
+ static NumberFormat get_nf_json() {
+ DecimalFormat nf = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ROOT);
+ nf.setParseIntegerOnly(false);
+ nf.setGroupingUsed(false);
+ nf.setMaximumFractionDigits(17);
+ nf.setMinimumFractionDigits(0);
+ nf.setMinimumIntegerDigits(1);
+ nf.setDecimalSeparatorAlwaysShown(false);
+ return nf;
+ }
+
+ static NumberFormat nf_json = get_nf_json();
+}
+
+class JsonHash extends JsonUtil {
+ Hashtable<String,AltosJson> hash;
+
+ void append_hash(StringBuffer result, int indent, boolean pretty) {
+ boolean first = true;
+
+ result.append("{");
+
+ ArrayList<String> key_list = new ArrayList<String>(hash.keySet());
+
+ Collections.sort(key_list, new Comparator<String>() {
+ @Override
+ public int compare(String a, String b) { return a.compareTo(b); }
+ });
+
+ for (String key : key_list) {
+ AltosJson value = hash.get(key);
+
+ if (!first)
+ result.append(",");
+ first = false;
+ if (pretty)
+ indent(result, indent+1);
+ quote(result, key);
+ append(result, ": ");
+ append(result, value, indent+1, pretty);
+ }
+ if (pretty)
+ indent(result, indent);
+ append(result, "}");
+ }
+
+ void put(String key, AltosJson value) {
+ hash.put(key, value);
+ }
+
+ AltosJson get(String key) {
+ return hash.get(key);
+ }
+
+ JsonHash() {
+ hash = new Hashtable<String,AltosJson>();
+ }
+}
+
+class JsonArray extends JsonUtil {
+ ArrayList<AltosJson> array;
+
+ void append_array(StringBuffer result, int indent, boolean pretty) {
+ boolean first = true;
+
+ append(result, "[");
+ for (int i = 0; i < array.size(); i++) {
+ AltosJson value = array.get(i);
+
+ if (!first)
+ append(result, ",");
+ first = false;
+ if (pretty)
+ indent(result, indent+1);
+ append(result, value, indent+1, pretty);
+ }
+ if (pretty)
+ indent(result, indent);
+ append(result, "]");
+ }
+
+ void put(int index, AltosJson value) {
+ if (index >= array.size())
+ array.add(index, value);
+ else
+ array.set(index, value);
+ }
+
+ AltosJson get(int index) {
+ if (index < 0 || index > array.size())
+ return null;
+ return array.get(index);
+ }
+
+ int size() {
+ return array.size();
+ }
+
+ JsonArray() {
+ array = new ArrayList<AltosJson>();
+ }
+}
+
+class JsonToken {
+ double dval;
+ long lval;
+ String sval;
+ boolean bval;
+ int token;
+
+ static final int _string = 0;
+ static final int _double = 1;
+ static final int _long = 2;
+ static final int _boolean = 3;
+ static final int _oc = 4;
+ static final int _cc = 5;
+ static final int _os = 6;
+ static final int _cs = 7;
+ static final int _comma = 8;
+ static final int _colon = 9;
+ static final int _end = 10;
+ static final int _error = 11;
+
+ static String token_name(int token) {
+ switch (token) {
+ case _string:
+ return "string";
+ case _double:
+ return "number";
+ case _long:
+ return "number";
+ case _boolean:
+ return "boolean";
+ case _oc:
+ return "{";
+ case _cc:
+ return "}";
+ case _os:
+ return "[";
+ case _cs:
+ return "]";
+ case _comma:
+ return ",";
+ case _colon:
+ return ":";
+ case _end:
+ return "<EOF>";
+ case _error:
+ return "<ERROR>";
+ default:
+ return "<UNKNOWN>";
+ }
+ }
+
+ String token_name() {
+ return token_name(token);
+ }
+
+ JsonToken(int token) {
+ this.token = token;
+ }
+
+ JsonToken(int token, boolean bval) {
+ this.token = token;
+ this.bval = bval;
+ }
+
+ JsonToken(int token, double dval) {
+ this.token = token;
+ this.dval = dval;
+ }
+
+ JsonToken(int token, long lval) {
+ this.token = token;
+ this.lval = lval;
+ }
+
+ JsonToken(int token, String sval) {
+ this.token = token;
+ this.sval = sval;
+ }
+
+ JsonToken(int token, StringBuffer bval) {
+ this(token, bval.toString());
+ }
+}
+
+/*
+ * Lexer for json
+ */
+class JsonLexer extends JsonUtil {
+ StringReader f;
+ int line;
+ int ungot = -2;
+ StringBuffer pending_token;
+ JsonToken token;
+
+ static class keyword {
+ String word;
+ JsonToken token;
+
+ JsonToken match(String value) {
+ if (word.equals(value))
+ return token;
+ return null;
+ }
+
+ keyword(String word, JsonToken token) {
+ this.word = word;
+ this.token = token;
+ }
+ }
+
+ /* boolean values are the only keywords in json
+ */
+ static keyword[] keywords = {
+ new keyword("true", new JsonToken(JsonToken._boolean, true)),
+ new keyword("false", new JsonToken(JsonToken._boolean, false)),
+ };
+
+ static JsonToken keyword(String word) {
+ for (int i = 0; i < keywords.length; i++) {
+ JsonToken token = keywords[i].match(word);
+ if (token != null)
+ return token;
+ }
+ return null;
+ }
+
+ /* Get the next char (-1 for EOF) */
+ int ch() throws IOException {
+ int c;
+ if (ungot != -2) {
+ c = ungot;
+ ungot = -2;
+ } else
+ c = f.read();
+ if (c != -1)
+ pending_token.append((char) c);
+ if (c == '\n')
+ ++line;
+ return c;
+ }
+
+ void unch(int c) {
+ if (ungot != -2)
+ throw new IllegalArgumentException("ungot buffer full");
+ pending_token.deleteCharAt( pending_token.length()-1);
+ if (c == '\n')
+ --line;
+ ungot = c;
+ }
+
+ String last_token_string() {
+ if (pending_token == null)
+ return null;
+
+ return pending_token.toString();
+ }
+
+ static boolean is_long_range(double d) {
+ return -9223372036854775808.0 <= d && d <= 9223372036854775807.0;
+ }
+
+ JsonToken lex() {
+ pending_token = new StringBuffer();
+
+ try {
+ for (;;) {
+ int c = ch();
+
+ switch (c) {
+ case -1:
+ return new JsonToken(JsonToken._end);
+ case '\n':
+ case ' ':
+ case '\t':
+ continue;
+ case '{':
+ return new JsonToken(JsonToken._oc);
+ case '}':
+ return new JsonToken(JsonToken._cc);
+ case '[':
+ return new JsonToken(JsonToken._os);
+ case ']':
+ return new JsonToken(JsonToken._cs);
+ case ',':
+ return new JsonToken(JsonToken._comma);
+ case ':':
+ return new JsonToken(JsonToken._colon);
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.': case '-': case '+':
+ StringBuffer dbuf = new StringBuffer();
+ boolean is_double = false;
+ while (Character.isDigit(c) || c == '.' || c == '+' || c == '-' || c == 'e' || c == 'E') {
+ if (c == '.' || c == 'E')
+ is_double = true;
+ dbuf.appendCodePoint(c);
+ c = ch();
+ }
+ unch(c);
+ String dstr = dbuf.toString();
+ double dval;
+ try {
+ dval = nf_json.parse(dstr).doubleValue();
+ } catch (ParseException pe) {
+ return new JsonToken(JsonToken._error, dstr);
+ }
+ if (is_double || !is_long_range(dval))
+ return new JsonToken(JsonToken._double, dval);
+ else {
+ long lval = Long.parseLong(dstr);
+ return new JsonToken(JsonToken._long, lval);
+ }
+ case '"':
+ StringBuffer bval = new StringBuffer();
+ for (;;) {
+ c = ch();
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ c = ch();
+ switch (c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ default:
+ break;
+ }
+ }
+ bval.appendCodePoint(c);
+ }
+ return new JsonToken(JsonToken._string, bval);
+ default:
+ if (Character.isLetter(c)) {
+ StringBuffer tbuf = new StringBuffer();
+ do {
+ tbuf.appendCodePoint(c);
+ c = ch();
+ } while (Character.isLetter(c));
+ unch(c);
+ JsonToken token = keyword(tbuf.toString());
+ if (token != null)
+ return token;
+ }
+ break;
+ }
+ }
+ } catch (IOException ie) {
+ return new JsonToken(JsonToken._error, "<EIO>");
+ }
+ }
+
+ void next() {
+ token = lex();
+ }
+
+ JsonToken expect(int e) {
+ JsonToken t = token;
+ if (t.token != e)
+ throw new IllegalArgumentException(String.format("got \"%s\" while expecting \"%s\"",
+ token.token_name(),
+ JsonToken.token_name(e)));
+ next();
+ return t;
+ }
+
+ JsonLexer(String s) {
+ f = new StringReader(s);
+ line = 1;
+ token = null;
+ }
+}
+
+/*
+ * Parse a json string into a AltosJson object
+ */
+class JsonParse {
+ JsonLexer lexer;
+
+ void parse_error(String format, Object ... arguments) {
+ throw new IllegalArgumentException(String.format("line %d: JSON parse error %s\n",
+ lexer.line,
+ String.format(format, arguments)));
+ }
+
+ /* Hashes are { string: value ... } */
+ JsonHash hash() {
+ JsonHash hash = new JsonHash();
+
+ /* skip the open brace */
+ lexer.next();
+ for (;;) {
+ /* Allow for empty hashes */
+ if (lexer.token.token == JsonToken._cc) {
+ lexer.next();
+ return hash;
+ }
+
+ /* string : value */
+ String key = lexer.expect(JsonToken._string).sval;
+ lexer.expect(JsonToken._colon);
+ AltosJson value = value();
+ hash.put(key, value);
+
+ switch (lexer.token.token) {
+ case JsonToken._comma:
+ lexer.next();
+ break;
+ case JsonToken._cc:
+ lexer.next();
+ return hash;
+ default:
+ parse_error("got %s expect \",\" or \"}\"", lexer.token.token_name());
+ return null;
+ }
+ }
+ }
+
+ /* Arrays are [ value ... ] */
+ JsonArray array() {
+ JsonArray array = new JsonArray();
+
+ lexer.next();
+ for (int i = 0;; i++) {
+ /* Allow for empty arrays */
+ if (lexer.token.token == JsonToken._cs) {
+ lexer.next();
+ return array;
+ }
+
+ AltosJson value = value();
+ array.put(i, value);
+ switch (lexer.token.token) {
+ case JsonToken._comma:
+ lexer.next();
+ break;
+ case JsonToken._cs:
+ lexer.next();
+ return array;
+ default:
+ parse_error("got %s expect \",\" or \"]\"", lexer.token.token_name());
+ return null;
+ }
+ }
+ }
+
+ /* Json is a simple LL language; one token is sufficient to
+ * identify the next object in the input
+ */
+ AltosJson value() {
+ switch (lexer.token.token) {
+ case JsonToken._oc:
+ return new AltosJson(hash());
+ case JsonToken._os:
+ return new AltosJson(array());
+ case JsonToken._double:
+ double dval = lexer.token.dval;
+ lexer.next();
+ return new AltosJson(dval);
+ case JsonToken._long:
+ long lval = lexer.token.lval;
+ lexer.next();
+ return new AltosJson(lval);
+ case JsonToken._string:
+ String sval = lexer.token.sval;
+ lexer.next();
+ return new AltosJson(sval);
+ case JsonToken._boolean:
+ boolean bval = lexer.token.bval;
+ lexer.next();
+ return new AltosJson(bval);
+ default:
+ parse_error("Unexpected token \"%s\"", lexer.token.token_name());
+ }
+ return null;
+ }
+
+ AltosJson parse() {
+ lexer.next();
+ return value();
+ }
+
+ JsonParse(String s) {
+ lexer = new JsonLexer(s);
+ }
+}
+
+public class AltosJson extends JsonUtil {
+ private static final int type_none = 0;
+ private static final int type_hash = 1;
+ private static final int type_array = 2;
+ private static final int type_double = 3;
+ private static final int type_long = 4;
+ private static final int type_string = 5;
+ private static final int type_boolean = 6;
+
+ private int type;
+
+ private JsonHash hash;
+ private JsonArray array;
+ private double d_number;
+ private long l_number;
+ private String string;
+ private boolean bool;
+
+ /* Generate string representation of the value
+ */
+ StringBuffer append(StringBuffer result, int indent, boolean pretty) {
+ switch (type) {
+ case type_hash:
+ hash.append_hash(result, indent, pretty);
+ break;
+ case type_array:
+ array.append_array(result, indent, pretty);
+ break;
+ case type_double:
+ String dval = nf_json.format(d_number);
+ if (dval.equals("-0"))
+ dval = "0";
+ result.append(dval);
+ break;
+ case type_long:
+ result.append(new Long(l_number).toString());
+ break;
+ case type_string:
+ quote(result, string);
+ break;
+ case type_boolean:
+ result.append(bool ? "true" : "false");
+ break;
+ }
+ return result;
+ }
+
+ private String toString(int indent, boolean pretty) {
+ StringBuffer result = new StringBuffer();
+ append(result, indent, pretty);
+ return result.toString();
+ }
+
+ public String toString() {
+ return toString(0, false);
+ }
+
+ public String toPrettyString() {
+ return toString(0, true);
+ }
+
+ /* Parse string representation to a value
+ */
+
+ public static AltosJson fromString(String string) {
+ JsonParse parse = new JsonParse(string);
+ try {
+ return parse.parse();
+ } catch (IllegalArgumentException ie) {
+ System.out.printf("json:\n%s\n%s\n", string, ie.getMessage());
+ return null;
+ }
+ }
+
+ /* Accessor functions
+ */
+ private boolean assert_type(boolean setting, int type, int other_type, String error) {
+ if (setting && this.type == type_none) {
+ this.type = type;
+ return false;
+ }
+ if (this.type != type && this.type != other_type)
+ throw new IllegalArgumentException(error);
+ return true;
+ }
+
+ private boolean assert_type(boolean setting, int type, String error) {
+ return assert_type(setting, type, type, error);
+ }
+
+ private void assert_hash(boolean setting) {
+ if (!assert_type(setting, type_hash, "not a hash"))
+ hash = new JsonHash();
+ }
+
+ private void assert_array(boolean setting) {
+ if (!assert_type(setting, type_array, "not an array"))
+ array = new JsonArray();
+ }
+
+ private void assert_number() {
+ assert_type(false, type_double, type_long, "not a number");
+ }
+
+ private void assert_double() {
+ assert_type(true, type_double, type_long, "not a number");
+ }
+
+ private void assert_long() {
+ assert_type(true, type_long, type_double, "not a number");
+ }
+
+ private void assert_string(boolean setting) {
+ assert_type(setting, type_string, "not a string");
+ }
+
+ private void assert_boolean(boolean setting) {
+ assert_type(setting, type_boolean, "not a boolean");
+ }
+
+ /* Primitive accessors
+ */
+ public double number() {
+ assert_number();
+ if (type == type_double)
+ return d_number;
+ else
+ return (double) l_number;
+ }
+
+ public long l_number() {
+ assert_number();
+ if (type == type_double)
+ return (long) d_number;
+ else
+ return l_number;
+ }
+
+ public String string() {
+ assert_string(false);
+ return string;
+ }
+
+ public boolean bool() {
+ assert_boolean(false);
+ return bool;
+ }
+
+ public AltosJson get(int index) {
+ assert_array(false);
+ return array.get(index);
+ }
+
+ public AltosJson get(String key) {
+ assert_hash(false);
+ return hash.get(key);
+ }
+
+ public int size() {
+ assert_array(false);
+ return array.size();
+ }
+
+ /* Typed accessors with defaulting
+ */
+ public double get_double(String key, double def) {
+ AltosJson value = get(key);
+ if (value != null) {
+ return value.number();
+ }
+ return def;
+ }
+
+ public long get_long(String key, long def) {
+ AltosJson value = get(key);
+ if (value != null)
+ return value.l_number();
+ return def;
+ }
+
+ public int get_int(String key, int def) {
+ AltosJson value = get(key);
+ if (value != null)
+ return (int) value.l_number();
+ return def;
+ }
+
+ public String get_string(String key, String def) {
+ AltosJson value = get(key);
+ if (value != null)
+ return value.string();
+ return def;
+ }
+
+ public boolean get_boolean(String key, boolean def) {
+ AltosJson value = get(key);
+ if (value != null)
+ return value.bool();
+ return def;
+ }
+
+ public double get_double(int index, double def) {
+ AltosJson value = get(index);
+ if (value != null)
+ return value.number();
+ return def;
+ }
+
+ public long get_long(int index, long def) {
+ AltosJson value = get(index);
+ if (value != null)
+ return value.l_number();
+ return def;
+ }
+
+ public int get_int(int index, int def) {
+ AltosJson value = get(index);
+ if (value != null)
+ return (int) value.l_number();
+ return def;
+ }
+
+ public String get_string(int index, String def) {
+ AltosJson value = get(index);
+ if (value != null)
+ return value.string();
+ return def;
+ }
+
+ public boolean get_boolean(int index, boolean def) {
+ AltosJson value = get(index);
+ if (value != null)
+ return value.bool();
+ return def;
+ }
+
+ public double[] get_double_array(String key, double[] def) {
+ AltosJson value = get(key);
+ if (value != null) {
+ double[] ret = new double[value.size()];
+ for (int i = 0; i < value.size(); i++)
+ ret[i] = value.get_double(i, def == null ? 0 : def[i]);
+ return ret;
+ }
+ return def;
+ }
+
+ public int[] get_int_array(String key, int[] def) {
+ AltosJson value = get(key);
+ if (value != null) {
+ int[] ret = new int[value.size()];
+ for (int i = 0; i < value.size(); i++)
+ ret[i] = value.get_int(i, def == null ? 0 : def[i]);
+ return ret;
+ }
+ return def;
+ }
+
+ /* Array setter functions
+ */
+ public AltosJson put(int index, AltosJson value) {
+ assert_array(true);
+ array.put(index, value);
+ return value;
+ }
+
+ public Object put(int index, Object value) {
+ assert_array(true);
+ if (value != null)
+ array.put(index, new AltosJson(value));
+ return value;
+ }
+
+ public double put(int index, double value) {
+ assert_array(true);
+ array.put(index, new AltosJson(value));
+ return value;
+ }
+
+ public AltosJson put(int index, double[] value) {
+ if (value != null) {
+ assert_array(true);
+ array.put(index, new AltosJson(value));
+ }
+ return this;
+ }
+
+ public int[] put(int index, int[] value) {
+ if (value != null) {
+ assert_array(true);
+ array.put(index, new AltosJson(value));
+ }
+ return value;
+ }
+
+ public String put(int index, String value) {
+ if (value != null) {
+ assert_array(true);
+ array.put(index, new AltosJson(value));
+ }
+ return value;
+ }
+
+ public boolean put(int index, boolean value) {
+ assert_array(true);
+ array.put(index, new AltosJson(value));
+ return value;
+ }
+
+ /* Hash setter functions
+ */
+ public AltosJson put(String key, AltosJson value) {
+ assert_hash(true);
+ hash.put(key, value);
+ return value;
+ }
+
+ public Object put(String key, Object value) {
+ assert_hash(true);
+ if (value != null)
+ hash.put(key, new AltosJson(value));
+ return value;
+ }
+
+ public double put(String key, double value) {
+ assert_hash(true);
+ hash.put(key, new AltosJson(value));
+ return value;
+ }
+
+ public String put(String key, String value) {
+ if (value != null) {
+ assert_hash(true);
+ hash.put(key, new AltosJson(value));
+ }
+ return value;
+ }
+
+ public boolean put(String key, boolean value) {
+ assert_hash(true);
+ hash.put(key, new AltosJson(value));
+ return value;
+ }
+
+ public AltosJson[] put(String key, AltosJson[] value) {
+ if (value != null) {
+ assert_hash(true);
+ hash.put(key, new AltosJson(value));
+ }
+ return value;
+ }
+
+ public double[] put(String key, double[] value) {
+ if (value != null) {
+ assert_hash(true);
+ hash.put(key, new AltosJson(value));
+ }
+ return value;
+ }
+
+ public int[] put(String key, int[] value) {
+ if (value != null) {
+ assert_hash(true);
+ hash.put(key, new AltosJson(value));
+ }
+ return value;
+ }
+
+ /* Primitive setter functions
+ */
+ public double put(double value) {
+ assert_double();
+ d_number = value;
+ return value;
+ }
+
+ public byte put(byte value) {
+ assert_long();
+ l_number = value;
+ return value;
+ }
+
+ public char put(char value) {
+ assert_long();
+ l_number = value;
+ return value;
+ }
+
+ public int put(int value) {
+ assert_long();
+ l_number = value;
+ return value;
+ }
+
+ public long put(long value) {
+ assert_long();
+ l_number = value;
+ return value;
+ }
+
+ public String put(String value) {
+ assert_string(true);
+ string = value;
+ return value;
+ }
+
+ public boolean put(boolean value) {
+ assert_boolean(true);
+ bool = value;
+ return value;
+ }
+
+ private boolean isInnerClass(Class c) {
+ for (Field field : c.getDeclaredFields())
+ if (field.isSynthetic())
+ return true;
+ return false;
+ }
+
+ /* Construct an object of the specified class from the JSON
+ * representation.
+ *
+ * This works as long as the structure is non-recursive, and
+ * all inner classes are only members of their immediate outer
+ * class
+ */
+ private Object make(Class c, Class enclosing_class, Object enclosing_object) {
+ Object ret;
+ if (c == Boolean.TYPE) {
+ ret = bool();
+ } else if (c == Byte.TYPE) {
+ ret = (Byte) (byte) l_number();
+ } else if (c == Character.TYPE) {
+ ret = (Character) (char) l_number();
+ } else if (c == Integer.TYPE) {
+ ret = (Integer) (int) l_number();
+ } else if (c == Long.TYPE) {
+ ret = l_number();
+ } else if (c == Double.TYPE) {
+ ret = number();
+ } else if (c == String.class) {
+ ret = string();
+ } else if (c.isArray()) {
+ assert_array(false);
+
+ Class element_class = c.getComponentType();
+ if (element_class == Double.TYPE) {
+ double[] array = (double[]) Array.newInstance(element_class, size());
+ for (int i = 0; i < array.length; i++)
+ array[i] = (Double) get(i).make(element_class);
+ ret = array;
+ } else {
+ Object[] array = (Object[]) Array.newInstance(element_class, size());
+ for (int i = 0; i < array.length; i++)
+ array[i] = get(i).make(element_class);
+ ret = array;
+ }
+ } else {
+ assert_hash(false);
+ Object object = null;
+ try {
+ /* Inner classes have a hidden extra parameter
+ * to the constructor. Assume that the enclosing object is
+ * of the enclosing class and construct the object
+ * based on that.
+ */
+ if (enclosing_class != null && isInnerClass(c)) {
+ Constructor<?> ctor = ((Class<?>)c).getDeclaredConstructor((Class<?>) enclosing_class);
+ object = ctor.newInstance(enclosing_object);
+ } else {
+ object = c.newInstance();
+ }
+ for (; c != null; c = c.getSuperclass()) {
+ for (Field field : c.getDeclaredFields()) {
+ String fieldName = field.getName();
+ Class fieldClass = field.getType();
+ String className = fieldClass.getName();
+
+ if (Modifier.isStatic(field.getModifiers()))
+ continue;
+ if (field.isSynthetic())
+ continue;
+ try {
+ AltosJson json = get(fieldName);
+ if (json != null) {
+ Object val = json.make(fieldClass, c, object);
+ field.setAccessible(true);
+ field.set(object, val);
+ }
+ } catch (IllegalAccessException ie) {
+ }
+ }
+ }
+ ret = object;
+ } catch (InvocationTargetException ie) {
+ ret = null;
+ } catch (NoSuchMethodException ie) {
+ ret = null;
+ } catch (InstantiationException ie) {
+ ret = null;
+ } catch (IllegalAccessException ie) {
+ ret = null;
+ }
+ }
+ return ret;
+ }
+
+ /* This is the public API for the
+ * above function which doesn't handle
+ * inner classes
+ */
+ public Object make(Class c) {
+ return make(c, null, null);
+ }
+
+ /* Constructors, one for each primitive type, String and Object */
+ public AltosJson(boolean bool) {
+ type = type_boolean;
+ this.bool = bool;
+ }
+
+ public AltosJson(byte number) {
+ type = type_long;
+ this.l_number = number;
+ }
+
+ public AltosJson(char number) {
+ type = type_long;
+ this.l_number = number;
+ }
+
+ public AltosJson(int number) {
+ type = type_long;
+ this.l_number = number;
+ }
+
+ public AltosJson(long number) {
+ type = type_long;
+ this.l_number = number;
+ }
+
+ public AltosJson(double number) {
+ type = type_double;
+ this.d_number = number;
+ }
+
+ public AltosJson(String string) {
+ type = type_string;
+ this.string = string;
+ }
+
+ public AltosJson(Object object) {
+ if (object instanceof Boolean) {
+ type = type_boolean;
+ bool = (Boolean) object;
+ } else if (object instanceof Byte) {
+ type = type_long;
+ l_number = (Byte) object;
+ } else if (object instanceof Character) {
+ type = type_long;
+ l_number = (Character) object;
+ } else if (object instanceof Integer) {
+ type = type_long;
+ l_number = (Integer) object;
+ } else if (object instanceof Long) {
+ type = type_long;
+ l_number = (Long) object;
+ } else if (object instanceof Double) {
+ type = type_double;
+ d_number = (Double) object;
+ } else if (object instanceof String) {
+ type = type_string;
+ string = (String) object;
+ } else if (object.getClass().isArray()) {
+ assert_array(true);
+
+ Class component_class = object.getClass().getComponentType();
+ if (component_class == Boolean.TYPE) {
+ boolean[] array = (boolean[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ } else if (component_class == Byte.TYPE) {
+ byte[] array = (byte[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ } else if (component_class == Character.TYPE) {
+ char[] array = (char[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ } else if (component_class == Integer.TYPE) {
+ int[] array = (int[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ } else if (component_class == Long.TYPE) {
+ long[] array = (long[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ } else if (component_class == Double.TYPE) {
+ double[] array = (double[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ } else {
+ Object[] array = (Object[]) object;
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ }
+ } else {
+ assert_hash(true);
+ for (Class c = object.getClass(); c != null; c = c.getSuperclass()) {
+ for (Field field : c.getDeclaredFields()) {
+ String fieldName = field.getName();
+ Class fieldClass = field.getType();
+ String className = fieldClass.getName();
+
+ /* Skip static fields */
+ if (Modifier.isStatic(field.getModifiers()))
+ continue;
+
+ /* Skip synthetic fields. We're assuming
+ * those are always an inner class reference
+ * to the outer class object
+ */
+ if (field.isSynthetic())
+ continue;
+ try {
+ /* We may need to force the field to be accessible if
+ * it is private
+ */
+ field.setAccessible(true);
+ Object val = field.get(object);
+ if (val != null) {
+ AltosJson json = new AltosJson(val);
+ put(fieldName, json);
+ }
+ } catch (IllegalAccessException ie) {
+ }
+ }
+ }
+ }
+ }
+
+ /* Array constructors, one for each primitive type, String and Object */
+ public AltosJson(boolean[] bools) {
+ assert_array(true);
+ for(int i = 0; i < bools.length; i++)
+ put(i, new AltosJson(bools[i]));
+ }
+
+ public AltosJson(byte[] numbers) {
+ assert_array(true);
+ for(int i = 0; i < numbers.length; i++)
+ put(i, new AltosJson(numbers[i]));
+ }
+
+ public AltosJson(char[] numbers) {
+ assert_array(true);
+ for(int i = 0; i < numbers.length; i++)
+ put(i, new AltosJson(numbers[i]));
+ }
+
+ public AltosJson(int[] numbers) {
+ assert_array(true);
+ for(int i = 0; i < numbers.length; i++)
+ put(i, new AltosJson(numbers[i]));
+ }
+
+ public AltosJson(long[] numbers) {
+ assert_array(true);
+ for(int i = 0; i < numbers.length; i++)
+ put(i, new AltosJson(numbers[i]));
+ }
+
+ public AltosJson(double[] numbers) {
+ assert_array(true);
+ for(int i = 0; i < numbers.length; i++)
+ put(i, new AltosJson(numbers[i]));
+ }
+
+ public AltosJson(String[] strings) {
+ assert_array(true);
+ for(int i = 0; i < strings.length; i++)
+ put(i, new AltosJson(strings[i]));
+ }
+
+ public AltosJson(AltosJson[] jsons) {
+ assert_array(true);
+ for (int i = 0; i < jsons.length; i++)
+ put(i, jsons[i]);
+ }
+
+ public AltosJson(Object[] array) {
+ assert_array(true);
+ for (int i = 0; i < array.length; i++)
+ put(i, new AltosJson(array[i]));
+ }
+
+ /* Empty constructor
+ */
+ public AltosJson() {
+ type = type_none;
+ }
+
+ public AltosJson(JsonHash hash) {
+ type = type_hash;
+ this.hash = hash;
+ }
+
+ public AltosJson(JsonArray array) {
+ type = type_array;
+ this.array = array;
+ }
+}