11package jtamaro .interaction ;
22
3+ import java .awt .Font ;
4+ import java .awt .Insets ;
35import java .awt .event .WindowAdapter ;
46import java .awt .event .WindowEvent ;
7+ import java .lang .reflect .InvocationTargetException ;
8+ import java .lang .reflect .Method ;
9+ import java .util .Arrays ;
10+ import java .util .logging .Level ;
11+ import java .util .logging .Logger ;
12+ import java .util .stream .Collectors ;
13+ import javax .swing .BorderFactory ;
514import javax .swing .JFrame ;
615import javax .swing .JScrollPane ;
716import javax .swing .JTextArea ;
817import jtamaro .data .Sequence ;
918
1019final class ModelFrame <M > extends JFrame {
1120
12- private final JTextArea textArea ;
21+ private static final Logger LOGGER = Logger .getLogger (ModelFrame .class .getName ());
22+
23+ private static final String EOL = System .lineSeparator ();
1324
1425 public ModelFrame (Interaction <M > bang , Trace <M > trace ) {
1526 super ();
1627 setTitle ("Model" );
17- textArea = new JTextArea (3 , 40 );
18- add (new JScrollPane (textArea ));
28+ final JTextArea textArea = new JTextArea ();
29+ textArea .setMargin (new Insets (8 , 8 , 8 , 8 ));
30+ textArea .setFont (new Font (Font .MONOSPACED , Font .PLAIN , 12 ));
31+ textArea .setEditable (false );
32+ textArea .setFocusable (false );
33+ final JScrollPane scrollPane = new JScrollPane (textArea );
34+ scrollPane .setBorder (BorderFactory .createEmptyBorder ());
35+ add (scrollPane );
1936
2037 final TraceListener tl = ev -> {
2138 final M model = getLastModel (trace .getEventSequence (), bang .getInitialModel ());
22- textArea .setText (model . toString ( ));
39+ textArea .setText (prettyPrint ( model , 0 ));
2340 };
2441 // Load current model
2542 tl .eventAppended (null );
@@ -40,4 +57,57 @@ private M getLastModel(Sequence<TraceEvent<M>> events, M model) {
4057 ? model
4158 : events .first ().process (getLastModel (events .rest (), model ));
4259 }
60+
61+ private String prettyPrint (Object obj , int indent ) {
62+ return switch (obj ) {
63+ case Sequence <?> seq -> prettyPrintSequence (seq , indent + 1 );
64+ case Record rec -> prettyPrintRecord (rec , indent + 1 );
65+ default -> obj .toString ();
66+ };
67+ }
68+
69+ private String prettyPrintSequence (Sequence <?> seq , int indent ) {
70+ final String prefix = " " .repeat (indent ) + "• " ;
71+ return seq .map (it -> prefix + prettyPrint (it , indent ))
72+ .reduce ("" , (it , acc ) -> acc + EOL + it );
73+ }
74+
75+ private String prettyPrintRecord (Record rec , int indent ) {
76+ final Class <?> clazz = rec .getClass ();
77+ final String recName = clazz .getSimpleName ();
78+ final String prefix = " " .repeat (indent ) + "• " ;
79+
80+ final String contents = Arrays .stream (clazz .getRecordComponents ()).map (component -> {
81+ final String compName = component .getName ();
82+ final StringBuilder compSb = new StringBuilder ()
83+ .append (prefix )
84+ .append (compName )
85+ .append (": " );
86+ try {
87+ final Method accessor = component .getAccessor ();
88+ final boolean isAccessible = accessor .canAccess (rec );
89+ if (!isAccessible ) {
90+ accessor .setAccessible (true );
91+ }
92+
93+ compSb .append (prettyPrint (accessor .invoke (rec ), indent ));
94+
95+ if (!isAccessible ) {
96+ accessor .setAccessible (false );
97+ }
98+ } catch (IllegalAccessException | InvocationTargetException e ) {
99+ LOGGER .log (Level .WARNING , "Failed to read component"
100+ + compName
101+ + " of record class "
102+ + recName , e );
103+ compSb .append ("???" );
104+ }
105+
106+ return compSb .toString ();
107+ }).collect (Collectors .joining (EOL ));
108+
109+ return recName
110+ + EOL
111+ + contents ;
112+ }
43113}
0 commit comments