@@ -3,6 +3,7 @@ package stateless
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "html"
6
7
"sort"
7
8
"strings"
8
9
"text/template"
@@ -12,6 +13,13 @@ import (
12
13
type graph struct {
13
14
}
14
15
16
+ type transitionLabel struct {
17
+ reentry []string
18
+ internal []string
19
+ transitioning []string
20
+ ignored []string
21
+ }
22
+
15
23
func (g * graph ) formatStateMachine (sm * StateMachine ) string {
16
24
var sb strings.Builder
17
25
sb .WriteString ("digraph {\n \t compound=true;\n \t node [shape=Mrecord];\n \t rankdir=\" LR\" ;\n \n " )
@@ -34,7 +42,7 @@ func (g *graph) formatStateMachine(sm *StateMachine) string {
34
42
dest := sm .stateConfig [sr .InitialTransitionTarget ]
35
43
if dest != nil {
36
44
src := clusterStr (sr .State , true , true )
37
- formatOneLine (& sb , src , str (dest .State , true ), "" )
45
+ formatOneLine (& sb , src , str (dest .State , true ), `""` )
38
46
}
39
47
}
40
48
}
@@ -124,7 +132,7 @@ func (g *graph) formatAllStateTransitions(sb *strings.Builder, sm *StateMachine,
124
132
destination State
125
133
}
126
134
127
- lines := make (map [line ][] string , len (triggerList ))
135
+ lines := make (map [line ]transitionLabel , len (triggerList ))
128
136
order := make ([]line , 0 , len (triggerList ))
129
137
for _ , trigger := range triggerList {
130
138
switch t := trigger .(type ) {
@@ -133,21 +141,27 @@ func (g *graph) formatAllStateTransitions(sb *strings.Builder, sm *StateMachine,
133
141
if _ , ok := lines [ln ]; ! ok {
134
142
order = append (order , ln )
135
143
}
136
- lines [ln ] = append (lines [ln ], formatOneTransition (t .Trigger , nil , t .Guard ))
144
+ transition := lines [ln ]
145
+ transition .ignored = append (transition .ignored , formatOneTransition (t .Trigger , nil , t .Guard ))
146
+ lines [ln ] = transition
137
147
case * reentryTriggerBehaviour :
138
148
actions := g .getEntryActions (sr .EntryActions , t .Trigger )
139
149
ln := line {sr .State , t .Destination }
140
150
if _ , ok := lines [ln ]; ! ok {
141
151
order = append (order , ln )
142
152
}
143
- lines [ln ] = append (lines [ln ], formatOneTransition (t .Trigger , actions , t .Guard ))
153
+ transition := lines [ln ]
154
+ transition .reentry = append (transition .reentry , formatOneTransition (t .Trigger , actions , t .Guard ))
155
+ lines [ln ] = transition
144
156
case * internalTriggerBehaviour :
145
157
actions := g .getEntryActions (sr .EntryActions , t .Trigger )
146
158
ln := line {sr .State , sr .State }
147
159
if _ , ok := lines [ln ]; ! ok {
148
160
order = append (order , ln )
149
161
}
150
- lines [ln ] = append (lines [ln ], formatOneTransition (t .Trigger , actions , t .Guard ))
162
+ transition := lines [ln ]
163
+ transition .internal = append (transition .internal , formatOneTransition (t .Trigger , actions , t .Guard ))
164
+ lines [ln ] = transition
151
165
case * transitioningTriggerBehaviour :
152
166
src := sm .stateConfig [sr .State ]
153
167
if src == nil {
@@ -168,16 +182,54 @@ func (g *graph) formatAllStateTransitions(sb *strings.Builder, sm *StateMachine,
168
182
if _ , ok := lines [ln ]; ! ok {
169
183
order = append (order , ln )
170
184
}
171
- lines [ln ] = append (lines [ln ], formatOneTransition (t .Trigger , actions , t .Guard ))
185
+ transition := lines [ln ]
186
+ transition .transitioning = append (transition .transitioning , formatOneTransition (t .Trigger , actions , t .Guard ))
187
+ lines [ln ] = transition
172
188
case * dynamicTriggerBehaviour :
173
189
// TODO: not supported yet
174
190
}
175
191
}
176
192
177
193
for _ , ln := range order {
178
194
content := lines [ln ]
179
- formatOneLine (sb , str (ln .source , true ), str (ln .destination , true ), strings .Join (content , "\\ n" ))
195
+ formatOneLine (sb , str (ln .source , true ), str (ln .destination , true ), toTransitionsLabel (content ))
196
+ }
197
+ }
198
+
199
+ func toTransitionsLabel (transitions transitionLabel ) string {
200
+ var sb strings.Builder
201
+ sb .WriteString (`<<TABLE BORDER="0">` )
202
+ for _ , t := range transitions .transitioning {
203
+ sb .WriteString (`<TR><TD ALIGN="LEFT">` )
204
+ sb .WriteString (html .EscapeString (t ))
205
+ sb .WriteString (`</TD></TR>` )
206
+ }
207
+ if len (transitions .reentry ) > 0 {
208
+ sb .WriteString (`<TR><TD><B>Reentry</B></TD></TR>` )
209
+ for _ , t := range transitions .reentry {
210
+ sb .WriteString (`<TR><TD ALIGN="LEFT">` )
211
+ sb .WriteString (html .EscapeString (t ))
212
+ sb .WriteString (`</TD></TR>` )
213
+ }
214
+ }
215
+ if len (transitions .internal ) > 0 {
216
+ sb .WriteString (`<TR><TD><B>Internal</B></TD></TR>` )
217
+ for _ , t := range transitions .internal {
218
+ sb .WriteString (`<TR><TD ALIGN="LEFT">` )
219
+ sb .WriteString (html .EscapeString (t ))
220
+ sb .WriteString (`</TD></TR>` )
221
+ }
180
222
}
223
+ if len (transitions .ignored ) > 0 {
224
+ sb .WriteString (`<TR><TD><B>Ignored</B></TD></TR>` )
225
+ for _ , t := range transitions .ignored {
226
+ sb .WriteString (`<TR><TD ALIGN="LEFT">` )
227
+ sb .WriteString (html .EscapeString (t ))
228
+ sb .WriteString (`</TD></TR>` )
229
+ }
230
+ }
231
+ sb .WriteString (`</TABLE>>` )
232
+ return sb .String ()
181
233
}
182
234
183
235
func formatOneTransition (trigger Trigger , actions []string , guards transitionGuard ) string {
@@ -197,7 +249,7 @@ func formatOneTransition(trigger Trigger, actions []string, guards transitionGua
197
249
}
198
250
199
251
func formatOneLine (sb * strings.Builder , fromNodeName , toNodeName , label string ) {
200
- sb .WriteString (fmt .Sprintf ("\t %s -> %s [label=\" %s \" " , fromNodeName , toNodeName , label ))
252
+ sb .WriteString (fmt .Sprintf ("\t %s -> %s [label=%s " , fromNodeName , toNodeName , label ))
201
253
sb .WriteString ("];\n " )
202
254
}
203
255
0 commit comments