srecord noeq G22JavaObjectVisualizer(G22Utils g22utils) is Swingable { settable bool withTypeAndTime = true; settable bool singleLineLayout; // TODO settable int maxElements = 1000; // max elements of collection to show settable bool horizontal; Set seen = identityHashSet(); transient settable O object; transient settable long nanos = -1; *(G22Utils *g22utils, O *object) {} // call after visualizeObject (so properties are filled) S objectInfos() { new LS infos; infos.add(str(shortClassName(object))); // shows "null" for null value for (key, val : mainVisualizer().properties) { S s = n2OrStrOrNull(val); if (nempty(s)) infos.add(key + " " + s); } ret joinNemptiesWithComma(infos); } cachedVisualize { printVars("Visualizing", +this, +withTypeAndTime); JComponent c = visualizeObject(object); if (withTypeAndTime) { S type = objectInfos(); S timeDesc = nanos < 0 ? "" : formatElapsedTimeWithAppropriateUnit(nanosToSeconds(nanos)); printVars(+nanos, +timeDesc); c = northAndCenterWithMargins( westAndEastWithMargin( withLabel("Type:", jlabel(type)), empty(timeDesc) ? jpanel() : withLabel("Execution time:", jlabel(timeDesc)) ), c); } ret c; } JComponent visualizeObject(O object) { ret mainVisualizer().visualize(object); } simplyCached ObjectVisualizer mainVisualizer() { ret new ObjectVisualizer().recurse(true).bigFont(true).horizontal(horizontal); } class ObjectVisualizer { settable bool recurse; settable bool bigFont; settable bool horizontal; new LinkedHashMap properties; // only one recursion level so far ObjectVisualizer subVisualizer() { ret new ObjectVisualizer().horizontal(!horizontal); } JComponent makeBig(JComponent c) { if (bigFont) ret fontSizePlus(10, c); ret c; } JComponent visualize(O object) { try { ret visualize2(object); } catch e { ret jErrorView(new RuntimeException("Visualization error", e)); } } JComponent visualize2(O object) { // exception if (object cast Throwable) ret jErrorView(object); // null or empty string if (object == null || eq(object, "")) ret jpanel(); // Swing component if (object cast Swingable) object = object.visualize(); if (object cast JComponent && getParent(object) == null) ret object; // number if (object cast Int) object = toLong(object); if (object cast Long) ret makeBig(jcenteredLabel(n2(object))); if (object cast Number) ret makeBig(jcenterNarrowLabel(str(object))); if (object cast Pair) { if (horizontal) ret jcenteredline(jlabel("<"), visualize(object.a), jlabel(","), visualize(object.b), jlabel(">")); else ret vstackWithSpacing(visualize(object.a), visualize(object.b)); } // image if (object cast MakesBufferedImage) object = toBufferedImage(object); if (object cast BufferedImage) { var is = g22utils.stdImageSurface(object); if (!recurse) is.setAutoZoomToDisplay(false); ret jscroll_centered_borderless(is); } if (recurse) { // TODO /*if (!seen.add(object)) ret jlabel("Object already seen");*/ // multi-map ifclass MultiMap if (object cast MultiMap) { propertyLength(l(object)); propertyKeyCount(object.keyCount()); object = multiMapToMapPairs(object); } endif ifclass MultiSetMap if (object cast MultiSetMap) { propertyLength(l(object)); propertyKeyCount(object.keyCount()); object = multiSetMapToMapPairs(object); } endif // map if (object cast Map) { propertyLength(l(object)); object = mapToPairs(object); } // collection if (object cast Collection) { propertyLength(l(object)); var sub = subVisualizer(); var elements = map(cloneTakeFirst(maxElements, object), element -> sub.visualize(element)); ret jscroll_centered_borderless(horizontal ? hstackWithSpacing(elements) : vstackWithSpacing(elements); } if (isArray(object)) propertyLength(arrayLength(object)); } if (object cast S) { propertyLength(l(object)); } S string = str(object); if (containsNewLine(string)) ret jscroll_borderless(wordWrapTypeWriterTextArea(string)); var lbl = jcenterNarrowLabel(shorten(string)); if (l(string) <= 10) ret makeBig(lbl); ret lbl; } // end of visualize2 void propertyLength(int l) { properties.put("size", l); } void propertyKeyCount(int l) { properties.put("keys", l); } } // end of ObjectVisualizer }