sbool structure_showTiming; static String structure(Object o) { new structure_Data d; structure_1(o, d); long time = now(); S s = str(d.out); int nOrig = l(s); d.out = null; s = structure_2(s, d.refd); if (structure_showTiming) done_always("structure_2 (" + nOrig + " => " + l(s) + ")", time); ret s; } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static int structure_shareStringsLongerThan = 20; static class structure_Data { new StringBuilder out; int stringSizeLimit; new IdentityHashMap seen; new BitSet refd; new HashMap strings; new HashSet concepts; HashMap> fieldsByClass = new HashMap; Class conceptClass = findClass("Concept"); } static void structure_1(Object o, structure_Data d) { StringBuilder out = d.out; if (o == null) { out.append("null"); ret; } Class c = o.getClass(); S name = c.getName(); S dynName = shortDynamicClassName(o); bool concept = d.conceptClass != null && d.conceptClass.isInstance(o); L lFields = d.fieldsByClass.get(c); if (lFields == null) { // these are never back-referenced (for readability) if (o instanceof Number) { if (o instanceof Integer) { out.append((int) o); ret; } if (o instanceof Long) { out.append((long) o).append("L"); ret; } if (o instanceof Float) { out.append("fl "); quote_impl(str(o), out); ret; } if (o instanceof Double) { out.append("d("); quote_impl(str(o), out); out.append(")"); ret; } if (o instanceof BigInteger) { out.append("bigint(").append(o).append(")"); ret; } } if (o instanceof Boolean) { out.append(((Boolean) o).booleanValue() ? "t" : "f"); ret; } if (o instanceof Character) { out.append(quoteCharacter((Character) o)); ret; } if (o instanceof File) { out.append("File ").append(quote(((File) o).getPath())); ret; } // referencable objects follow Integer ref = null; if (!(o instanceof S)) { ref = d.seen.get(o); if (ref != null) { d.refd.set(ref); out.append("r").append(ref); ret; } } if (o instanceof S && (ref = d.strings.get((S) o)) != null) { d.refd.set(ref); out.append("r").append(ref); ret; } ref = d.seen.size()+1; d.seen.put(o, ref); out.append("m").append(ref).append(" "); // marker if (o instanceof S) { S s = d.stringSizeLimit != 0 ? shorten((S) o, d.stringSizeLimit) : (S) o; if (l(s) >= structure_shareStringsLongerThan) d.strings.put(s, ref); quote_impl(s, out); ret; } if (o instanceof HashSet) { out.append("hashset "); structure_1(new ArrayList((Set) o), d); ret; } if (o instanceof TreeSet) { out.append("treeset "); structure_1(new ArrayList((Set) o), d); ret; } if (o instanceof Collection && neq(name, "main$Concept$RefL")) { out.append("["); int l = out.length(); for (Object x : (Collection) o) { if (out.length() != l) out.append(", "); structure_1(x, d); } out.append("]"); ret; } if (o instanceof Map) { if (o instanceof HashMap) out.append("hm"); out.append("{"); int l = out.length(); for (Object e : ((Map) o).entrySet()) { if (out.length() != l) out.append(", "); structure_1(((Map.Entry) e).getKey(), d); out.append("="); structure_1(((Map.Entry) e).getValue(), d); } out.append("}"); ret; } if (c.isArray()) { if (o instanceof byte[]) { out.append("ba ").append(quote(bytesToHex((byte[]) o))); ret; } int n = Array.getLength(o); if (o instanceof bool[]) { S hex = boolArrayToHex((bool[]) o); int i = l(hex); while (i > 0 && hex.charAt(i-1) == '0' && hex.charAt(i-2) == '0') i -= 2; out.append("boolarray ").append(n).append(" ").append(quote(substring(hex, 0, i))); ret; } S atype = "array", sep = ", "; if (o instanceof int[]) { //ret "intarray " + quote(intArrayToHex((int[]) o)); atype = "intarray"; sep = " "; } out.append(atype).append("{"); for (int i = 0; i < n; i++) { if (i != 0) out.append(sep); structure_1(Array.get(o, i), d); } out.append("}"); ret; } if (o instanceof Class) { out.append("class(").append(quote(((Class) o).getName())).append(")"); ret; } if (o instanceof Throwable) { out.append("exception(").append(quote(((Throwable) o).getMessage())).append(")"); ret; } if (o instanceof BitSet) { BitSet bs = (BitSet) o; out.append("bitset{"); int l = out.length(); for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { if (out.length() != l) out.append(", "); out.append(i); } out.append("}"); ret; } // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) { out.append(str(o)); ret; } if (name.equals("main$Lisp")) { out.append("l("); structure_1(getOpt(o, "head"), d); L args = cast getOpt(o, "args"); if (nempty(args)) for (int i = 0; i < l(args); i++) { out.append(", "); O arg = args.get(i); // sweet shortening if (arg != null && eq(arg.getClass().getName(), "main$Lisp") && isTrue(call(arg, "isEmpty"))) arg = get(arg, "head"); structure_1(arg, d); } out.append(")"); ret; } if (concept && !d.concepts.contains(dynName)) { d.concepts.add(dynName); out.append("c "); } // serialize an object with fields. // first, collect all fields and values in fv. TreeSet fields = new TreeSet(new Comparator() { public int compare(Field a, Field b) { ret stdcompare(a.getName(), b.getName()); } }); while (c != Object.class) { for (Field field : getDeclaredFields_cached(c)) { if ((field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) continue; S fieldName = field.getName(); fields.add(field); // put special cases here... } c = c.getSuperclass(); } lFields = asList(fields); // put this$1 first for (int i = 0; i < l(lFields); i++) { Field f = lFields.get(i); if (f.getName().equals("this$1")) { lFields.remove(i); lFields.add(0, f); break; } } d.fieldsByClass.put(c, lFields); } new LinkedHashMap fv; for (Field f : lFields) { Object value; try { value = f.get(o); } catch (Exception e) { value = "?"; } if (value != null) fv.put(f.getName(), value); } String shortName = dropPrefix("main$", name); // Now we have fields & values. Process fieldValues if it's a DynamicObject. // omit field "className" if equal to class's name if (concept && eq(fv.get("className"), shortName)) fv.remove("className"); if (o instanceof DynamicObject) { fv.putAll((Map) fv.get("fieldValues")); fv.remove("fieldValues"); shortName = dynName; fv.remove("className"); } S singleField = fv.size() == 1 ? first(fv.keySet()) : null; // Render this$1 first because unstructure needs it for constructor call. out.append(shortName).append("("); int l = out.length(); for (S fieldName : fv.keySet()) { if (out.length() != l) out.append(", "); out.append(fieldName).append("="); structure_1(fv.get(fieldName), d); } if (out.length() == l) out.setLength(l-1); // drop "(" else out.append(")"); } // drop unused markers static S structure_2(final S s, final BitSet refd) { final new StringBuilder out; javaTok_streaming(s, new JavaTok_Stream { int n = -1; bool drop; void add(int i, int j) { ++n; if ((n & 1) != 0 && structure_isMarker(s, i, j)) { S t = s.substring(i+1, j); if (!refd.get(parseInt(t))) { drop = true; ret; } } if (drop) drop = false; else out.append(s, i, j); } }); ret str(out); }