diff --git a/DiagramBuilder/Class/Diagram/ClassBuilder.cs b/DiagramBuilder/Class/Diagram/ClassBuilder.cs index 6aab0c5a2105ccba08f44f56a8d50749981ae191..3866e28a0199ed898a71d97fe9bce5d02d395b61 100644 --- a/DiagramBuilder/Class/Diagram/ClassBuilder.cs +++ b/DiagramBuilder/Class/Diagram/ClassBuilder.cs @@ -21,9 +21,9 @@ public class ClassBuilder : IClassBuilder return this; } - public IOperationBuilder AddOperation(string name, PropertyType returnType, VisibilityType visibilityType, string returnTypeValue) + public IOperationBuilder AddOperation(string name, PropertyType returnType, VisibilityType visibilityType) { - return new OperationBuilder(_xmiClassDiagramUmlBuilder, _class, name, returnType, visibilityType, returnTypeValue); + return new OperationBuilder(_xmiClassDiagramUmlBuilder, _class, name, returnType, visibilityType); } public IClassBuilder AddAssociation(string targetClassName, VisibilityType visibilityType, AssociationType associationType) diff --git a/DiagramBuilder/Class/Diagram/OperationBuilder.cs b/DiagramBuilder/Class/Diagram/OperationBuilder.cs index 8b4716daab927dbfc0003fdcf8294d72d3df5a58..9dc0866ca8a95297bd1d30d4ccb4146137bc6f06 100644 --- a/DiagramBuilder/Class/Diagram/OperationBuilder.cs +++ b/DiagramBuilder/Class/Diagram/OperationBuilder.cs @@ -11,11 +11,11 @@ public class OperationBuilder : IOperationBuilder private XmiElement _class { get; set; } private XmiElement _operation { get; set; } - internal OperationBuilder(XmiClassDiagramUmlBuilder xmiClassDiagramUmlBuilder, XmiElement class_, string name, PropertyType returnType, VisibilityType visibilityType, string returnTypeValue) + internal OperationBuilder(XmiClassDiagramUmlBuilder xmiClassDiagramUmlBuilder, XmiElement class_, string name, PropertyType returnType, VisibilityType visibilityType) { _xmiClassDiagramUmlBuilder = xmiClassDiagramUmlBuilder; _class = class_; - _operation = _xmiClassDiagramUmlBuilder.AddOperation(_class, name, returnType, visibilityType, returnTypeValue); + _operation = _xmiClassDiagramUmlBuilder.AddOperation(_class, name, returnType, visibilityType); } public IClassBuilder AddProperty(string name, VisibilityType visibilityType, PropertyType propertyType) @@ -24,9 +24,9 @@ public class OperationBuilder : IOperationBuilder return this; } - public IOperationBuilder AddOperation(string name, PropertyType returnType, VisibilityType visibilityType, string returnTypeValue) + public IOperationBuilder AddOperation(string name, PropertyType returnType, VisibilityType visibilityType) { - _operation = _xmiClassDiagramUmlBuilder.AddOperation(_class, name, returnType, visibilityType, returnTypeValue); + _operation = _xmiClassDiagramUmlBuilder.AddOperation(_class, name, returnType, visibilityType); return this; } diff --git a/DiagramBuilder/Class/Interface/IClassBuilder.cs b/DiagramBuilder/Class/Interface/IClassBuilder.cs index ac1de87ba60656ef4aefc0c620bec4ba01af7a75..3700fe1a60413d7da8963f615769c7c7e35eb81d 100644 --- a/DiagramBuilder/Class/Interface/IClassBuilder.cs +++ b/DiagramBuilder/Class/Interface/IClassBuilder.cs @@ -5,7 +5,7 @@ namespace DiagramBuilder.Class.Interface; public interface IClassBuilder { IClassBuilder AddProperty(string name, VisibilityType visibilityType, PropertyType propertyType); - IOperationBuilder AddOperation(string name, PropertyType returnType, VisibilityType visibilityType, string returnTypeValue); + IOperationBuilder AddOperation(string name, PropertyType returnType, VisibilityType visibilityType); IClassBuilder AddAssociation(string targetClass, VisibilityType visibilityType, AssociationType associationType); IClassBuilder AddGeneralization(string targetClass); } \ No newline at end of file diff --git a/DiagramBuilder/Class/XML/XmiClassDiagramUmlBuilder.cs b/DiagramBuilder/Class/XML/XmiClassDiagramUmlBuilder.cs index 6ce7f64ecfaaaafa44be550b0a29fe89ac74c4db..c8e1f0fd88fac21a87ddd1952a4d475b72f3dc6f 100644 --- a/DiagramBuilder/Class/XML/XmiClassDiagramUmlBuilder.cs +++ b/DiagramBuilder/Class/XML/XmiClassDiagramUmlBuilder.cs @@ -63,7 +63,7 @@ public class XmiClassDiagramUmlBuilder : DiagramUmlBuilder AddLowerValueAttribute(property, PropertyType.LiteralInteger, null); } - public XmiElement AddOperation(XmiElement parent, string name, PropertyType returnType, VisibilityType visibilityType, string returnTypeValue) + public XmiElement AddOperation(XmiElement parent, string name, PropertyType returnType, VisibilityType visibilityType) { //add visibility attribute var visibilityAttribute = new XmiAttributeDTO() @@ -235,30 +235,37 @@ public class XmiClassDiagramUmlBuilder : DiagramUmlBuilder { foreach (var association in _associations) { - var targetElement = _elementClasses[association.Target.ClassName]; - //add association="package1-Association1" - var attributes = new List<XmiAttributeDTO>() + if (_elementClasses.TryGetValue(association.Target.ClassName, out var targetElement)) { - new XmiAttributeDTO() + //add association="package1-Association1" + var attributes = new List<XmiAttributeDTO>() { - AttributeName = "association", - AttributeValue = $"{association.Package}-{association.Name}" - }, - new XmiAttributeDTO() - { - AttributeName = "type", - AttributeValue = $"{association.Package}-{association.Source.ClassName}" - } - }; - AddOwnedAttribute(targetElement, XmiType.UmlProperty, association.Target.AssociationProperyName, attributes); - var package = _packages[association.Package]; + new XmiAttributeDTO() + { + AttributeName = "association", + AttributeValue = $"{association.Package}-{association.Name}" + }, + new XmiAttributeDTO() + { + AttributeName = "type", + AttributeValue = $"{association.Package}-{association.Source.ClassName}" + } + }; + AddOwnedAttribute(targetElement, XmiType.UmlProperty, association.Target.AssociationProperyName, attributes); + var package = _packages[association.Package]; - var memberEndAttribute = new XmiAttributeDTO() + var memberEndAttribute = new XmiAttributeDTO() + { + AttributeName = "memberEnd", + AttributeValue = $"{association.Package}-{association.Target.ClassName}-{association.Target.AssociationProperyName} {association.Package}-{association.Source.ClassName}-{association.Source.AssociationProperyName}" + }; + AddPackagedElement(package, XmiType.UmlAssociation, association.Name, new() { memberEndAttribute }); + + } + else { - AttributeName = "memberEnd", - AttributeValue = $"{association.Package}-{association.Target.ClassName}-{association.Target.AssociationProperyName} {association.Package}-{association.Source.ClassName}-{association.Source.AssociationProperyName}" - }; - AddPackagedElement(package, XmiType.UmlAssociation, association.Name, new() { memberEndAttribute }); + throw new ArgumentException($"Target class {association.Target.ClassName} not found"); + } } } } \ No newline at end of file diff --git a/LanguageRecogniser/Cpp/CppClassDiagramVisitor.cs b/LanguageRecogniser/Cpp/CppClassDiagramVisitor.cs index cd9dede0404b540bd32d24d9b8b64c437a3fe041..590f25b4d6717447109ebb4263b5aca9e0e97ad0 100644 --- a/LanguageRecogniser/Cpp/CppClassDiagramVisitor.cs +++ b/LanguageRecogniser/Cpp/CppClassDiagramVisitor.cs @@ -89,7 +89,7 @@ public class CppClassDiagramVisitor : CPP14ParserBaseVisitor<object> if(member is Function function) { - var operationBuilder = _classes.Last().Value.AddOperation(function.Name, Convertor.StringToPropertyType(function.ReturnType), actualAccessSpecifier, function.ReturnType); + var operationBuilder = _classes.Last().Value.AddOperation(function.Name, Convertor.StringToPropertyType(function.ReturnType), actualAccessSpecifier); foreach(var param in function.Parameters) { operationBuilder.AddParameter(param.Name, Convertor.StringToPropertyType(param.Type)); diff --git a/UnitTest/ClassDiagramTests.cs b/UnitTest/ClassDiagramTests.cs index 359d98c85ab05425aa87a6996043f8f5e04c9610..0e287fab2ba141dfb96ce53b05a4260c4b7c1125 100644 --- a/UnitTest/ClassDiagramTests.cs +++ b/UnitTest/ClassDiagramTests.cs @@ -138,6 +138,143 @@ public class ClassDiagramTests } + [Test] + [TestCase(1)] + [TestCase(2)] + [TestCase(5)] + public void BuildDiagramWithProperties(int numberOfProperties) + { + if (numberOfProperties >= 1) + { + var package = ClassDiagramBuilder.AddPackage("TestPackage"); + var testClass = package.AddClass("TestClass", false); + + for (int i = 1; i <= numberOfProperties; i++) + { + testClass.AddProperty($"Property{i}", VisibilityType.Public, PropertyType.PrimitiveTypeString); + } + + var xmi = ClassDiagramBuilder.BuildDiagram(); + AssertCommonAssertions(xmi); + } + } + + [Test] + public void BuildDiagramWithMultiplePackagesAndClasses() + { + var package1 = ClassDiagramBuilder.AddPackage("Package1"); + package1.AddClass("Class1", false) + .AddAssociation("Class2", VisibilityType.Public, AssociationType.Association); + + var package2 = ClassDiagramBuilder.AddPackage("Package2"); + package2.AddClass("Class2", false) + .AddAssociation("Class1", VisibilityType.Public, AssociationType.Aggregation); + + var xmi = ClassDiagramBuilder.BuildDiagram(); + AssertCommonAssertions(xmi); + } + + [Test] + [TestCase("Class1", VisibilityType.Public, AssociationType.Association, true)] + [TestCase("Class1", VisibilityType.Private, AssociationType.Association, true)] + [TestCase("Class1", VisibilityType.Protected, AssociationType.Association, true)] + [TestCase("Class1", VisibilityType.Public, AssociationType.Aggregation, true)] + [TestCase("Class1", VisibilityType.Private, AssociationType.Aggregation, true)] + [TestCase("Class1", VisibilityType.Protected, AssociationType.Aggregation, true)] + [TestCase("Class1", VisibilityType.Public, AssociationType.Composition, true)] + [TestCase("Class1", VisibilityType.Private, AssociationType.Composition, true)] + [TestCase("Class1", VisibilityType.Protected, AssociationType.Composition, true)] + [TestCase("Class2", VisibilityType.Public, AssociationType.Association, true)] + [TestCase("Class2", VisibilityType.Private, AssociationType.Association, true)] + [TestCase("Class2", VisibilityType.Protected, AssociationType.Association, true)] + [TestCase("Class2", VisibilityType.Public, AssociationType.Aggregation, true)] + [TestCase("Class2", VisibilityType.Private, AssociationType.Aggregation, true)] + [TestCase("Class2", VisibilityType.Protected, AssociationType.Aggregation, true)] + [TestCase("Class2", VisibilityType.Public, AssociationType.Composition, true)] + [TestCase("Class2", VisibilityType.Private, AssociationType.Composition, true)] + [TestCase("Class2", VisibilityType.Protected, AssociationType.Composition, true)] + [TestCase("NonExistingClassName", VisibilityType.Public, AssociationType.Association, false)] + [TestCase("NonExistingClassName", VisibilityType.Private, AssociationType.Association, false)] + [TestCase("NonExistingClassName", VisibilityType.Protected, AssociationType.Association, false)] + public void BuildDiagramWithMixedAssociationsAndGeneralization(string targetAssociation, AssociationType type, VisibilityType visibilityType, bool isValid) + { + var package = ClassDiagramBuilder.AddPackage("TestPackage"); + var class1 = package.AddClass("Class1", false); + var class2 = package.AddClass("Class2", false); + + class2.AddAssociation(targetAssociation, visibilityType, type); + + if (!isValid) + { + Assert.Throws<ArgumentException>(() => ClassDiagramBuilder.BuildDiagram()); + } + else + { + var xmi = ClassDiagramBuilder.BuildDiagram(); + AssertCommonAssertions(xmi); + } + } + + + [Test] + public void BuildDiagramWithDuplicateClassNames() + { + var package = ClassDiagramBuilder.AddPackage("TestPackage"); + + // Add classes with the same name (should handle gracefully) + var class1 = package.AddClass("Class1", false); + Assert.Throws<ArgumentException>(() => package.AddClass("Class1", false)); + + + var xmi = ClassDiagramBuilder.BuildDiagram(); + AssertCommonAssertions(xmi); + } + + [Test] + [TestCase(1)] + [TestCase(2)] + [TestCase(10)] + public void BuildDiagramWithOperations(int numberOfOperations) + { + if (numberOfOperations >= 1) + { + var package = ClassDiagramBuilder.AddPackage("TestPackage"); + var testClass = package.AddClass("TestClass", false); + + for (int i = 1; i <= numberOfOperations; i++) + { + testClass.AddOperation($"Operation{i}", PropertyType.PrimitiveTypeBoolean, VisibilityType.Public); + } + + var xmi = ClassDiagramBuilder.BuildDiagram(); + AssertCommonAssertions(xmi); + } + } + + [Test] + public void BuildEmptyDiagramWithNoClasses() + { + var xmi = ClassDiagramBuilder.BuildDiagram(); + Assert.IsFalse(string.IsNullOrEmpty(xmi), "XMI should not be null or empty"); + AssertCommonAssertions(xmi); + } + + [Test] + public void BuildDiagramWithGeneralization() + { + var package = ClassDiagramBuilder.AddPackage("TestPackage"); + var class1 = package.AddClass("Class1", false); + var class2 = package.AddClass("Class2", false); + + class2.AddGeneralization("Class1"); + + var xmi = ClassDiagramBuilder.BuildDiagram(); + AssertCommonAssertions(xmi); + + // Add specific assertions for generalization + Assert.IsTrue(xmi.Contains("general=\"TestPackage-Class1\""), "XMI should contain Generalization"); + } + void ValidateSchema() @@ -179,4 +316,18 @@ public class ClassDiagramTests _errors.Add((e.Severity.ToString(), e.Message)); } } + + private void AssertCommonAssertions(string xmi) + { + Assert.IsFalse(string.IsNullOrEmpty(xmi), "XMI should not be null or empty"); + Assert.IsTrue(xmi.Contains("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"), "XMI should contain the XML header"); + // Add more assertions based on the expected structure of your XMI + + if (!string.IsNullOrEmpty(generatedFileName)) + { + File.WriteAllText(generatedFileName, xmi); + ValidateSchema(); + RemoveGeneratedFile(); + } + } } \ No newline at end of file