@@ -259,6 +259,298 @@ func (fb *flowBuilder) addStructuredInheritanceSplit(s *ast.InheritanceSplitStmt
259259 return splitID
260260}
261261
262+ func (fb * flowBuilder ) addEnumSplit (s * ast.EnumSplitStmt ) model.ID {
263+ splitX := fb .posX
264+ centerY := fb .posY
265+ split := & microflows.ExclusiveSplit {
266+ BaseMicroflowObject : microflows.BaseMicroflowObject {
267+ BaseElement : model.BaseElement {ID : model .ID (types .GenerateID ())},
268+ Position : model.Point {X : splitX , Y : centerY },
269+ Size : model.Size {Width : SplitWidth , Height : SplitHeight },
270+ },
271+ Caption : "$" + s .Variable ,
272+ SplitCondition : & microflows.ExpressionSplitCondition {
273+ BaseElement : model.BaseElement {ID : model .ID (types .GenerateID ())},
274+ Expression : "$" + s .Variable ,
275+ },
276+ ErrorHandlingType : microflows .ErrorHandlingTypeRollback ,
277+ }
278+ fb .objects = append (fb .objects , split )
279+
280+ splitID := split .ID
281+ if fb .pendingAnnotations != nil {
282+ fb .applyAnnotations (splitID , fb .pendingAnnotations )
283+ fb .pendingAnnotations = nil
284+ }
285+
286+ savedEndsWithReturn := fb .endsWithReturn
287+ fb .endsWithReturn = false
288+ allBranchesReturn := len (s .Cases ) > 0 || len (s .ElseBody ) > 0
289+ branchStartX := splitX + SplitWidth + HorizontalSpacing / 2
290+ branchIndex := 0
291+ type branchTail struct {
292+ id model.ID
293+ values []string
294+ fromSplit bool
295+ }
296+ var branchTails []branchTail
297+ routePendingErrorToElse := len (s .ElseBody ) > 0 && fb .errorHandlerSkipVar != "" && s .Variable == fb .errorHandlerSkipVar
298+ pendingErrorForElse := pendingErrorHandlerState {}
299+
300+ addBranch := func (values []string , body []ast.MicroflowStatement ) {
301+ branchNumber := branchIndex
302+ branchY := centerY + branchNumber * VerticalSpacing
303+ branchIndex ++
304+ if len (body ) == 0 {
305+ allBranchesReturn = false
306+ branchTails = append (branchTails , branchTail {id : splitID , values : values , fromSplit : true })
307+ return
308+ }
309+
310+ fb .posX = branchStartX
311+ fb .posY = branchY
312+ fb .endsWithReturn = false
313+ branchEntryID := splitID
314+ branchEntryIsSplit := true
315+ if len (values ) > 1 {
316+ branchMerge := & microflows.ExclusiveMerge {
317+ BaseMicroflowObject : microflows.BaseMicroflowObject {
318+ BaseElement : model.BaseElement {ID : model .ID (types .GenerateID ())},
319+ Position : model.Point {X : branchStartX , Y : branchY },
320+ Size : model.Size {Width : MergeSize , Height : MergeSize },
321+ },
322+ }
323+ fb .objects = append (fb .objects , branchMerge )
324+ for i , value := range values {
325+ var flow * microflows.SequenceFlow
326+ if enumSplitUsesHorizontalOrigin (values ) {
327+ flow = newHorizontalFlowWithEnumCase (splitID , branchMerge .ID , value )
328+ } else {
329+ flow = newDownwardFlowWithEnumCase (splitID , branchMerge .ID , value )
330+ }
331+ if i > 0 {
332+ flow .DestinationConnectionIndex = enumSplitGroupedDestinationAnchor (AnchorLeft , i )
333+ }
334+ applyEnumGroupedControlVectors (flow , i , len (values ))
335+ fb .flows = append (fb .flows , flow )
336+ }
337+ branchEntryID = branchMerge .ID
338+ branchEntryIsSplit = false
339+ fb .posX = branchStartX + HorizontalSpacing / 2
340+ }
341+
342+ var lastID model.ID
343+ var prevAnchor * ast.FlowAnchors
344+ var pendingCase string
345+ var pendingAnchor * ast.FlowAnchors
346+ for i , stmt := range body {
347+ thisAnchor := stmtOwnAnchor (stmt )
348+ actID := fb .addStatement (stmt )
349+ if actID == "" {
350+ continue
351+ }
352+ fb .applyPendingAnnotations (actID )
353+ if lastID == "" {
354+ var flow * microflows.SequenceFlow
355+ if branchEntryIsSplit {
356+ if enumSplitUsesHorizontalOrigin (values ) {
357+ flow = newHorizontalFlowWithEnumCase (splitID , actID , enumSplitFlowCaseValue (values , 0 ))
358+ } else {
359+ flow = newDownwardFlowWithEnumCase (splitID , actID , enumSplitFlowCaseValue (values , 0 ))
360+ }
361+ } else {
362+ flow = newHorizontalFlow (branchEntryID , actID )
363+ }
364+ if thisAnchor != nil && thisAnchor .To != ast .AnchorSideUnset {
365+ flow .DestinationConnectionIndex = int (thisAnchor .To )
366+ }
367+ baseDestinationAnchor := flow .DestinationConnectionIndex
368+ if branchEntryIsSplit {
369+ applyEnumGroupedControlVectors (flow , 0 , len (values ))
370+ }
371+ fb .flows = append (fb .flows , flow )
372+ if branchEntryIsSplit {
373+ for i := 1 ; i < len (values ); i ++ {
374+ var extraFlow * microflows.SequenceFlow
375+ if enumSplitUsesHorizontalOrigin (values ) {
376+ extraFlow = newHorizontalFlowWithEnumCase (splitID , actID , values [i ])
377+ } else {
378+ extraFlow = newDownwardFlowWithEnumCase (splitID , actID , values [i ])
379+ }
380+ if thisAnchor != nil && thisAnchor .To != ast .AnchorSideUnset {
381+ extraFlow .DestinationConnectionIndex = int (thisAnchor .To )
382+ }
383+ extraFlow .DestinationConnectionIndex = enumSplitGroupedDestinationAnchor (baseDestinationAnchor , i )
384+ applyEnumGroupedControlVectors (extraFlow , i , len (values ))
385+ fb .flows = append (fb .flows , extraFlow )
386+ }
387+ }
388+ if routePendingErrorToElse && len (values ) == 0 {
389+ fb .routePendingErrorHandlerToAlternative (splitID , actID )
390+ }
391+ fb .addPendingErrorHandlerFlowForStatement (flow .OriginID , flow .DestinationID , stmt , statementsReferenceVar (body [i + 1 :], fb .errorHandlerSkipVar ))
392+ } else {
393+ var flow * microflows.SequenceFlow
394+ originAnchor := prevAnchor
395+ destAnchor := thisAnchor
396+ if pendingCase != "" {
397+ flow = newHorizontalFlowWithCase (lastID , actID , pendingCase )
398+ originAnchor , destAnchor = pendingFlowAnchors (prevAnchor , pendingAnchor , thisAnchor )
399+ pendingCase = ""
400+ pendingAnchor = nil
401+ } else {
402+ flow = newHorizontalFlow (lastID , actID )
403+ }
404+ applyUserAnchors (flow , originAnchor , destAnchor )
405+ fb .flows = append (fb .flows , flow )
406+ fb .addPendingErrorHandlerFlowForStatement (flow .OriginID , flow .DestinationID , stmt , statementsReferenceVar (body [i + 1 :], fb .errorHandlerSkipVar ))
407+ }
408+ prevAnchor = thisAnchor
409+ if fb .nextConnectionPoint != "" {
410+ lastID = fb .nextConnectionPoint
411+ fb .nextConnectionPoint = ""
412+ pendingCase = fb .nextFlowCase
413+ fb .nextFlowCase = ""
414+ pendingAnchor = fb .nextFlowAnchor
415+ fb .nextFlowAnchor = nil
416+ } else {
417+ lastID = actID
418+ }
419+ }
420+ if ! lastStmtIsReturn (body ) {
421+ allBranchesReturn = false
422+ if lastID != "" {
423+ branchTails = append (branchTails , branchTail {id : lastID })
424+ }
425+ }
426+ }
427+
428+ if routePendingErrorToElse {
429+ pendingErrorForElse = fb .capturePendingErrorHandler ()
430+ fb .clearPendingErrorHandler ()
431+ }
432+ for _ , c := range s .Cases {
433+ addBranch (enumSplitCaseValues (c ), c .Body )
434+ }
435+ if routePendingErrorToElse && fb .capturePendingErrorHandler ().isEmpty () {
436+ fb .restorePendingErrorHandler (pendingErrorForElse )
437+ }
438+ if len (s .ElseBody ) > 0 {
439+ addBranch (nil , s .ElseBody )
440+ }
441+
442+ fb .posX = branchStartX + fb .measurer .measureStatements (appendEnumBodies (s )).Width + HorizontalSpacing / 2
443+ fb .posY = centerY
444+ fb .endsWithReturn = savedEndsWithReturn
445+ if allBranchesReturn {
446+ fb .endsWithReturn = true
447+ } else if len (branchTails ) == 1 && ! branchTails [0 ].fromSplit {
448+ fb .nextConnectionPoint = branchTails [0 ].id
449+ } else if len (branchTails ) > 0 {
450+ merge := & microflows.ExclusiveMerge {
451+ BaseMicroflowObject : microflows.BaseMicroflowObject {
452+ BaseElement : model.BaseElement {ID : model .ID (types .GenerateID ())},
453+ Position : model.Point {X : fb .posX , Y : centerY },
454+ Size : model.Size {Width : MergeSize , Height : MergeSize },
455+ },
456+ }
457+ fb .objects = append (fb .objects , merge )
458+ for _ , tail := range branchTails {
459+ if tail .fromSplit {
460+ var flow * microflows.SequenceFlow
461+ if enumSplitUsesHorizontalOrigin (tail .values ) {
462+ flow = newHorizontalFlowWithEnumCase (splitID , merge .ID , enumSplitFlowCaseValue (tail .values , 0 ))
463+ } else {
464+ flow = newDownwardFlowWithEnumCase (splitID , merge .ID , enumSplitFlowCaseValue (tail .values , 0 ))
465+ }
466+ baseDestinationAnchor := flow .DestinationConnectionIndex
467+ applyEnumGroupedControlVectors (flow , 0 , len (tail .values ))
468+ fb .flows = append (fb .flows , flow )
469+ for i := 1 ; i < len (tail .values ); i ++ {
470+ var extraFlow * microflows.SequenceFlow
471+ if enumSplitUsesHorizontalOrigin (tail .values ) {
472+ extraFlow = newHorizontalFlowWithEnumCase (splitID , merge .ID , tail .values [i ])
473+ } else {
474+ extraFlow = newDownwardFlowWithEnumCase (splitID , merge .ID , tail .values [i ])
475+ }
476+ extraFlow .DestinationConnectionIndex = enumSplitGroupedDestinationAnchor (baseDestinationAnchor , i )
477+ applyEnumGroupedControlVectors (extraFlow , i , len (tail .values ))
478+ fb .flows = append (fb .flows , extraFlow )
479+ }
480+ } else {
481+ fb .flows = append (fb .flows , newHorizontalFlow (tail .id , merge .ID ))
482+ }
483+ }
484+ fb .nextConnectionPoint = merge .ID
485+ }
486+ return splitID
487+ }
488+
489+ func enumSplitCaseValues (c ast.EnumSplitCase ) []string {
490+ if len (c .Values ) > 0 {
491+ return append ([]string (nil ), c .Values ... )
492+ }
493+ if c .Value != "" {
494+ return []string {c .Value }
495+ }
496+ return nil
497+ }
498+
499+ func enumSplitFlowCaseValue (values []string , index int ) string {
500+ if index >= 0 && index < len (values ) {
501+ return values [index ]
502+ }
503+ return ""
504+ }
505+
506+ func enumSplitUsesHorizontalOrigin (values []string ) bool {
507+ return len (values ) > 0 && values [0 ] != "" && values [0 ] != "(empty)"
508+ }
509+
510+ func enumSplitGroupedDestinationAnchor (baseAnchor , index int ) int {
511+ if index == 0 {
512+ return baseAnchor
513+ }
514+ offset := 0
515+ for _ , candidate := range []int {AnchorTop , AnchorBottom , AnchorRight , AnchorLeft } {
516+ if candidate == baseAnchor {
517+ continue
518+ }
519+ if offset == index - 1 {
520+ return candidate
521+ }
522+ offset ++
523+ }
524+ return baseAnchor
525+ }
526+
527+ func applyEnumGroupedControlVectors (flow * microflows.SequenceFlow , index , total int ) {
528+ if flow == nil || total == 0 {
529+ return
530+ }
531+ switch {
532+ case flow .OriginConnectionIndex == AnchorRight && flow .DestinationConnectionIndex == AnchorLeft :
533+ flow .OriginControlVector = "15;0"
534+ if total > 1 {
535+ flow .DestinationControlVector = "-15;0"
536+ } else {
537+ flow .DestinationControlVector = "-30;0"
538+ }
539+ case flow .OriginConnectionIndex == AnchorRight && flow .DestinationConnectionIndex == AnchorTop :
540+ flow .OriginControlVector = "0;-95"
541+ flow .DestinationControlVector = "15;-45"
542+ case flow .OriginConnectionIndex == AnchorRight && flow .DestinationConnectionIndex == AnchorBottom :
543+ flow .OriginControlVector = "0;95"
544+ flow .DestinationControlVector = "15;45"
545+ case flow .OriginConnectionIndex == AnchorBottom && flow .DestinationConnectionIndex == AnchorTop :
546+ flow .OriginControlVector = "0;15"
547+ flow .DestinationControlVector = "0;-30"
548+ case index > 0 :
549+ flow .OriginControlVector = "15;0"
550+ flow .DestinationControlVector = "-15;0"
551+ }
552+ }
553+
262554func appendInheritanceBodies (s * ast.InheritanceSplitStmt ) []ast.MicroflowStatement {
263555 var stmts []ast.MicroflowStatement
264556 for _ , c := range s .Cases {
@@ -268,6 +560,15 @@ func appendInheritanceBodies(s *ast.InheritanceSplitStmt) []ast.MicroflowStateme
268560 return stmts
269561}
270562
563+ func appendEnumBodies (s * ast.EnumSplitStmt ) []ast.MicroflowStatement {
564+ var stmts []ast.MicroflowStatement
565+ for _ , c := range s .Cases {
566+ stmts = append (stmts , c .Body ... )
567+ }
568+ stmts = append (stmts , s .ElseBody ... )
569+ return stmts
570+ }
571+
271572func qualifiedNameString (qn ast.QualifiedName ) string {
272573 if qn .Module == "" {
273574 return qn .Name
0 commit comments