From 0ca83ff35dce3746543783fe5a707b39ca9ea147 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Konvi=C4=8Dka?= <konvicka.g630@gmail.com>
Date: Thu, 14 Dec 2023 15:25:52 +0100
Subject: [PATCH] feat(#17): Add loop diagram option

---
 ConsoleApp1/Program.cs                        | 23 ++++-----
 .../Diagram/ActivityDiagramBuilder.cs         | 36 ++++++++++---
 .../Activity/Diagram/DecisionBuilder.cs       | 50 ++++++++++++++++++-
 .../Activity/Interface/IDecisionBuilder.cs    |  3 ++
 .../Activity/Interface/IDiagramBuilder.cs     |  4 ++
 .../XML/XmiActivityDiagramUmlBuilder.cs       |  5 ++
 6 files changed, 99 insertions(+), 22 deletions(-)

diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs
index 348b888..bad7b3b 100644
--- a/ConsoleApp1/Program.cs
+++ b/ConsoleApp1/Program.cs
@@ -67,20 +67,15 @@ Console.WriteLine("Parsing Complete!");
 ActivityDiagramBuilderBuilderCreator activityDiagramBuilderBuilderCreator = new ActivityDiagramBuilderBuilderCreator();
 DiagramBuilder.Activity.Interface.IDiagramBuilder diagramBuilder = activityDiagramBuilderBuilderCreator.FactoryMethod("ActivityDiagram");
 
-diagramBuilder.AddAction("jedna").AddDecision("decision", "true", "false")
-    .AddAction("false1", BranchType.False)
-    .AddAction("true1", BranchType.True)
-    .AddDecision("decision2", "true", "false", BranchType.False)
-    .AddAction("true2", BranchType.True)
-    .AddAction("false2", BranchType.False)
-    .AddDecision("decision3", "true", "false", BranchType.True)
-    .AddAction("true3", BranchType.True)
-    .AddAction("false3", BranchType.False)
-    .AddAction("false4", BranchType.False)
-    .AddMerge("merge3")
-    .AddMerge("merge2")
-    .AddAction("false5", BranchType.False)
-    .AddMerge("merge");
+diagramBuilder
+    .AddLoop("test", "cond")
+    .AddDecision("D1", "t","f")
+    .AddLoop("test2", "cond2", BranchType.True)
+    .AddAction("a", BranchType.True)
+    .CloseLoop("loopCLose", BranchType.True)
+    .AddMerge("merge1")
+    .CloseLoop("a")
+    .AddAction("c");
 var xmi = diagramBuilder.BuildDiagram();
 Console.WriteLine(xmi);
 File.WriteAllText("/Users/jakubkonvicka/Applications/EA.app/Contents/SharedSupport/prefix/drive_c/xmi/activity.xml", xmi);
diff --git a/DiagramBuilder/Activity/Diagram/ActivityDiagramBuilder.cs b/DiagramBuilder/Activity/Diagram/ActivityDiagramBuilder.cs
index 7258321..f69d305 100644
--- a/DiagramBuilder/Activity/Diagram/ActivityDiagramBuilder.cs
+++ b/DiagramBuilder/Activity/Diagram/ActivityDiagramBuilder.cs
@@ -8,10 +8,12 @@ public class ActivityDiagramBuilder : IDiagramBuilder
 {
     public XmiActivityDiagramUmlBuilder _xmiActivityDiagramUmlBuilder { get; set; }
     private string _name { get; set; }
-    private XmiElement _lastActivity { get; set; } = null;
+    public XmiElement LastActivity { get; set; } = null;
     private XmiElement _lastEdge => _xmiActivityDiagramUmlBuilder.LatestEdge;
     public bool IsDecisionClosed { get; set; } = true;
+    private Stack<XmiElement> _loopElements { get; set; } = new Stack<XmiElement>();
     
+    private XmiElement _lastClosedDecision { get; set; } = null;
     internal ActivityDiagramBuilder()
     {
         
@@ -21,26 +23,46 @@ public class ActivityDiagramBuilder : IDiagramBuilder
         this._name = name;
         XmiBuilder xmiBuilder = new XmiBuilder();
         _xmiActivityDiagramUmlBuilder = new XmiActivityDiagramUmlBuilder(name, xmiBuilder);
-        _lastActivity = _xmiActivityDiagramUmlBuilder.InitialNode;
+        LastActivity = _xmiActivityDiagramUmlBuilder.InitialNode;
     }
 
     public IDiagramBuilder AddAction(string name)
     {
         if(_lastEdge == null)
-            _lastActivity = _xmiActivityDiagramUmlBuilder.AddAction(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, null, null);
+            LastActivity = _xmiActivityDiagramUmlBuilder.AddAction(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, null, null, _lastClosedDecision);
         else
-            _lastActivity = _xmiActivityDiagramUmlBuilder.AddAction(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, new() {_lastEdge}, null);
+            LastActivity = _xmiActivityDiagramUmlBuilder.AddAction(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, new() {_lastEdge}, null, _lastClosedDecision);
+        _lastClosedDecision = null;
+        return this;
+    }
+
+    public IDiagramBuilder AddLoop(string name, string condition)
+    {
+        if(_lastEdge == null)
+            LastActivity = _xmiActivityDiagramUmlBuilder.AddDecision(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, null, null, _lastClosedDecision);
+        else
+            LastActivity = _xmiActivityDiagramUmlBuilder.AddDecision(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, new() {_lastEdge}, null, _lastClosedDecision);
+        _loopElements.Push(LastActivity);
+        return this;
+    }
+
+    public IDiagramBuilder CloseLoop(string name)
+    {
+        var lastLoopElement = _loopElements.Pop();
+        _xmiActivityDiagramUmlBuilder.AddDirectedEdge(_xmiActivityDiagramUmlBuilder.ActivityDiagram, LastActivity,
+            lastLoopElement);
+        _lastClosedDecision = lastLoopElement;
         return this;
     }
 
     public IDecisionBuilder AddDecision(string name, string trueCondition, string falseCondition)
     {
         if(_lastEdge == null)
-            _lastActivity = _xmiActivityDiagramUmlBuilder.AddDecision(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, null, null);
+            LastActivity = _xmiActivityDiagramUmlBuilder.AddDecision(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, null, null, _lastClosedDecision);
         else
-            _lastActivity = _xmiActivityDiagramUmlBuilder.AddDecision(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, new() {_lastEdge}, null);
+            LastActivity = _xmiActivityDiagramUmlBuilder.AddDecision(_xmiActivityDiagramUmlBuilder.ActivityDiagram, name, new() {_lastEdge}, null, _lastClosedDecision);
         
-        return new DecisionBuilder(this._name, this._xmiActivityDiagramUmlBuilder, _lastEdge, _lastActivity);
+        return new DecisionBuilder(this._name, this._xmiActivityDiagramUmlBuilder, _lastEdge, LastActivity, this);
     }
 
     public string? BuildDiagram()
diff --git a/DiagramBuilder/Activity/Diagram/DecisionBuilder.cs b/DiagramBuilder/Activity/Diagram/DecisionBuilder.cs
index cbb75f4..eefd567 100644
--- a/DiagramBuilder/Activity/Diagram/DecisionBuilder.cs
+++ b/DiagramBuilder/Activity/Diagram/DecisionBuilder.cs
@@ -12,14 +12,17 @@ namespace DiagramBuilder.Activity.Diagram
 
         // Dictionary for true and false branch
         private List<Dictionary<BranchType, XmiElement>> _lastAddedEdge { get; set; } = new();
+        private List<Dictionary<BranchType, XmiElement>> _loopElements { get; set; } = new();
         private List<Dictionary<BranchType, XmiElement>> _lastAddedNode { get; set; } = new();
         
+        IDiagramBuilder _diagramBuilder { get; set; }
         private List<BranchType> _lastBranchTypeForDecision { get; set; } = new();
         
         private long _actualDecisionId { get; set; } = 0;
 
-        public DecisionBuilder(string name, XmiActivityDiagramUmlBuilder xmiActivityDiagramUmlBuilder, XmiElement trueBranchEdge, XmiElement lastAddedNode)
+        public DecisionBuilder(string name, XmiActivityDiagramUmlBuilder xmiActivityDiagramUmlBuilder, XmiElement trueBranchEdge, XmiElement lastAddedNode, IDiagramBuilder _diagramBuilder)
         {
+            this._diagramBuilder = _diagramBuilder;
             _xmiActivityDiagramUmlBuilder = xmiActivityDiagramUmlBuilder;
             _lastAddedEdge.Add(new Dictionary<BranchType, XmiElement>());
             _lastAddedNode.Add(new Dictionary<BranchType, XmiElement>());
@@ -53,6 +56,12 @@ namespace DiagramBuilder.Activity.Diagram
             return this;
         }
 
+        public IDiagramBuilder AddAction(string name)
+        {
+            this._diagramBuilder.AddAction(name);
+            return this._diagramBuilder;
+        }
+
         public IDecisionBuilder AddMerge(string name)
         {
             var mergeNode = _xmiActivityDiagramUmlBuilder.AddMerge(
@@ -60,11 +69,15 @@ namespace DiagramBuilder.Activity.Diagram
                 _lastAddedNode.Last()[BranchType.True], _lastAddedNode.Last()[BranchType.False]);
             _lastAddedEdge.Remove(_lastAddedEdge.Last());
             _lastAddedNode.Remove(_lastAddedNode.Last());
+            
+            this._diagramBuilder.LastActivity = mergeNode;
+            
             if (_lastAddedNode.Count == 0)
                 return this;
             
             _lastAddedNode.Last()[_lastBranchTypeForDecision.Last()] = mergeNode;
             _lastBranchTypeForDecision.RemoveAt(_lastBranchTypeForDecision.Count-1);
+            
             return this;
         }
 
@@ -93,5 +106,40 @@ namespace DiagramBuilder.Activity.Diagram
             //_lastAddedEdge.Last().Add(BranchType.False, _xmiActivityDiagramUmlBuilder.LatestEdge);
             return this;
         }
+
+        public IDecisionBuilder AddLoop(string name, string condition, BranchType type)
+        { 
+            
+            if (!_lastAddedEdge.Last().TryGetValue(type, out var edge))
+            {
+                _lastAddedNode.Last()[type] = _xmiActivityDiagramUmlBuilder.AddDecision(
+                    _xmiActivityDiagramUmlBuilder.ActivityDiagram, name, null, null, _lastAddedNode.Last()[type]);
+                _lastAddedEdge.Last().Add(type, _xmiActivityDiagramUmlBuilder.LatestEdge);
+            }
+            else
+            {
+                _lastAddedNode.Last()[type] = _xmiActivityDiagramUmlBuilder.AddDecision(
+                    _xmiActivityDiagramUmlBuilder.ActivityDiagram, name, new List<XmiElement> { _lastAddedEdge.Last()[type] }, null, _lastAddedNode.Last()[type]);
+            }
+            
+            
+            _loopElements.Add(new Dictionary<BranchType, XmiElement>());
+            _loopElements.Last().Add(type, _lastAddedNode.Last()[type]);
+            return this;
+        }
+
+        public IDecisionBuilder CloseLoop(string name, BranchType type)
+        {
+            _xmiActivityDiagramUmlBuilder.AddDirectedEdge(_xmiActivityDiagramUmlBuilder.ActivityDiagram, _lastAddedNode.Last()[type],
+                _loopElements.Last()[type]);
+            _loopElements.Remove(_loopElements.Last());
+            return this;
+        }
+
+        public IDiagramBuilder CloseLoop(string name)
+        {
+            this._diagramBuilder.CloseLoop(name);
+            return this._diagramBuilder;
+        }
     }
 }
diff --git a/DiagramBuilder/Activity/Interface/IDecisionBuilder.cs b/DiagramBuilder/Activity/Interface/IDecisionBuilder.cs
index 6277107..9d85319 100644
--- a/DiagramBuilder/Activity/Interface/IDecisionBuilder.cs
+++ b/DiagramBuilder/Activity/Interface/IDecisionBuilder.cs
@@ -7,4 +7,7 @@ public interface IDecisionBuilder
     public IDecisionBuilder AddAction(string name, BranchType type);
     public IDecisionBuilder AddMerge(string name);
     public IDecisionBuilder AddDecision(string name, string trueCondition, string falseCondition, BranchType type);
+    public IDecisionBuilder AddLoop(string name, string condition, BranchType type);
+    public IDecisionBuilder CloseLoop(string name, BranchType type);
+    public IDiagramBuilder CloseLoop(string name);
 }
\ No newline at end of file
diff --git a/DiagramBuilder/Activity/Interface/IDiagramBuilder.cs b/DiagramBuilder/Activity/Interface/IDiagramBuilder.cs
index 5d9c400..8013b52 100644
--- a/DiagramBuilder/Activity/Interface/IDiagramBuilder.cs
+++ b/DiagramBuilder/Activity/Interface/IDiagramBuilder.cs
@@ -1,11 +1,15 @@
 using DiagramBuilder.Class.Interface;
+using DiagramBuilder.XML;
 
 namespace DiagramBuilder.Activity.Interface;
 
 public interface IDiagramBuilder
 {
     public bool IsDecisionClosed { get; set; }
+    public XmiElement LastActivity { get; set; }
     public IDiagramBuilder AddAction(string name);
+    public IDiagramBuilder AddLoop(string name, string condition);
+    public IDiagramBuilder CloseLoop(string name);
     public IDecisionBuilder AddDecision(string name, string trueCondition, string falseCondition);
     public string? BuildDiagram();
 }
\ No newline at end of file
diff --git a/DiagramBuilder/Activity/XML/XmiActivityDiagramUmlBuilder.cs b/DiagramBuilder/Activity/XML/XmiActivityDiagramUmlBuilder.cs
index fb510d9..1b96c96 100644
--- a/DiagramBuilder/Activity/XML/XmiActivityDiagramUmlBuilder.cs
+++ b/DiagramBuilder/Activity/XML/XmiActivityDiagramUmlBuilder.cs
@@ -112,6 +112,11 @@ public class XmiActivityDiagramUmlBuilder : DiagramUmlBuilder
         return activity;
     }
     
+    public void AddDirectedEdge(XmiElement parent, XmiElement source, XmiElement target)
+    {
+        AddEdge(parent, XmiType.UmlControlFlow, source, target);
+    }
+    
     private XmiElement AddActivityFinalNode(XmiElement parent, string name)
     {
         string id = name;
-- 
GitLab