static String structureConcept(Object o) { new HashSet refd; new StringBuilder out; structureConcept_1(out, o, new structureConcept_Data(refd)); return structureConcept_2(str(out), refd); } // leave to false, unless unstructure() breaks static boolean structureConcept_allowShortening = false; static int structureConcept_shareStringsLongerThan = 20; static class structureConcept_Data { int stringSizeLimit; new IdentityHashMap seen; HashSet refd; new HashMap strings; new HashSet concepts; Class conceptClass = findClass("Concept"); bool markConcepts; new Map unlisted; // unlisted empty instances to check default field values *(HashSet *refd) {} } static void structureConcept_1(StringBuilder out, Object o, structureConcept_Data d) { if (o == null) { out.append("null"); ret; } // these are never back-referenced (for readability) if (o instanceof BigInteger) { out.append("bigint(").append(o).append(")"); ret; } if (o instanceof Double) { out.append("d(").append(quote(str(o))).append(")"); ret; } if (o instanceof Float) { out.append("fl ").append(quote(str(o))); ret; } if (o instanceof Long) { out.append(o).append("L"); ret; } if (o instanceof Integer) { out.append(str(o)); 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 = d.seen.get(o); if (ref != null) { d.refd.add(ref); out.append("r").append(ref); ret; } if (o instanceof S && (ref = d.strings.get((S) o)) != null) { d.refd.add(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) >= structureConcept_shareStringsLongerThan) d.strings.put(s, ref); out.append(quote(s)); ret; } String name = o.getClass().getName(); if (o instanceof HashSet) { out.append("hashset "); structureConcept_1(out, new ArrayList((Set) o), d); ret; } if (o instanceof TreeSet) { out.append("treeset "); structureConcept_1(out, new ArrayList((Set) o), d); ret; } if (o instanceof Collection && neq(name, "main$Concept$RefL")) { if (!o instanceof ArrayList) out.append(shortDynamicClassName(o)); out.append("["); int l = out.length(); for (Object x : (Collection) o) { if (out.length() != l) out.append(", "); structureConcept_1(out, deref(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(", "); structureConcept_1(out, ((Map.Entry) e).getKey(), d); out.append("="); structureConcept_1(out, ((Map.Entry) e).getValue(), d); } out.append("}"); ret; } if (o.getClass().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); structureConcept_1(out, 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; } String shortName = o.getClass().getName().replaceAll("^main\\$", ""); if (shortName.equals("Lisp")) { out.append("l("); structureConcept_1(out, 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"); structureConcept_1(out, arg, d); } out.append(")"); ret; } Class c = o.getClass(); bool concept = d.conceptClass != null && d.conceptClass.isInstance(o); S dynName = shortDynamicClassName(o); if (concept && !d.concepts.contains(dynName)) { d.concepts.add(dynName); if (d.markConcepts) out.append("c "); } // serialize an object with fields. // first, collect all fields and values in fv. Map fv = new TreeMap; while (c != Object.class) { for (Field field : c.getDeclaredFields()) { if ((field.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) continue; S fieldName = field.getName(); Object value; try { field.setAccessible(true); value = field.get(o); } catch (Exception e) { value = "?"; } // omit field "className" if equal to class's name if (concept && eq(fieldName, "className") && eq(value, shortName)) value = null; // put special cases here... if (value != null) fv.put(fieldName, value); } c = c.getSuperclass(); } // Now we have fields & values. Process fieldValues if it's a DynamicObject. if (o instanceof DynamicObject) { fv.putAll((Map) fv.get("fieldValues")); fv.remove("fieldValues"); shortName = dynName; fv.remove("className"); } S singleField = null; if (o instanceof ConceptL) { out.append(shortName).append("["); int l = out.length(); for (Object x : o/ConceptL.l) { if (out.length() != l) out.append(", "); structureConcept_1(out, deref(x), d); } out.append("]"); ret; } if (o instanceof Str && empty(o/Str.otherNames)) { out/*.append("Str ")*/.append(quote(o/Str.name)); ret; } if (o instanceof Concept) { Concept cpt = cast o; Set fields = conceptFields(cpt); if (l(fields) == 1) singleField = first(fields); Concept unl = d.unlisted.get(dynName); if (unl == null) d.unlisted.put(dynName, unl = unlisted(cpt.getClass())); fv.clear(); for (S field : fields) { O val = cget(cpt, field); if (val != null && neq(cget(unl, field), val)) fv.put(field, val); } } S fieldOrder = toStringOpt(getOpt(o, "_fieldOrder")); // Render this$1 first because unstructure needs it for constructor call. out.append(shortName).append("("); int l = out.length(); if (fv.containsKey("this$1")) { out.append("this$1="); structureConcept_1(out, fv.get("this$1"), d); fv.remove("this$1"); } // Render the other fields. if (fieldOrder != null) fv = putKeysFirst(fv, toObjectArray(splitAtSpace(fieldOrder))); for (S fieldName : keys(fv)) { if (out.length() != l) out.append(", "); if (neq(fieldName, singleField)) out.append(fieldName).append("="); structureConcept_1(out, fv.get(fieldName), d); } if (out.length() == l) out.setLength(l-1); // drop "(" else out.append(")"); } // drop unused markers static S structureConcept_2(S s, HashSet refd) { L tok = javaTok(s); new StringBuilder out; for (int i = 1; i < l(tok); i += 2) { S t = tok.get(i); if (t.startsWith("m") && isInteger(t.substring(1)) && !refd.contains(parseInt(t.substring(1)))) continue; out.append(t).append(tok.get(i+1)); } ret str(out); }