/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.util; import org.json.simple.JSONAware; import org.json.simple.JSONObject; import org.json.simple.JSONStreamAware; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class JSON { private JSON() {} //never public final static JSONStreamAware emptyJSON = prepare(new JSONObject()); public static JSONStreamAware prepare(final JSONObject json) { return new JSONStreamAware() { private final char[] jsonChars = JSON.toJSONString(json).toCharArray(); @Override public void writeJSONString(Writer out) throws IOException { out.write(jsonChars); } }; } public static JSONStreamAware prepareRequest(final JSONObject json) { json.put("protocol", 1); return prepare(json); } public static String toString(JSONStreamAware jsonStreamAware) { StringWriter stringWriter = new StringWriter(); try { JSON.writeJSONString(jsonStreamAware, stringWriter); } catch (IOException ignore) {} return stringWriter.toString(); } /** String escape pattern */ private static final Pattern pattern = Pattern.compile( "[\"\\\\\\u0008\\f\\n\\r\\t/\\u0000-\\u001f\\u007f-\\u009f\\u2000-\\u20ff\\ud800-\\udbff]"); /** * Create a formatted JSON string * * @param json JSON list or map * @return Formatted string */ public static String toJSONString(JSONAware json) { if (json == null) return "null"; if (json instanceof Map) { StringBuilder sb = new StringBuilder(1024); encodeObject((Map)json, sb); return sb.toString(); } if (json instanceof List) { StringBuilder sb = new StringBuilder(1024); encodeArray((List)json, sb); return sb.toString(); } return json.toJSONString(); } /** * Write a formatted JSON string * * @param json JSON list or map * @param writer Writer * @throws IOException I/O error occurred */ public static void writeJSONString(JSONStreamAware json, Writer writer) throws IOException { if (json == null) { writer.write("null"); return; } if (json instanceof Map) { StringBuilder sb = new StringBuilder(1024); encodeObject((Map)json, sb); writer.write(sb.toString()); return; } if (json instanceof List) { StringBuilder sb = new StringBuilder(1024); encodeArray((List)json, sb); writer.write(sb.toString()); return; } json.writeJSONString(writer); } /** * Create a formatted string from a list * * @param list List * @param sb String builder */ private static void encodeArray(List<?> list, StringBuilder sb) { if (list == null) { sb.append("null"); return; } boolean firstElement = true; sb.append('['); for (Object obj : list) { if (firstElement) firstElement = false; else sb.append(','); encodeValue(obj, sb); } sb.append(']'); } /** * Create a formatted string from a map * * @param map Map * @param sb String builder */ public static void encodeObject(Map<?, ?> map, StringBuilder sb) { if (map == null) { sb.append("null"); return; } Set<Map.Entry<Object, Object>> entries = (Set)map.entrySet(); Iterator<Map.Entry<Object, Object>> it = entries.iterator(); boolean firstElement = true; sb.append('{'); while (it.hasNext()) { Map.Entry<Object, Object> entry = it.next(); Object key = entry.getKey(); Object value = entry.getValue(); if (key == null) continue; if (firstElement) firstElement = false; else sb.append(','); sb.append('\"').append(key.toString()).append("\":"); encodeValue(value, sb); } sb.append('}'); } /** * Encode a JSON value * * @param value JSON value * @param sb String builder */ public static void encodeValue(Object value, StringBuilder sb) { if (value == null) { sb.append("null"); } else if (value instanceof Double) { if (((Double)value).isInfinite() || ((Double)value).isNaN()) sb.append("null"); else sb.append(value.toString()); } else if (value instanceof Float) { if (((Float)value).isInfinite() || ((Float)value).isNaN()) sb.append("null"); else sb.append(value.toString()); } else if (value instanceof Number) { sb.append(value.toString()); } else if (value instanceof Boolean) { sb.append(value.toString()); } else if (value instanceof Map) { encodeObject((Map<Object, Object>)value, sb); } else if (value instanceof List) { encodeArray((List<Object>)value, sb); } else { sb.append('\"'); escapeString(value.toString(), sb); sb.append('\"'); } } /** * Escape control characters in a string and append them to the string buffer * * @param string String to be written * @param sb String builder */ private static void escapeString(String string, StringBuilder sb) { if (string.length() == 0) return; // // Find the next special character in the string // int start = 0; Matcher matcher = pattern.matcher(string); while (matcher.find(start)) { int pos = matcher.start(); if (pos > start) sb.append(string.substring(start, pos)); start = pos + 1; // // Escape control characters // char c = string.charAt(pos); switch (c) { case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '/': sb.append("\\/"); break; default: if((c>='\u0000' && c<='\u001F') || (c>='\u007F' && c<='\u009F') || (c>='\u2000' && c<='\u20FF')){ sb.append("\\u").append(String.format("%04X", (int)c)); } else { sb.append(c); } } } // // Append the remainder of the string // if (start == 0) sb.append(string); else if (start < string.length()) sb.append(string.substring(start)); } }