altoslib: Create data file open helper in AltosLib
[fw/altos] / altoslib / AltosJson.java
index 6ae7e7dc99de1278f8529bbc64d7d4553d5b8589..ce50b872f13c6c4bc9b68ccd9dcff4b352396171 100644 (file)
@@ -3,7 +3,8 @@
  *
  * 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.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -24,39 +25,39 @@ import java.lang.*;
 import java.lang.reflect.*;
 
 class JsonUtil {
-       StringBuffer quote(StringBuffer result, String a) {
-               result.append("\"");
+       Writer quote(Writer writer, String a) throws IOException {
+               writer.append("\"");
                for (int i = 0; i < a.length(); i++) {
                        char c = a.charAt(i);
 
                        switch (c) {
                        case '"':
                        case '\\':
-                               result.append('\\').append(c);
+                               writer.append('\\').append(c);
                                break;
                        case '\n':
-                               result.append("\\n");
+                               writer.append("\\n");
                                break;
                        default:
-                               result.append(c);
+                               writer.append(c);
                                break;
                        }
                }
-               result.append("\"");
-               return result;
+               writer.append("\"");
+               return writer;
        }
 
-       StringBuffer append(StringBuffer result, AltosJson value, int indent, boolean pretty) {
+       Writer append(Writer result, AltosJson value, int indent, boolean pretty) throws IOException {
                value.append(result, indent, pretty);
                return result;
        }
 
-       StringBuffer append(StringBuffer result, String string) {
+       Writer append(Writer result, String string) throws IOException {
                result.append(string);
                return result;
        }
 
-       StringBuffer indent(StringBuffer result, int indent) {
+       Writer indent(Writer result, int indent) throws IOException {
                result.append("\n");
                for (int i = 0; i < indent; i++)
                        result.append("\t");
@@ -79,7 +80,7 @@ class JsonUtil {
 class JsonHash extends JsonUtil {
        Hashtable<String,AltosJson> hash;
 
-       void append_hash(StringBuffer result, int indent, boolean pretty) {
+       void append_hash(Writer result, int indent, boolean pretty) throws IOException {
                boolean         first = true;
 
                result.append("{");
@@ -124,7 +125,7 @@ class JsonHash extends JsonUtil {
 class JsonArray extends JsonUtil {
        ArrayList<AltosJson> array;
 
-       void append_array(StringBuffer result, int indent, boolean pretty) {
+       void append_array(Writer result, int indent, boolean pretty) throws IOException {
                boolean first = true;
 
                append(result, "[");
@@ -184,6 +185,7 @@ class JsonToken {
        static final int _colon = 9;
        static final int _end = 10;
        static final int _error = 11;
+       static final int _none = 12;
 
        static String token_name(int token) {
                switch (token) {
@@ -244,7 +246,7 @@ class JsonToken {
                this.sval = sval;
        }
 
-       JsonToken(int token, StringBuffer bval) {
+       JsonToken(int token, Writer bval) {
                this(token, bval.toString());
        }
 }
@@ -253,11 +255,11 @@ class JsonToken {
  * Lexer for json
  */
 class JsonLexer extends JsonUtil {
-       StringReader    f;
-       int             line;
-       int             ungot = -2;
-       StringBuffer    pending_token;
-       JsonToken       token;
+       InputStream             f;
+       int                     line;
+       int                     ungot = -2;
+       StringBuffer            pending_token;
+       private JsonToken       token;
 
        static class keyword {
                String          word;
@@ -280,6 +282,9 @@ class JsonLexer extends JsonUtil {
        static keyword[] keywords = {
                new keyword("true", new JsonToken(JsonToken._boolean, true)),
                new keyword("false", new JsonToken(JsonToken._boolean, false)),
+               new keyword("NegInfinity", new JsonToken(JsonToken._double, Double.NEGATIVE_INFINITY)),
+               new keyword("Infinity", new JsonToken(JsonToken._double, Double.POSITIVE_INFINITY)),
+               new keyword("NaN", new JsonToken(JsonToken._double, Double.NaN))
        };
 
        static JsonToken keyword(String word) {
@@ -378,7 +383,7 @@ class JsonLexer extends JsonUtil {
                                                return new JsonToken(JsonToken._long, lval);
                                        }
                                case '"':
-                                       StringBuffer bval = new StringBuffer();
+                                       Writer bval = new StringWriter();
                                        for (;;) {
                                                c = ch();
                                                if (c == '"')
@@ -396,7 +401,7 @@ class JsonLexer extends JsonUtil {
                                                                break;
                                                        }
                                                }
-                                               bval.appendCodePoint(c);
+                                               bval.write(c);
                                        }
                                        return new JsonToken(JsonToken._string, bval);
                                default:
@@ -420,11 +425,17 @@ class JsonLexer extends JsonUtil {
        }
 
        void next() {
-               token = lex();
+               token = null;
+       }
+
+       JsonToken token() {
+               if (token == null)
+                       token = lex();
+               return token;
        }
 
        JsonToken expect(int e) {
-               JsonToken t = token;
+               JsonToken t = token();
                if (t.token != e)
                        throw new IllegalArgumentException(String.format("got \"%s\" while expecting \"%s\"",
                                                                         token.token_name(),
@@ -434,7 +445,13 @@ class JsonLexer extends JsonUtil {
        }
 
        JsonLexer(String s) {
-               f = new StringReader(s);
+               f = new AltosStringInputStream(s);
+               line = 1;
+               token = null;
+       }
+
+       JsonLexer(InputStream f) {
+               this.f = f;
                line = 1;
                token = null;
        }
@@ -460,7 +477,7 @@ class JsonParse {
                lexer.next();
                for (;;) {
                        /* Allow for empty hashes */
-                       if (lexer.token.token == JsonToken._cc) {
+                       if (lexer.token().token == JsonToken._cc) {
                                lexer.next();
                                return hash;
                        }
@@ -471,7 +488,7 @@ class JsonParse {
                        AltosJson value = value();
                        hash.put(key, value);
 
-                       switch (lexer.token.token) {
+                       switch (lexer.token().token) {
                        case JsonToken._comma:
                                lexer.next();
                                break;
@@ -479,7 +496,7 @@ class JsonParse {
                                lexer.next();
                                return hash;
                        default:
-                               parse_error("got %s expect \",\" or \"}\"", lexer.token.token_name());
+                               parse_error("got %s expect \",\" or \"}\"", lexer.token().token_name());
                                return null;
                        }
                }
@@ -492,14 +509,14 @@ class JsonParse {
                lexer.next();
                for (int i = 0;; i++) {
                        /* Allow for empty arrays */
-                       if (lexer.token.token == JsonToken._cs) {
+                       if (lexer.token().token == JsonToken._cs) {
                                lexer.next();
                                return array;
                        }
 
                        AltosJson value = value();
                        array.put(i, value);
-                       switch (lexer.token.token) {
+                       switch (lexer.token().token) {
                        case JsonToken._comma:
                                lexer.next();
                                break;
@@ -507,7 +524,7 @@ class JsonParse {
                                lexer.next();
                                return array;
                        default:
-                               parse_error("got %s expect \",\" or \"]\"", lexer.token.token_name());
+                               parse_error("got %s expect \",\" or \"]\"", lexer.token().token_name());
                                return null;
                        }
                }
@@ -517,29 +534,29 @@ class JsonParse {
         * identify the next object in the input
         */
        AltosJson value() {
-               switch (lexer.token.token) {
+               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;
+                       double dval = lexer.token().dval;
                        lexer.next();
                        return new AltosJson(dval);
                case JsonToken._long:
-                       long lval = lexer.token.lval;
+                       long lval = lexer.token().lval;
                        lexer.next();
                        return new AltosJson(lval);
                case JsonToken._string:
-                       String sval = lexer.token.sval;
+                       String sval = lexer.token().sval;
                        lexer.next();
                        return new AltosJson(sval);
                case JsonToken._boolean:
-                       boolean bval = lexer.token.bval;
+                       boolean bval = lexer.token().bval;
                        lexer.next();
                        return new AltosJson(bval);
                default:
-                       parse_error("Unexpected token \"%s\"", lexer.token.token_name());
+                       parse_error("Unexpected token \"%s\"", lexer.token().token_name());
                }
                return null;
        }
@@ -552,6 +569,10 @@ class JsonParse {
        JsonParse(String s) {
                lexer = new JsonLexer(s);
        }
+
+       JsonParse(InputStream f) {
+               lexer = new JsonLexer(f);
+       }
 }
 
 public class AltosJson extends JsonUtil {
@@ -574,7 +595,7 @@ public class AltosJson extends JsonUtil {
 
        /* Generate string representation of the value
         */
-       StringBuffer append(StringBuffer result, int indent, boolean pretty) {
+       Writer append(Writer result, int indent, boolean pretty) throws IOException {
                switch (type) {
                case type_hash:
                        hash.append_hash(result, indent, pretty);
@@ -583,10 +604,19 @@ public class AltosJson extends JsonUtil {
                        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);
+                       if (Double.isInfinite(d_number)) {
+                               if (d_number < 0)
+                                       result.append("NegInfinity");
+                               else
+                                       result.append("Infinity");
+                       } else if (Double.isNaN(d_number)) {
+                               result.append("NaN");
+                       } else {
+                               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());
@@ -602,9 +632,13 @@ public class AltosJson extends JsonUtil {
        }
 
        private String toString(int indent, boolean pretty) {
-               StringBuffer result = new StringBuffer();
-               append(result, indent, pretty);
-               return result.toString();
+               try {
+                       Writer result = new StringWriter();
+                       append(result, indent, pretty);
+                       return result.toString();
+               } catch (Exception e) {
+                       return null;
+               }
        }
 
        public String toString() {
@@ -615,6 +649,14 @@ public class AltosJson extends JsonUtil {
                return toString(0, true);
        }
 
+       public void write(Writer w, int indent, boolean pretty) throws IOException {
+               append(w, indent, pretty);
+       }
+
+       public void write(Writer w) throws IOException {
+               write(w, 0, true);
+       }
+
        /* Parse string representation to a value
         */
 
@@ -628,6 +670,16 @@ public class AltosJson extends JsonUtil {
                }
        }
 
+       public static AltosJson fromInputStream(InputStream f) {
+               JsonParse       parse = new JsonParse(f);
+               try {
+                       return parse.parse();
+               } catch (IllegalArgumentException ie) {
+                       System.out.printf("json:\n%s\n", ie.getMessage());
+                       return null;
+               }
+       }
+
        /* Accessor functions
         */
        private boolean assert_type(boolean setting, int type, int other_type, String error) {
@@ -1000,7 +1052,32 @@ public class AltosJson extends JsonUtil {
                        assert_array(false);
 
                        Class element_class = c.getComponentType();
-                       if (element_class == Double.TYPE) {
+                       if (element_class == Boolean.TYPE) {
+                               boolean[] array = (boolean[]) Array.newInstance(element_class, size());
+                               for (int i = 0; i < array.length; i++)
+                                       array[i] = (Boolean) get(i).make(element_class);
+                               ret = array;
+                       } else if (element_class == Byte.TYPE) {
+                               byte[] array = (byte[]) Array.newInstance(element_class, size());
+                               for (int i = 0; i < array.length; i++)
+                                       array[i] = (Byte) get(i).make(element_class);
+                               ret = array;
+                       } else if (element_class == Character.TYPE) {
+                               char[] array = (char[]) Array.newInstance(element_class, size());
+                               for (int i = 0; i < array.length; i++)
+                                       array[i] = (Character) get(i).make(element_class);
+                               ret = array;
+                       } else if (element_class == Integer.TYPE) {
+                               int[] array = (int[]) Array.newInstance(element_class, size());
+                               for (int i = 0; i < array.length; i++)
+                                       array[i] = (Integer) get(i).make(element_class);
+                               ret = array;
+                       } else if (element_class == Long.TYPE) {
+                               long[] array = (long[]) Array.newInstance(element_class, size());
+                               for (int i = 0; i < array.length; i++)
+                                       array[i] = (Long) get(i).make(element_class);
+                               ret = array;
+                       } else 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);
@@ -1026,11 +1103,10 @@ public class AltosJson extends JsonUtil {
                                } else {
                                        object = c.newInstance();
                                }
-                               for (; c != null; c = c.getSuperclass()) {
+                               for (; c != Object.class; 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;
@@ -1044,17 +1120,27 @@ public class AltosJson extends JsonUtil {
                                                                field.set(object, val);
                                                        }
                                                } catch (IllegalAccessException ie) {
+                                                       System.out.printf("%s:%s %s\n",
+                                                                         c.getName(), fieldName, ie.toString());
                                                }
                                        }
                                }
                                ret = object;
                        } catch (InvocationTargetException ie) {
+                               System.out.printf("%s: %s\n",
+                                                 c.getName(), ie.toString());
                                ret = null;
                        } catch (NoSuchMethodException ie) {
+                               System.out.printf("%s: %s\n",
+                                                 c.getName(), ie.toString());
                                ret = null;
                        } catch (InstantiationException ie) {
+                               System.out.printf("%s: %s\n",
+                                                 c.getName(), ie.toString());
                                ret = null;
                        } catch (IllegalAccessException ie) {
+                               System.out.printf("%s: %s\n",
+                                                 c.getName(), ie.toString());
                                ret = null;
                        }
                }
@@ -1162,11 +1248,13 @@ public class AltosJson extends JsonUtil {
                        }
                } else {
                        assert_hash(true);
-                       for (Class c = object.getClass(); c != null; c = c.getSuperclass()) {
+                       for (Class c = object.getClass(); c != Object.class; c = c.getSuperclass()) {
                                for (Field field : c.getDeclaredFields()) {
                                        String  fieldName = field.getName();
-                                       Class   fieldClass = field.getType();
-                                       String  className = fieldClass.getName();
+
+                                       /* XXX hack to allow fields to be not converted */
+                                       if (fieldName.startsWith("__"))
+                                               continue;
 
                                        /* Skip static fields */
                                        if (Modifier.isStatic(field.getModifiers()))
@@ -1189,6 +1277,8 @@ public class AltosJson extends JsonUtil {
                                                        put(fieldName, json);
                                                }
                                        } catch (IllegalAccessException ie) {
+                                               System.out.printf("%s:%s %s\n",
+                                                                 c.getName(), fieldName, ie.toString());
                                        }
                                }
                        }