From 6a625da2b6390fa55b3bcfbca7799f00f6fd51ed Mon Sep 17 00:00:00 2001 From: Shinovon Date: Fri, 26 Jul 2024 23:31:53 +0500 Subject: [PATCH 1/4] Update NNJSON --- src/cc/nnproject/json/AbstractJSON.java | 10 +- src/cc/nnproject/json/JSON.java | 262 +++++++------- src/cc/nnproject/json/JSONArray.java | 420 ++++++++++++++++------- src/cc/nnproject/json/JSONException.java | 8 +- src/cc/nnproject/json/JSONObject.java | 260 +++++++++----- src/cc/nnproject/json/JSONString.java | 8 +- 6 files changed, 632 insertions(+), 336 deletions(-) diff --git a/src/cc/nnproject/json/AbstractJSON.java b/src/cc/nnproject/json/AbstractJSON.java index a709e873..8c10a395 100644 --- a/src/cc/nnproject/json/AbstractJSON.java +++ b/src/cc/nnproject/json/AbstractJSON.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Arman Jussupgaliyev +Copyright (c) 2022-2024 Arman Jussupgaliyev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,14 +23,22 @@ of this software and associated documentation files (the "Software"), to deal public abstract class AbstractJSON { + // common methods for both JSONObject and JSONArray + public abstract void clear(); + public abstract int size(); + public abstract String toString(); + public abstract String build(); + public final String format() { return format(0); } + protected abstract String format(int l); + public abstract boolean similar(Object obj); } diff --git a/src/cc/nnproject/json/JSON.java b/src/cc/nnproject/json/JSON.java index 2dffa106..0a14f228 100644 --- a/src/cc/nnproject/json/JSON.java +++ b/src/cc/nnproject/json/JSON.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Arman Jussupgaliyev +Copyright (c) 2021-2024 Arman Jussupgaliyev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,80 +25,87 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Vector; /** - * JSON Library by nnproject.cc
- * Usage:

JSONObject obj = JSON.getObject(str); + * JSON Library compatible with CLDC 1.1 & JDK 1.1
+ * Usage:

JSONObject obj = JSON.getObject(str);

+ * Use with proguard argument:

-optimizations !code/simplification/object * @author Shinovon - * @version 1.2 + * @version 2.2 */ public final class JSON { - /** - * Parse all members once - */ - public final static boolean parse_members = false; + // parse all nested elements once + static final boolean parse_members = false; - public final static Object json_null = new Object(); - - public static final String FORMAT_TAB = " "; + // identation for formatting + static final String FORMAT_TAB = " "; + + // used for storing nulls, get methods must return real null + public static final Object json_null = new Object(); public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); + + public static AbstractJSON get(String text) throws JSONException { + if (text == null || text.length() <= 1) + throw new JSONException("Empty text"); + char c = text.charAt(0); + if (c != '{' && c != '[') + throw new JSONException("Not JSON object or array"); + return (AbstractJSON) parseJSON(text.trim()); + } - public static JSONObject getObject(String string) throws JSONException { - if (string == null || string.length() <= 1) - throw new JSONException("Empty string"); - if (string.charAt(0) != '{') + public static JSONObject getObject(String text) throws JSONException { + if (text == null || text.length() <= 1) + throw new JSONException("Empty text"); + if (text.charAt(0) != '{') throw new JSONException("Not JSON object"); - return (JSONObject) parseJSON(string); + return (JSONObject) parseJSON(text.trim()); } - public static JSONArray getArray(String string) throws JSONException { - if (string == null || string.length() <= 1) - throw new JSONException("Empty string"); - if (string.charAt(0) != '[') + public static JSONArray getArray(String text) throws JSONException { + if (text == null || text.length() <= 1) + throw new JSONException("Empty text"); + if (text.charAt(0) != '[') throw new JSONException("Not JSON array"); - return (JSONArray) parseJSON(string); + return (JSONArray) parseJSON(text.trim()); } static Object getJSON(Object obj) throws JSONException { if (obj instanceof Hashtable) { return new JSONObject((Hashtable) obj); - } else if (obj instanceof Vector) { + } + if (obj instanceof Vector) { return new JSONArray((Vector) obj); - } else if (obj == null) { + } + if (obj == null) { return json_null; - } else { - return obj; } + return obj; } static Object parseJSON(String str) throws JSONException { - if(str == null || (str = str.trim()).length() == 0) { - throw new JSONException("Empty string"); - } char first = str.charAt(0); int length = str.length() - 1; char last = str.charAt(length); - if (first == '{' && last != '}' || first == '[' && last != ']' || first == '"' && last != '"') { - throw new JSONException("Unexpected end of text"); - } else if (first == '"') { - // String - str = str.substring(1, str.length() - 1); - char[] chars = str.toCharArray(); - str = null; - try { + switch(first) { + case '"': { // string + if (last != '"') + throw new JSONException("Unexpected end of text"); + if(str.indexOf('\\') != -1) { + char[] chars = str.substring(1, length).toCharArray(); + str = null; int l = chars.length; StringBuffer sb = new StringBuffer(); int i = 0; - // Parse string escape chars + // parse escaped chars in string loop: { while (i < l) { char c = chars[i]; switch (c) { case '\\': { next: { - replaced: { - if(l < i + 1) { + replace: { + if (l < i + 1) { sb.append(c); break loop; } @@ -106,41 +113,43 @@ static Object parseJSON(String str) throws JSONException { switch (c1) { case 'u': i+=2; - String u = "" + chars[i++] + chars[i++] + chars[i++] + chars[i++]; - sb.append((char) Integer.parseInt(u, 16)); - break replaced; + sb.append((char) Integer.parseInt( + new String(new char[] {chars[i++], chars[i++], chars[i++], chars[i++]}), + 16)); + break replace; case 'x': i+=2; - String x = "" + chars[i++] + chars[i++]; - sb.append((char) Integer.parseInt(x, 16)); - break replaced; + sb.append((char) Integer.parseInt( + new String(new char[] {chars[i++], chars[i++]}), + 16)); + break replace; case 'n': sb.append('\n'); i+=2; - break replaced; + break replace; case 'r': sb.append('\r'); i+=2; - break replaced; + break replace; case 't': sb.append('\t'); i+=2; - break replaced; + break replace; case 'f': sb.append('\f'); i+=2; - break replaced; + break replace; case 'b': sb.append('\b'); i+=2; - break replaced; + break replace; case '\"': case '\'': case '\\': case '/': i+=2; sb.append((char) c1); - break replaced; + break replace; default: break next; } @@ -159,78 +168,49 @@ static Object parseJSON(String str) throws JSONException { } str = sb.toString(); sb = null; - } catch (Exception e) { - } - - return str; - } else if (first != '{' && first != '[') { - if (str.equals("null")) - return json_null; - if (str.equals("true")) - return TRUE; - if (str.equals("false")) - return FALSE; - if(str.length() > 2 && str.charAt(0) == '0' && str.charAt(1) == 'x') { - try { - return new Integer(Integer.parseInt(str.substring(2), 16)); - } catch (Exception e) { - try { - return new Long(Long.parseLong(str.substring(2), 16)); - } catch (Exception e2) { - } - } - } - try { - return new Integer(Integer.parseInt(str)); - } catch (Exception e) { - try { - return new Long(Long.parseLong(str)); - } catch (Exception e2) { - try { - return new Double(Double.parseDouble(str)); - } catch (Exception e3) { - } - } + return str; } - return str; - } else { - // Parse json object or array - int unclosed = 0; + return str.substring(1, length); + } + case '{': // JSON object or array + case '[': { boolean object = first == '{'; + if (object ? last != '}' : last != ']') + throw new JSONException("Unexpected end of text"); + int brackets = 0; int i = 1; char nextDelimiter = object ? ':' : ','; boolean escape = false; String key = null; - Object res = null; - if (object) res = new Hashtable(); - else res = new Vector(); + Object res = object ? (Object) new JSONObject() : (Object) new JSONArray(); for (int splIndex; i < length; i = splIndex + 1) { // skip all spaces for (; i < length - 1 && str.charAt(i) <= ' '; i++); splIndex = i; - boolean quotes = false; - for (; splIndex < length && (quotes || unclosed > 0 || str.charAt(splIndex) != nextDelimiter); splIndex++) { + boolean quote = false; + for (; splIndex < length && (quote || brackets > 0 || str.charAt(splIndex) != nextDelimiter); splIndex++) { char c = str.charAt(splIndex); if (!escape) { if (c == '\\') { escape = true; } else if (c == '"') { - quotes = !quotes; + quote = !quote; } } else escape = false; - if (!quotes) { + if (!quote) { if (c == '{' || c == '[') { - unclosed++; + brackets++; } else if (c == '}' || c == ']') { - unclosed--; + brackets--; } } } - if (quotes || unclosed > 0) { + // fail if unclosed quotes or brackets left + if (quote || brackets > 0) { throw new JSONException("Corrupted JSON"); } @@ -240,24 +220,57 @@ static Object parseJSON(String str) throws JSONException { nextDelimiter = ','; } else { Object value = str.substring(i, splIndex).trim(); - if (parse_members) value = parseJSON(value.toString()); - else value = new JSONString(value.toString()); + // don't check length because if value is empty, then exception is going to be thrown anyway + char c = ((String) value).charAt(0); + // leave JSONString as value to parse it later, if its object or array and nested parsing is disabled + value = parse_members || (c != '{' && c != '[') ? + parseJSON((String) value) : new JSONString((String) value); if (object) { - ((Hashtable) res).put(key, value); + ((JSONObject) res)._put(key, value); key = null; nextDelimiter = ':'; - } else if (splIndex > i) ((Vector) res).addElement(value); + } else if (splIndex > i) { + ((JSONArray) res).addElement(value); + } } } - return getJSON(res); + return res; + } + case 'n': // null + return json_null; + case 't': // true + return TRUE; + case 'f': // false + return FALSE; + default: // number + if ((first >= '0' && first <= '9') || first == '-') { + try { + // hex + if (length > 1 && first == '0' && str.charAt(1) == 'x') { + if (length > 9) // str.length() > 10 + return new Long(Long.parseLong(str.substring(2), 16)); + return new Integer(Integer.parseInt(str.substring(2), 16)); + } + // decimal + if (str.indexOf('.') != -1 || str.indexOf('E') != -1 || "-0".equals(str)) + return new Double(Double.parseDouble(str)); + if (first == '-') length--; + if (length > 8) // (str.length() - (str.charAt(0) == '-' ? 1 : 0)) >= 10 + return new Long(Long.parseLong(str)); + return new Integer(Integer.parseInt(str)); + } catch (Exception e) {} + } + throw new JSONException("Couldn't be parsed: " + str); +// return new JSONString(str); } } public static boolean isNull(Object obj) { - return json_null.equals(obj); + return obj == json_null || obj == null; } - public static String escape_utf8(String s) { + // transforms string for exporting + static String escape_utf8(String s) { int len = s.length(); StringBuffer sb = new StringBuffer(); int i = 0; @@ -266,7 +279,7 @@ public static String escape_utf8(String s) { switch (c) { case '"': case '\\': - sb.append("\\" + c); + sb.append("\\").append(c); break; case '\b': sb.append("\\b"); @@ -284,12 +297,13 @@ public static String escape_utf8(String s) { sb.append("\\t"); break; default: - if (c < 32 || c > 1103) { + if (c < 32 || c > 1103 || (c >= '\u0080' && c < '\u00a0')) { String u = Integer.toHexString(c); + sb.append("\\u"); for (int z = u.length(); z < 4; z++) { - u = "0" + u; + sb.append('0'); } - sb.append("\\u" + u); + sb.append(u); } else { sb.append(c); } @@ -299,30 +313,46 @@ public static String escape_utf8(String s) { return sb.toString(); } - public static double getDouble(Object o) throws JSONException { + static double getDouble(Object o) throws JSONException { try { + if (o instanceof JSONString) + return Double.parseDouble(((JSONString) o).str); if (o instanceof Integer) return ((Integer) o).intValue(); if (o instanceof Long) return ((Long) o).longValue(); if (o instanceof Double) return ((Double) o).doubleValue(); - } catch (Throwable e) { - } - throw new JSONException("Value cast failed: " + o); + } catch (Throwable e) {} + throw new JSONException("Cast to double failed: " + o); } - public static long getLong(Object o) throws JSONException { + static int getInt(Object o) throws JSONException { try { + if (o instanceof JSONString) + return Integer.parseInt(((JSONString) o).str); + if (o instanceof Integer) + return ((Integer) o).intValue(); + if (o instanceof Long) + return (int) ((Long) o).longValue(); + if (o instanceof Double) + return ((Double) o).intValue(); + } catch (Throwable e) {} + throw new JSONException("Cast to int failed: " + o); + } + + static long getLong(Object o) throws JSONException { + try { + if (o instanceof JSONString) + return Long.parseLong(((JSONString) o).str); if (o instanceof Integer) return ((Integer) o).longValue(); if (o instanceof Long) return ((Long) o).longValue(); if (o instanceof Double) return ((Double) o).longValue(); - } catch (Throwable e) { - } - throw new JSONException("Value cast failed: " + o); + } catch (Throwable e) {} + throw new JSONException("Cast to long failed: " + o); } } diff --git a/src/cc/nnproject/json/JSONArray.java b/src/cc/nnproject/json/JSONArray.java index 443e4793..8cb003ca 100644 --- a/src/cc/nnproject/json/JSONArray.java +++ b/src/cc/nnproject/json/JSONArray.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Arman Jussupgaliyev +Copyright (c) 2021-2024 Arman Jussupgaliyev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,31 +25,53 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Vector; public class JSONArray extends AbstractJSON { - - private Vector vector; - + + protected Object[] elements; + protected int count; + public JSONArray() { - this.vector = new Vector(); + elements = new Object[10]; + } + + public JSONArray(int size) { + elements = new Object[size]; } + /** + * @deprecated Doesn't adapt nested elements + */ public JSONArray(Vector vector) { - this.vector = vector; + elements = new Object[count = vector.size()]; + vector.copyInto(elements); } - + + /** + * @deprecated Compatibility with org.json + */ + public JSONArray(String str) { + JSONArray tmp = JSON.getArray(str); // FIXME + elements = tmp.elements; + count = tmp.count; + } + public Object get(int index) throws JSONException { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } try { - if(index >= 0 && index < vector.size()) { - Object o = vector.elementAt(index); - if (o instanceof JSONString) { - vector.setElementAt(o = JSON.parseJSON(o.toString()), index); - } - return o; - } + Object o = elements[index]; + if (o instanceof JSONString) + o = elements[index] = JSON.parseJSON(((JSONString) o).str); + if (o == JSON.json_null) + return null; + return o; } catch (Exception e) { } throw new JSONException("No value at " + index); } + // unused methods should be removed by proguard shrinking + public Object get(int index, Object def) { try { return get(index); @@ -63,17 +85,27 @@ public Object getNullable(int index) { } public String getString(int index) throws JSONException { - return get(index).toString(); + Object o = get(index); + if (o == null || o instanceof String) + return (String) o; + return String.valueOf(o); } public String getString(int index, String def) { try { - return get(index).toString(); + Object o = get(index); + if (o == null || o instanceof String) + return (String) o; + return String.valueOf(o); } catch (Exception e) { return def; } } + public String getNullableString(int index) { + return getString(index, null); + } + public JSONObject getObject(int index) throws JSONException { try { return (JSONObject) get(index); @@ -82,12 +114,16 @@ public JSONObject getObject(int index) throws JSONException { } } - public JSONObject getNullableObject(int index) { + public JSONObject getObject(int index, JSONObject def) { try { return getObject(index); } catch (Exception e) { - return null; } + return def; + } + + public JSONObject getNullableObject(int index) { + return getObject(index, null); } public JSONArray getArray(int index) throws JSONException { @@ -98,16 +134,20 @@ public JSONArray getArray(int index) throws JSONException { } } - public JSONArray getNullableArray(int index) { + public JSONArray getArray(int index, JSONArray def) { try { return getArray(index); } catch (Exception e) { - return null; } + return def; + } + + public JSONArray getNullableArray(int index) { + return getArray(index, null); } public int getInt(int index) throws JSONException { - return (int) JSON.getLong(get(index)); + return JSON.getInt(get(index)); } public int getInt(int index, int def) { @@ -144,14 +184,14 @@ public double getDouble(int index, double def) { public boolean getBoolean(int index) throws JSONException { Object o = get(index); - if(o == JSON.TRUE) return true; - if(o == JSON.FALSE) return false; - if(o instanceof Boolean) return ((Boolean) o).booleanValue(); - if(o instanceof String) { + if (o == JSON.TRUE) return true; + if (o == JSON.FALSE) return false; + if (o instanceof Boolean) return ((Boolean) o).booleanValue(); + if (o instanceof String) { String s = (String) o; s = s.toLowerCase(); - if(s.equals("true")) return true; - if(s.equals("false")) return false; + if (s.equals("true")) return true; + if (s.equals("false")) return false; } throw new JSONException("Not boolean: " + o + " (" + index + ")"); } @@ -165,91 +205,189 @@ public boolean getBoolean(int index, boolean def) { } public boolean isNull(int index) { - return JSON.isNull(getNullable(index)); + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + return elements[index] == JSON.json_null; + } + + /** + * @deprecated + */ + public void add(Object object) { + if (object == this) throw new JSONException(); + addElement(JSON.getJSON(object)); + } + + public void add(AbstractJSON json) { + if (json == this) throw new JSONException(); + addElement(json); } public void add(String s) { - vector.addElement(s); + addElement(s); + } + + public void add(int i) { + addElement(new Integer(i)); } - public void add(boolean b) { - vector.addElement(new Boolean(b)); + public void add(long l) { + addElement(new Long(l)); } public void add(double d) { - vector.addElement(Double.toString(d)); + addElement(new Double(d)); } - public void add(int i) { - vector.addElement(Integer.toString(i)); + public void add(boolean b) { + addElement(new Boolean(b)); } - public void add(long l) { - vector.addElement(Long.toString(l)); + /** + * @deprecated + */ + public void set(int index, Object object) { + if (object == this) throw new JSONException(); + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = JSON.getJSON(object); } - public void add(Object obj) { - vector.addElement(JSON.getJSON(obj)); + public void set(int index, AbstractJSON json) { + if (json == this) throw new JSONException(); + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = json; } - public void set(int idx, String s) { - vector.setElementAt(s, idx); + public void set(int index, String s) { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = s; + } + + public void set(int index, int i) { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = new Integer(i); } - public void set(int idx, boolean b) { - vector.setElementAt(new Boolean(b), idx); + public void set(int index, long l) { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = new Long(l); } - public void set(int idx, double d) { - vector.setElementAt(Double.toString(d), idx); + public void set(int index, double d) { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = new Double(d); + } + + public void set(int index, boolean b) { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + elements[index] = new Boolean(b); } - public void set(int idx, int i) { - vector.setElementAt(Integer.toString(i), idx); + /** + * @deprecated + */ + public void put(int index, Object object) { + if (object == this) throw new JSONException(); + insertElementAt(JSON.getJSON(object), index); } - - public void set(int idx, long l) { - vector.setElementAt(Long.toString(l), idx); + + public void put(int index, AbstractJSON json) { + if (json == this) throw new JSONException(); + insertElementAt(json, index); } - public void set(int idx, Object obj) { - vector.setElementAt(JSON.getJSON(obj), idx); + public void put(int index, String s) { + insertElementAt(s, index); } - public void put(int idx, String s) { - vector.insertElementAt(s, idx); + public void put(int index, int i) { + insertElementAt(new Integer(i), index); + } + + public void put(int index, long l) { + insertElementAt(new Long(l), index); } - public void put(int idx, boolean b) { - vector.insertElementAt(new Boolean(b), idx); + public void put(int index, double d) { + insertElementAt(new Double(d), index); } - public void put(int idx, double d) { - vector.insertElementAt(Double.toString(d), idx); + public void put(int index, boolean b) { + insertElementAt(new Boolean(b), index); } - public void put(int idx, int i) { - vector.insertElementAt(Integer.toString(i), idx); + public boolean has(Object object) { + return _indexOf(JSON.getJSON(object), 0) != -1; + } + + public boolean has(int i) { + return _indexOf(new Integer(i), 0) != -1; + } + + public boolean has(long l) { + return _indexOf(new Long(l), 0) != -1; } - public void put(int idx, long l) { - vector.insertElementAt(Long.toString(l), idx); + public boolean has(double d) { + return _indexOf(new Double(d), 0) != -1; } - public void put(int idx, Object obj) { - vector.insertElementAt(JSON.getJSON(obj), idx); + public boolean has(boolean b) { + return _indexOf(new Boolean(b), 0) != -1; } - public void remove(int idx) { - vector.removeElementAt(idx); + public int indexOf(Object object) { + return _indexOf(JSON.getJSON(object), 0); + } + + public int indexOf(Object object, int index) { + return _indexOf(JSON.getJSON(object), index); } public void clear() { - vector.removeAllElements(); + for (int i = 0; i < count; i++) elements[i] = null; + count = 0; + } + + public boolean remove(Object object) { + int i = _indexOf(JSON.getJSON(object), 0); + if (i == -1) return false; + remove(i); + return true; + } + + public void remove(int index) { + if (index < 0 || index >= count) { + throw new JSONException("Index out of bounds: " + index); + } + count--; + int size = count - index; + if (size > 0) + System.arraycopy(elements, index + 1, elements, index, size); + elements[count] = null; } public int size() { - return vector.size(); + return count; + } + + public boolean isEmpty() { + return count == 0; } public String toString() { @@ -257,53 +395,50 @@ public String toString() { } public boolean equals(Object obj) { - if(this == obj || super.equals(obj)) { - return true; - } - return similar(obj); + return this == obj || super.equals(obj) || similar(obj); } public boolean similar(Object obj) { - if(!(obj instanceof JSONArray)) { - return false; - } - int size = size(); - if(size != ((JSONArray)obj).size()) { - return false; - } - for(int i = 0; i < size; i++) { - Object a = get(i); - Object b = ((JSONArray)obj).get(i); - if(a == b) { - continue; - } - if(a == null) { - return false; - } - if(a instanceof AbstractJSON) { - if (!((AbstractJSON)a).similar(b)) { - return false; - } - } else if(!a.equals(b)) { - return false; - } - } - return true; + if (!(obj instanceof JSONArray)) { + return false; + } + int size = count; + if (size != ((JSONArray)obj).count) { + return false; + } + for (int i = 0; i < size; i++) { + Object a = get(i); + Object b = ((JSONArray)obj).get(i); + if (a == b) { + continue; + } + if (a == null) { + return false; + } + if (a instanceof AbstractJSON) { + if (!((AbstractJSON)a).similar(b)) { + return false; + } + } else if (!a.equals(b)) { + return false; + } + } + return true; } public String build() { - int size = size(); + int size = count; if (size == 0) return "[]"; StringBuffer s = new StringBuffer("["); int i = 0; while (i < size) { - Object v = vector.elementAt(i); + Object v = elements[i]; if (v instanceof AbstractJSON) { s.append(((AbstractJSON) v).build()); } else if (v instanceof String) { s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); - } else if(JSON.json_null.equals(v)) { + } else if (v == JSON.json_null) { s.append((String) null); } else { s.append(String.valueOf(v)); @@ -318,7 +453,7 @@ public String build() { } protected String format(int l) { - int size = size(); + int size = count; if (size == 0) return "[]"; String t = ""; @@ -328,23 +463,23 @@ protected String format(int l) { String t2 = t.concat(JSON.FORMAT_TAB); StringBuffer s = new StringBuffer("[\n"); s.append(t2); - for (int i = 0; i < size; ) { - Object v = null; - try { - v = get(i); - } catch (JSONException e) { + int i = 0; + while (i < size) { + Object v = elements[i]; + if (v instanceof JSONString) { + v = elements[i] = JSON.parseJSON(((JSONString) v).str); } if (v instanceof AbstractJSON) { s.append(((AbstractJSON) v).format(l + 1)); } else if (v instanceof String) { s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); - } else if(v == JSON.json_null) { + } else if (v == JSON.json_null) { s.append((String) null); } else { s.append(v); } i++; - if (i < size()) { + if (i < size) { s.append(",\n").append(t2); } } @@ -359,27 +494,80 @@ protected String format(int l) { public Enumeration elements() { return new Enumeration() { int i = 0; + public boolean hasMoreElements() { - return i < vector.size(); + return i < count; } + public Object nextElement() { - try { - return get(i++); - } catch (Exception e) { - return null; - } + Object o = elements[i]; + if (o instanceof JSONString) + o = elements[i] = JSON.parseJSON(((JSONString) o).str); + i++; + return o == JSON.json_null ? null : o; } }; } + public void copyInto(Object[] arr) { + copyInto(arr, 0, arr.length); + } + public void copyInto(Object[] arr, int offset, int length) { int i = offset; int j = 0; while(i < arr.length && j < length && j < size()) { - Object o = get(j++); - if(o == JSON.json_null) o = null; - arr[i++] = o; + arr[i++] = get(j++); } } + public Vector toVector() { + int size = count; + Vector copy = new Vector(size); + for (int i = 0; i < size; i++) { + Object o = elements[i]; + if (o instanceof JSONString) + o = elements[i] = JSON.parseJSON(((JSONString) o).str); + if (o instanceof JSONObject) { + o = ((JSONObject) o).toTable(); + } else if (o instanceof JSONArray) { + o = ((JSONArray) o).toVector(); + } + copy.addElement(o); + } + return copy; + } + + void addElement(Object object) { + if (count == elements.length) grow(); + elements[count++] = object; + } + + private void insertElementAt(Object object, int index) { + if (index < 0 || index > count) { + throw new JSONException("Index out of bounds: " + index); + } + if (count == elements.length) grow(); + int size = count - index; + if (size > 0) + System.arraycopy(elements, index, elements, index + 1, size); + elements[index] = object; + count++; + } + + private int _indexOf(Object object, int start) { + for (int i = start; i < count; i++) { + if (elements[i] instanceof JSONString) + elements[i] = JSON.parseJSON(((JSONString) elements[i]).str); + if (object.equals(elements[i])) return i; + } + return -1; + } + + private void grow() { + Object[] tmp = new Object[elements.length * 2]; + System.arraycopy(elements, 0, tmp, 0, count); + elements = tmp; + } + } diff --git a/src/cc/nnproject/json/JSONException.java b/src/cc/nnproject/json/JSONException.java index 1c72ae33..60a0e812 100644 --- a/src/cc/nnproject/json/JSONException.java +++ b/src/cc/nnproject/json/JSONException.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Arman Jussupgaliyev +Copyright (c) 2021-2024 Arman Jussupgaliyev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,11 +23,11 @@ of this software and associated documentation files (the "Software"), to deal public class JSONException extends RuntimeException { - JSONException() { + public JSONException() { } - JSONException(String string) { - super(string); + public JSONException(String msg) { + super(msg); } public String toString() { diff --git a/src/cc/nnproject/json/JSONObject.java b/src/cc/nnproject/json/JSONObject.java index c456a2b3..91a62641 100644 --- a/src/cc/nnproject/json/JSONObject.java +++ b/src/cc/nnproject/json/JSONObject.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Arman Jussupgaliyev +Copyright (c) 2021-2024 Arman Jussupgaliyev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,18 +26,24 @@ of this software and associated documentation files (the "Software"), to deal public class JSONObject extends AbstractJSON { - private Hashtable table; + protected Hashtable table; public JSONObject() { - this.table = new Hashtable(); + table = new Hashtable(); } + /** + * @deprecated Doesn't adapt nested elements + */ public JSONObject(Hashtable table) { this.table = table; } - - public boolean has(String name) { - return table.containsKey(name); + + /** + * @deprecated Compatibility with org.json + */ + public JSONObject(String str) { + table = JSON.getObject(str).table; // FIXME } public Object get(String name) throws JSONException { @@ -45,7 +51,9 @@ public Object get(String name) throws JSONException { if (has(name)) { Object o = table.get(name); if (o instanceof JSONString) - table.put(name, o = JSON.parseJSON(o.toString())); + table.put(name, o = JSON.parseJSON(((JSONString) o).str)); + if (o == JSON.json_null) + return null; return o; } } catch (JSONException e) { @@ -55,8 +63,10 @@ public Object get(String name) throws JSONException { throw new JSONException("No value for name: " + name); } + // unused methods should be removed by proguard shrinking + public Object get(String name, Object def) { - if(!has(name)) return def; + if (!has(name)) return def; try { return get(name); } catch (Exception e) { @@ -69,16 +79,18 @@ public Object getNullable(String name) { } public String getString(String name) throws JSONException { - return get(name).toString(); + Object o = get(name); + if (o == null || o instanceof String) + return (String) o; + return String.valueOf(o); } public String getString(String name, String def) { try { Object o = get(name, def); - if(o == null || o instanceof String) { + if (o == null || o instanceof String) return (String) o; - } - return o.toString(); + return String.valueOf(o); } catch (Exception e) { return def; } @@ -95,14 +107,18 @@ public JSONObject getObject(String name) throws JSONException { throw new JSONException("Not object: " + name); } } + public JSONObject getObject(String name, JSONObject def) { + if (has(name)) { + try { + return (JSONObject) get(name); + } catch (Exception e) { + } + } + return def; + } public JSONObject getNullableObject(String name) { - if(!has(name)) return null; - try { - return getObject(name); - } catch (Exception e) { - return null; - } + return getObject(name, null); } public JSONArray getArray(String name) throws JSONException { @@ -113,21 +129,27 @@ public JSONArray getArray(String name) throws JSONException { } } - public JSONArray getNullableArray(String name) { - if(!has(name)) return null; - try { - return getArray(name); - } catch (Exception e) { - return null; + public JSONArray getArray(String name, JSONArray def) throws JSONException { + if (has(name)) { + try { + return (JSONArray) get(name); + } catch (Exception e) { + } } + return def; + } + + + public JSONArray getNullableArray(String name) { + return getArray(name, null); } public int getInt(String name) throws JSONException { - return (int) JSON.getLong(get(name)); + return JSON.getInt(get(name)); } public int getInt(String name, int def) { - if(!has(name)) return def; + if (!has(name)) return def; try { return getInt(name); } catch (Exception e) { @@ -140,7 +162,7 @@ public long getLong(String name) throws JSONException { } public long getLong(String name, long def) { - if(!has(name)) return def; + if (!has(name)) return def; try { return getLong(name); } catch (Exception e) { @@ -153,7 +175,7 @@ public double getDouble(String name) throws JSONException { } public double getDouble(String name, double def) { - if(!has(name)) return def; + if (!has(name)) return def; try { return getDouble(name); } catch (Exception e) { @@ -163,20 +185,20 @@ public double getDouble(String name, double def) { public boolean getBoolean(String name) throws JSONException { Object o = get(name); - if(o == JSON.TRUE) return true; - if(o == JSON.FALSE) return false; - if(o instanceof Boolean) return ((Boolean) o).booleanValue(); - if(o instanceof String) { + if (o == JSON.TRUE) return true; + if (o == JSON.FALSE) return false; + if (o instanceof Boolean) return ((Boolean) o).booleanValue(); + if (o instanceof String) { String s = (String) o; s = s.toLowerCase(); - if(s.equals("true")) return true; - if(s.equals("false")) return false; + if (s.equals("true")) return true; + if (s.equals("false")) return false; } throw new JSONException("Not boolean: " + o); } public boolean getBoolean(String name, boolean def) { - if(!has(name)) return def; + if (!has(name)) return def; try { return getBoolean(name); } catch (Exception e) { @@ -185,19 +207,24 @@ public boolean getBoolean(String name, boolean def) { } public boolean isNull(String name) { - return JSON.isNull(getNullable(name)); + if (!has(name)) + throw new JSONException("No value for name: " + name); + return table.get(name) == JSON.json_null; } - - public void put(String name, String s) { - table.put(name, s); + + /** + * @deprecated + */ + public void put(String name, Object obj) { + table.put(name, JSON.getJSON(obj)); } - - public void put(String name, boolean b) { - table.put(name, new Boolean(b)); + + public void put(String name, AbstractJSON json) { + table.put(name, json); } - - public void put(String name, double d) { - table.put(name, new Double(d)); + + public void put(String name, String s) { + table.put(name, s); } public void put(String name, int i) { @@ -207,62 +234,79 @@ public void put(String name, int i) { public void put(String name, long l) { table.put(name, new Long(l)); } + + public void put(String name, double d) { + table.put(name, new Double(d)); + } + + public void put(String name, boolean b) { + table.put(name, new Boolean(b)); + } - public void put(String name, Object obj) { - table.put(name, JSON.getJSON(obj)); + public boolean hasValue(Object object) { + return table.contains(JSON.getJSON(object)); } - public void remove(String name) { - table.remove(name); + // hasKey + public boolean has(String name) { + return table.containsKey(name); } public void clear() { table.clear(); } + public void remove(String name) { + table.remove(name); + } + public int size() { return table.size(); } + public boolean isEmpty() { + return table.isEmpty(); + } + public String toString() { return build(); } public boolean equals(Object obj) { - if(this == obj || super.equals(obj)) { - return true; - } - return similar(obj); + return this == obj || super.equals(obj) || similar(obj); } public boolean similar(Object obj) { - if(!(obj instanceof JSONObject)) { - return false; - } - int size = size(); - if(size != ((JSONObject)obj).size()) { - return false; - } - Enumeration keys = table.keys(); - while(keys.hasMoreElements()) { - String key = (String) keys.nextElement(); - Object a = get(key); - Object b = ((JSONObject)obj).get(key); - if(a == b) { - continue; - } - if(a == null) { - return false; - } - if(a instanceof AbstractJSON) { - if (!((AbstractJSON)a).similar(b)) { - return false; - } - } else if(!a.equals(b)) { - return false; - } - } - return true; + if (!(obj instanceof JSONObject)) { + return false; + } + if (table.equals(((JSONObject) obj).table)) { + return true; + } + int size = size(); + if (size != ((JSONObject)obj).size()) { + return false; + } + Enumeration keys = table.keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + Object a = get(key); + Object b = ((JSONObject)obj).get(key); + if (a == b) { + continue; + } + if (a == null) { + return false; + } + if (a instanceof AbstractJSON) { + if (!((AbstractJSON)a).similar(b)) { + return false; + } + } else if (!a.equals(b)) { + return false; + } + } + return true; } public String build() { @@ -271,14 +315,14 @@ public String build() { StringBuffer s = new StringBuffer("{"); Enumeration keys = table.keys(); while (true) { - String k = keys.nextElement().toString(); + String k = (String) keys.nextElement(); s.append("\"").append(k).append("\":"); Object v = table.get(k); if (v instanceof AbstractJSON) { s.append(((AbstractJSON) v).build()); } else if (v instanceof String) { s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); - } else if(JSON.json_null.equals(v)) { + } else if (v == JSON.json_null) { s.append((String) null); } else { s.append(v); @@ -305,19 +349,17 @@ protected String format(int l) { s.append(t2); Enumeration keys = table.keys(); int i = 0; - while(keys.hasMoreElements()) { - String k = keys.nextElement().toString(); + while (keys.hasMoreElements()) { + String k = (String) keys.nextElement(); s.append("\"").append(k).append("\": "); - Object v = null; - try { - v = get(k); - } catch (JSONException e) { - } + Object v = get(k); + if (v instanceof JSONString) + table.put(k, v = JSON.parseJSON(((JSONString) v).str)); if (v instanceof AbstractJSON) { s.append(((AbstractJSON) v).format(l + 1)); } else if (v instanceof String) { s.append("\"").append(JSON.escape_utf8((String) v)).append("\""); - } else if(v == JSON.json_null) { + } else if (v == JSON.json_null) { s.append((String) null); } else { s.append(v); @@ -340,12 +382,44 @@ public Enumeration keys() { } public JSONArray keysAsArray() { - JSONArray array = new JSONArray(); + JSONArray array = new JSONArray(table.size()); Enumeration keys = table.keys(); - while(keys.hasMoreElements()) { - array.add(keys.nextElement()); + while (keys.hasMoreElements()) { + array.addElement(keys.nextElement()); } return array; } + + /** + * @deprecated Use {@link JSONObject#toTable()} instead + */ + public Hashtable getTable() { + return table; + } + + public Hashtable toTable() { + Hashtable copy = new Hashtable(table.size()); + Enumeration keys = table.keys(); + while (keys.hasMoreElements()) { + String k = (String) keys.nextElement(); + Object v = table.get(k); + if (v instanceof JSONString) + table.put(k, v = JSON.parseJSON(((JSONString) v).str)); + if (v instanceof JSONObject) { + v = ((JSONObject) v).toTable(); + } else if (v instanceof JSONArray) { + v = ((JSONArray) v).toVector(); + } + copy.put(k, v); + } + return copy; + } + + void _put(String name, Object obj) { + table.put(name, obj); + } + + // TODO: Enumeration elements() + // TODO: String keyOf(Object) } diff --git a/src/cc/nnproject/json/JSONString.java b/src/cc/nnproject/json/JSONString.java index f8afe400..9fb4f596 100644 --- a/src/cc/nnproject/json/JSONString.java +++ b/src/cc/nnproject/json/JSONString.java @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Arman Jussupgaliyev +Copyright (c) 2021-2024 Arman Jussupgaliyev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,12 @@ of this software and associated documentation files (the "Software"), to deal class JSONString { - private String str; + String str; JSONString(String s) { this.str = s; } - String getString() { - return str; - } - public String toString() { return str; } From b95fc2e3792a2bf1240cc698077fdd38b695dc0a Mon Sep 17 00:00:00 2001 From: Shinovon Date: Thu, 5 Sep 2024 20:14:37 +0500 Subject: [PATCH 2/4] Add bus stop info --- src/mahomaps/route/RouteDecoder.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mahomaps/route/RouteDecoder.java b/src/mahomaps/route/RouteDecoder.java index 23bfe290..5664f818 100644 --- a/src/mahomaps/route/RouteDecoder.java +++ b/src/mahomaps/route/RouteDecoder.java @@ -147,7 +147,7 @@ public static RouteSegment[] DecodeSegments(JSONArray j, Geopoint[] line) { throw new IllegalArgumentException(); } final JSONObject segmd = props.getObject("SegmentMetaData"); - final String descr = segmd.getString("text"); + String descr = segmd.getString("text"); int dist = 0; { JSONObject dj = segmd.getNullableObject("Distance"); @@ -169,6 +169,15 @@ public static RouteSegment[] DecodeSegments(JSONArray j, Geopoint[] line) { } else if (trt.equals("underground")) { arr[i] = new MetroSegment(descr, sv, line[sv]); } else { + JSONArray f = js.getNullableArray("features"); + if (f != null && f.size() > 0) { + JSONObject t = f.getObject(f.size() - 1); + if (t.has("properties") + && (t = t.getObject("properties")).has("StopMetaData") + && (t = t.getObject("StopMetaData")).has("name")) { + descr = descr.concat("\nОстановка: ").concat(t.getString("name")); + } + } arr[i] = new TransportSegment(descr, sv, line[sv]); } continue; From c0bd64a0351f1d31e5869d1a62806ecefa1de0c8 Mon Sep 17 00:00:00 2001 From: Shinovon Date: Thu, 5 Sep 2024 20:16:17 +0500 Subject: [PATCH 3/4] Update nnjson --- src/cc/nnproject/json/JSONObject.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/nnproject/json/JSONObject.java b/src/cc/nnproject/json/JSONObject.java index 91a62641..161c6b5c 100644 --- a/src/cc/nnproject/json/JSONObject.java +++ b/src/cc/nnproject/json/JSONObject.java @@ -220,11 +220,11 @@ public void put(String name, Object obj) { } public void put(String name, AbstractJSON json) { - table.put(name, json); + table.put(name, json == null ? JSON.json_null : json); } public void put(String name, String s) { - table.put(name, s); + table.put(name, s == null ? JSON.json_null : s); } public void put(String name, int i) { From e4171b73a005ea87a9e2e2d04445a4c476c8d7cc Mon Sep 17 00:00:00 2001 From: Shinovon Date: Thu, 5 Sep 2024 21:12:35 +0500 Subject: [PATCH 4/4] Refresh token automatically --- src/mahomaps/api/YmapsApi.java | 10 +++++----- src/mahomaps/overlays/RouteOverlay.java | 11 ++++++++++- src/mahomaps/screens/SearchLoader.java | 8 ++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/mahomaps/api/YmapsApi.java b/src/mahomaps/api/YmapsApi.java index f7b282cd..dd2c54ee 100644 --- a/src/mahomaps/api/YmapsApi.java +++ b/src/mahomaps/api/YmapsApi.java @@ -53,17 +53,17 @@ private final String GetRouteUrl(Geopoint a, Geopoint b, int type) { public final JSONArray Search(String text, Geopoint around, double zone) throws JSONException, IOException, Http403Exception { - JSONArray j = (JSON.getObject(GetUtf(GetSearchUrl(text, around, zone)))).getArray("features"); - return j; + JSONObject j = JSON.getObject(GetUtf(GetSearchUrl(text, around, zone))); + if (!j.has("features")) throw new Http403Exception(); + return j.getArray("features"); } - public final JSONObject Route(Geopoint a, Geopoint b, int type) + public final JSONArray Routes(Geopoint a, Geopoint b, int type) throws JSONException, IOException, Http403Exception { JSONArray j = (JSON.getObject(GetUtf(GetRouteUrl(a, b, type)))).getArray("features"); if (j.size() == 0) throw new ConnectionNotFoundException(); - JSONObject j1 = j.getObject(0).getArray("features").getObject(0); - return j1; + return j.getObject(0).getArray("features"); } public static final int ROUTE_BYFOOT = 1; diff --git a/src/mahomaps/overlays/RouteOverlay.java b/src/mahomaps/overlays/RouteOverlay.java index dfef72b0..31c1d0a0 100644 --- a/src/mahomaps/overlays/RouteOverlay.java +++ b/src/mahomaps/overlays/RouteOverlay.java @@ -35,6 +35,7 @@ public class RouteOverlay extends MapOverlay implements Runnable, IButtonHandler private Geopoint b; private final int method; Route route; + private int tries; private boolean anchorsShown = false; @@ -62,12 +63,20 @@ public boolean OnPointTap(Geopoint p) { public void run() { try { - route = new Route(MahoMapsApp.api.Route(a, b, method)); + if (tries != 0) { + MahoMapsApp.api.RefreshToken(); + } + // TODO route variant selection + route = new Route(MahoMapsApp.api.Routes(a, b, method).getObject(0)); LoadRoute(); } catch (IOException e) { content = new FillFlowContainer(new UIElement[] { new SimpleText(MahoMapsApp.text[111]), new Button(MahoMapsApp.text[37], 2, this), new Button(MahoMapsApp.text[38], 0, this) }); } catch (Http403Exception e) { + if (tries++ == 0) { + run(); + return; + } content = new FillFlowContainer( new UIElement[] { new SimpleText(MahoMapsApp.text[135]), new SimpleText(MahoMapsApp.text[136]), new Button(MahoMapsApp.text[37], 2, this), new Button(MahoMapsApp.text[38], 0, this) }); diff --git a/src/mahomaps/screens/SearchLoader.java b/src/mahomaps/screens/SearchLoader.java index 02ce6ae6..d2822cbf 100644 --- a/src/mahomaps/screens/SearchLoader.java +++ b/src/mahomaps/screens/SearchLoader.java @@ -20,6 +20,7 @@ public class SearchLoader extends Form implements Runnable, CommandListener { private Thread th; public final String query; public final Geopoint point; + private int tries; public SearchLoader(String query, Geopoint point) { super(query); @@ -33,6 +34,9 @@ public SearchLoader(String query, Geopoint point) { public void run() { append(new Gauge(MahoMapsApp.text[14], false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING)); try { + if (tries != 0) { + MahoMapsApp.api.RefreshToken(); + } JSONArray arr = MahoMapsApp.api.Search(query, point, 0.1d); MahoMapsApp.BringSubScreen(new SearchScreen(query, point, arr)); } catch (IOException e) { @@ -42,6 +46,10 @@ public void run() { e.printStackTrace(); } catch (Http403Exception e) { deleteAll(); + if (tries++ == 0) { + run(); + return; + } append(new StringItem(MahoMapsApp.text[135], MahoMapsApp.text[136])); } catch (Exception e) { deleteAll();