package osgi.enroute.util.dto; import java.io.IOException; import java.lang.reflect.Array; import java.util.IdentityHashMap; import java.util.Map; class JSON { boolean refs = false; int limit = -1; int indent = 0; public Encoder enc(Appendable app) { return new Encoder(app); } public Encoder enc() { return new Encoder(null); } /** * Encodes an object to a JSON stream. */ public class Encoder { int level = 0; Appendable result; Map<Object,String> objectRefs = new IdentityHashMap<Object, String>(); public Encoder(Appendable app) { this.result = app == null ? new StringBuilder() : app; } public Encoder put(Object value) throws IOException { append("#", value); return this; } /** * Append the specified value's string representation to the specified * StringBuilder. * * @param result * StringBuilder to which the string representation is * appended. * @param objectRefs * References to "seen" objects. * @param refpath * The reference path of the specified value. * @param value * The object whose string representation is to be appended. * @return The specified StringBuilder. * @throws IOException */ private void append(final String refpath, Object value) throws IOException { if (value == null) { result.append("null"); return; } if (value instanceof CharSequence) { appendString(result, (CharSequence) value); return; } // Numbers if (value instanceof Number || value instanceof Boolean) { result.append(value.toString()); return; } // Characters if (value instanceof Character) { appendString(result, (CharSequence) value); return; } // Complex types // Check the references final String path = objectRefs.get(value); if (path != null) { if (refs) { result.append("{\"$ref\":"); appendString(result, path); result.append("}"); } else throw new IllegalArgumentException( "Cycles are not allowed by default. You can override this in the JSON object setAllowReferences(). "); } objectRefs.put(value, refpath); try { if (value instanceof DTOs) value = ((DTOs) value).toMap(); if (value instanceof Map) { Map< ? , ? > map = (Map< ? , ? >) value; incr('{'); String delim = ""; for (Map.Entry< ? , ? > entry : map.entrySet()) { result.append(delim); String name = String.valueOf(entry.getKey()); indent(); appendString(result, name); result.append(":"); final Object v = entry.getValue(); append(refpath + "/" + name, v); if ( indent > 0) { result.append('\n'); indent(); } else delim = ", "; } decr('}'); } else if (value instanceof Iterable) { incr('['); int i = 0; for (Object item : (Iterable< ? >) value) { if (i > 0) { result.append(","); } append(refpath + "/" + i, item); i++; } result.append("]"); decr(']'); } else if (value.getClass().isArray()) { // // Handle arrays // incr('['); String del =""; final int length = Array.getLength(value); for (int i = 0; i < length; i++) { result.append(del); append(refpath + "/" + i, Array.get(value, i)); del = ", "; } decr(']'); } else { if (!(value instanceof CharSequence)) value = value.toString(); appendString(result, (CharSequence) value); } } finally { if (!refs) objectRefs.remove(value); } } private void incr(char c) throws IOException { result.append(c).append("\n"); if (indent > 0) { level++; indent(); } } private void indent() throws IOException { for ( int i=0; i<level*indent; i++) { result.append(' '); } } private void decr(char c) throws IOException { result.append(c).append("\n"); if (indent > 0) { level--; indent(); } } /** * Append the specified string to the specified StringBuilder. * * @param result * StringBuilder to which the string is appended. * @param string * The string to be appended. * @return The specified StringBuilder. * @throws IOException */ private void appendString(final Appendable result, CharSequence string) throws IOException { if (limit > 0 && limit >= string.length()) { StringBuilder sb = new StringBuilder(string.subSequence(0, limit / 2)); sb.append("..."); sb.append(string.subSequence(limit / 2, string.length())); string = sb; } result.append("\""); for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); switch (c) { case '"' : case '\\' : result.append('\\').append(c); break; // fall through default : if (c < 0x20) { String hex = Integer.toHexString(c | 0x10000); result.append("\\u").append(hex.substring(1)); } else result.append(c); } } result.append("\""); } public String toString() { return result.toString(); } } public JSON references() { refs = true; return this; } public JSON limit(int limit) { this.limit = limit; return this; } public JSON indent(int indent) { this.indent = indent; return this; } }