static bool structure_showTiming, structure_checkTokenCount; static S structure(O o) { ret structure(o, new structure_Data); } static S structure(O o, structure_Data d) { new StringWriter sw; d.out = new PrintWriter(sw); structure_go(o, d); S s = str(sw); if (structure_checkTokenCount) { print("token count=" + d.n); assertEquals("token count", l(javaTokC(s)), d.n); } ret s; } static void structure_go(O o, structure_Data d) { structure_1(o, d); while (nempty(d.stack)) popLast(d.stack).run(); } static void structureToPrintWriter(O o, PrintWriter out) { new structure_Data d; d.out = out; structure_go(o, d); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static class structure_Data { PrintWriter out; int stringSizeLimit; int shareStringsLongerThan = 20; new IdentityHashMap seen; new BitSet refd; new HashMap strings; new HashSet concepts; HashMap> fieldsByClass = new HashMap; Class conceptClass = findClass("Concept"); int n; // token count new L stack; // append single token structure_Data append(S token) { out.print(token); ++n; ret this; } structure_Data append(int i) { out.print(i); ++n; ret this; } // append multiple tokens structure_Data append(S token, int tokCount) { out.print(token); n += tokCount; ret this; } // extend last token structure_Data app(S token) { out.print(token); ret this; } structure_Data app(int i) { out.print(i); ret this; } } static void structure_1(final Object o, final structure_Data d) { final PrintWriter out = d.out; if (o == null) { d.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) { int i = ((Int) o).intValue(); out.print(i); d.n += i < 0 ? 2 : 1; ret; } if (o instanceof Long) { long l = ((Long) o).longValue(); out.print(l); out.print("L"); d.n += l < 0 ? 2 : 1; ret; } if (o instanceof Float) { d.append("fl ", 2); quoteToPrintWriter(str(o), out); ret; } if (o instanceof Double) { d.append("d(", 3); quoteToPrintWriter(str(o), out); d.append(")"); ret; } if (o instanceof BigInteger) { out.print("bigint("); out.print(o); out.print(")"); d.n += ((BigInteger) o).signum() < 0 ? 5 : 4; ret; } } if (o instanceof Boolean) { d.append(((Boolean) o).booleanValue() ? "t" : "f"); ret; } if (o instanceof Character) { d.append(quoteCharacter((Character) o)); ret; } if (o instanceof File) { d.append("File ").append(quote(((File) o).getPath())); ret; } // referencable objects follow Integer ref = d.seen.get(o); if (o instanceof S && ref == null) ref = d.strings.get((S) o); if (ref != null) { d.refd.set(ref); d.append("t").app(ref); ret; } ref = d.n; //d.seen.size()+1; if (d.shareStringsLongerThan < Int.MAX_VALUE || !o instanceof S) d.seen.put(o, ref); //d.append("m").app(ref).app(" "); // marker if (o instanceof S) { S s = d.stringSizeLimit != 0 ? shorten((S) o, d.stringSizeLimit) : (S) o; if (l(s) >= d.shareStringsLongerThan) d.strings.put(s, ref); quoteToPrintWriter(s, out); d.n++; ret; } if (o instanceof HashSet) { d.append("hashset "); structure_1(new ArrayList((Set) o), d); ret; } if (o instanceof TreeSet) { d.append("treeset "); structure_1(new ArrayList((Set) o), d); ret; } if (o instanceof Collection && neq(name, "main$Concept$RefL")) { d.append("["); final int l = d.n; final Iterator it = ((Collection) o).iterator(); d.stack.add(r { if (!it.hasNext()) d.append("]"); else { d.stack.add(this); if (d.n != l) d.append(", "); structure_1(it.next(), d); } }); ret; } if (o instanceof Map) { if (o instanceof LinkedHashMap) d.append("lhm"); else if (o instanceof HashMap) d.append("hm"); d.append("{"); final int l = d.n; final Iterator it = ((Map) o).entrySet().iterator(); d.stack.add(new Runnable() { bool v; Map.Entry e; public void run() { if (v) { d.append("="); v = false; d.stack.add(this); structure_1(e.getValue(), d); } else { if (!it.hasNext()) d.append("}"); else { e = (Map.Entry) it.next(); v = true; d.stack.add(this); if (d.n != l) d.append(", "); structure_1(e.getKey(), d); } } } }); ret; } if (c.isArray()) { if (o instanceof byte[]) { d.append("ba ").append(quote(bytesToHex((byte[]) o))); ret; } final 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; d.append("boolarray ").append(n).app(" ").append(quote(substring(hex, 0, i))); ret; } S atype = "array", sep = ", "; if (o instanceof int[]) { //ret "intarray " + quote(intArrayToHex((int[]) o)); atype = "intarray"; sep = " "; } d.append(atype).append("{"); d.stack.add(new Runnable() { int i; public void run() { if (i >= n) d.append("}"); else { d.stack.add(this); if (i > 0) d.append(", "); structure_1(Array.get(o, i++), d); } } }); ret; } if (o instanceof Class) { d.append("class(", 2).append(quote(((Class) o).getName())).append(")"); ret; } if (o instanceof Throwable) { d.append("exception(", 2).append(quote(((Throwable) o).getMessage())).append(")"); ret; } if (o instanceof BitSet) { BitSet bs = (BitSet) o; d.append("bitset{", 2); int l = d.n; for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { if (d.n != l) d.append(", "); d.append(i); } d.append("}"); ret; } // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) { d.append("j ").append(quote(str(o))); ret; // Hm. this is not unstructure-able } ifclass Lisp if (o << Lisp) { d.append("l(", 2); final Lisp lisp = cast o; structure_1(lisp.head, d); final Iterator it = lisp.args.iterator(); d.stack.add(r { if (!it.hasNext()) d.append(")"); else { d.stack.add(this); d.append(", "); structure_1(it.next(), d); } }); ret; } endif /*if (name.equals("main$Lisp")) { fail("lisp not supported right now"); }*/ if (concept && !d.concepts.contains(dynName)) { d.concepts.add(dynName); d.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); // Render this$1 first because unstructure needs it for constructor call. 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; d.append(shortName); final int l = d.n; final Iterator it = fv.entrySet().iterator(); d.stack.add(r { if (!it.hasNext()) { if (d.n != l) d.append(")"); } else { Map.Entry e = (Map.Entry) it.next(); d.append(d.n == l ? "(" : ", "); d.append((S) e.getKey()).append("="); d.stack.add(this); structure_1(e.getValue(), d); } }); }