diff --git a/ERMine.Core.UnitTesting/Parsing/ModelParserTest.cs b/ERMine.Core.UnitTesting/Parsing/ModelParserTest.cs index aea649e..8c84d41 100644 --- a/ERMine.Core.UnitTesting/Parsing/ModelParserTest.cs +++ b/ERMine.Core.UnitTesting/Parsing/ModelParserTest.cs @@ -82,6 +82,94 @@ public void Parse_TwoEntitiesAndRelationship_Label() var entityRelationships = ModelParser.EntityRelationships.Parse(input); Assert.AreEqual(3, entityRelationships.Count()); + Assert.AreEqual(2, entityRelationships.Count(e => e is Entity)); + Assert.AreEqual(1, entityRelationships.Count(r => r is Relationship)); + + } + + [TestMethod] + public void Parse_TwoRelationships_Label() + { + var input = "[Student] *-isfriend-* [Student]" + "\r\n"; + input += "[Student] *-follow-* [Course]\r\n"; + + var entityRelationships = ModelParser.Relationships.Parse(input); + + Assert.AreEqual(2, entityRelationships.Count()); + + } + + [TestMethod] + public void Parse_OneTernary_Label() + { + var input = "-SupplySchedule- [Vendor]* [Part]+ [Warehouse]+\r\n"; + + var entityRelationships = ModelParser.Relationships.Parse(input); + + Assert.AreEqual(1, entityRelationships.Count()); + + } + + [TestMethod] + public void Parse_ThreeRelationships_Label() + { + var input = "[Student] *-isfriend-* [Student]" + "\r\n"; + input += "[Student] *-follow-* [Course]\r\n"; + input += "-SupplySchedule- [Vendor]* [Part]+ [Warehouse]+\r\n"; + + var entityRelationships = ModelParser.Relationships.Parse(input); + + Assert.AreEqual(3, entityRelationships.Count()); + Assert.AreEqual(3, entityRelationships.Count(r => r is Relationship)); + + } + + [TestMethod] + public void Parse_ThreeEntitiesAndTernaryRelationship_Label() + { + var input = "[Warehouse]" + "\r\n"; + input += "-SupplySchedule- [Vendor]* [Part]+ [Warehouse]+" + "\r\n"; + input += "[Part]" + "\r\n"; + input += "[Vendor]" + "\r\n"; + + var entityRelationships = ModelParser.EntityRelationships.Parse(input); + + Assert.AreEqual(3, entityRelationships.Count(e => e is Entity)); + Assert.AreEqual(1, entityRelationships.Count(r => r is Relationship)); + + } + + [TestMethod] + public void Parse_TwiceThreeRelationship_Label() + { + var input = "[Warehouse]" + "\r\n"; + input += "-SupplySchedule- [Vendor]* [Part]+ [Warehouse]+" + "\r\n"; + input += "[Part]" + "\r\n"; + input += "[Vendor]" + "\r\n"; + input += "[Student] *-isfriend-* [Student]" + "\r\n"; + input += "[Student] *-follow-* [Course]\r\n"; + + var entityRelationships = ModelParser.EntityRelationships.Parse(input); + + Assert.AreEqual(3, entityRelationships.Count(e => e is Entity)); + Assert.AreEqual(3, entityRelationships.Count(r => r is Relationship)); + + } + + [TestMethod] + public void Parse_TwiceThree2Relationship_Label() + { + var input = "[Warehouse]" + "\r\n"; + input += "-SupplySchedule- [Vendor]* [Part]+ [Warehouse]+" + "\r\n"; + input += "[Student] *-isfriend-* [Student]" + "\r\n"; + input += "[Student] *-follow-* [Course]\r\n"; + input += "[Part]" + "\r\n"; + input += "[Vendor]" + "\r\n"; + + var entityRelationships = ModelParser.EntityRelationships.Parse(input); + + Assert.AreEqual(3, entityRelationships.Count(e => e is Entity)); + Assert.AreEqual(3, entityRelationships.Count(r => r is Relationship)); } } diff --git a/ERMine.Core.UnitTesting/Parsing/RelationshipParserTest.cs b/ERMine.Core.UnitTesting/Parsing/RelationshipParserTest.cs index b2463fe..7d1ed6b 100644 --- a/ERMine.Core.UnitTesting/Parsing/RelationshipParserTest.cs +++ b/ERMine.Core.UnitTesting/Parsing/RelationshipParserTest.cs @@ -14,7 +14,7 @@ public class RelationshipParserTest public void Relationship_Binary_WithLabel() { var input = "[Student] +-follow-* [Course]"; - var relationship = RelationshipParser.Relationship.Parse(input); + var relationship = RelationshipBinaryParser.Relationship.Parse(input); Assert.AreEqual("follow", relationship.Label); Assert.AreEqual("Student", relationship.Entities.ElementAt(0).Label); @@ -27,7 +27,7 @@ public void Relationship_Binary_WithLabel() public void Relationship_Binary_WithoutLabel() { var input = "[Post]*--1[Comment]"; - var relationship = RelationshipParser.Relationship.Parse(input); + var relationship = RelationshipBinaryParser.Relationship.Parse(input); Assert.AreEqual(null, relationship.Label); Assert.AreEqual("Post", relationship.Entities.ElementAt(0).Label); @@ -40,12 +40,27 @@ public void Relationship_Binary_WithoutLabel() public void Relationship_Binary_Self() { var input = "[Employee] *-manages-1 [Employee]"; - var relationship = RelationshipParser.Relationship.Parse(input); + var relationship = RelationshipBinaryParser.Relationship.Parse(input); Assert.AreEqual("Employee", relationship.Entities.ElementAt(0).Label); Assert.AreEqual("Employee", relationship.Entities.ElementAt(1).Label); Assert.IsTrue(relationship.Entities.ElementAt(0).Label == relationship.Entities.ElementAt(1).Label); } + [TestMethod] + public void Relationship_Ternary_Simple() + { + var input = "-SupplySchedule- [Vendor]* [Part]+ [Warehouse]+ "; + var relationship = RelationshipTernaryParser.Relationship.Parse(input); + + Assert.AreEqual("Vendor", relationship.Entities.ElementAt(0).Label); + Assert.AreEqual(Cardinality.ZeroOrMore, relationship.Cardinality.ElementAt(0)); + Assert.AreEqual("Part", relationship.Entities.ElementAt(1).Label); + Assert.AreEqual(Cardinality.OneOrMore, relationship.Cardinality.ElementAt(1)); + Assert.AreEqual("Warehouse", relationship.Entities.ElementAt(2).Label); + Assert.AreEqual(Cardinality.OneOrMore, relationship.Cardinality.ElementAt(2)); + + } + } } diff --git a/ERMine.Core/ERMine.Core.csproj b/ERMine.Core/ERMine.Core.csproj index 289b312..4f06f9a 100644 --- a/ERMine.Core/ERMine.Core.csproj +++ b/ERMine.Core/ERMine.Core.csproj @@ -56,7 +56,8 @@ - + + diff --git a/ERMine.Core/Modeling/Factory/RelationshipFactory.cs b/ERMine.Core/Modeling/Factory/RelationshipFactory.cs index 968f6c9..708d78e 100644 --- a/ERMine.Core/Modeling/Factory/RelationshipFactory.cs +++ b/ERMine.Core/Modeling/Factory/RelationshipFactory.cs @@ -17,6 +17,15 @@ public Relationship Create(string label, IEnumerable> return relationship; } + public Relationship Create(string label, IEnumerable> members) + { + var relationship = new Relationship(members.Count()); + relationship.Label = label; + foreach (var item in members) + relationship.Add(new Entity(item.Item1), item.Item2); + return relationship; + } + public Relationship Create(string label, string firstEntity, Cardinality firstCardinality, string secondEntity, Cardinality secondCardinality) { var tuples = new List>(); diff --git a/ERMine.Core/Parsing/ModelParser.cs b/ERMine.Core/Parsing/ModelParser.cs index 5f109fa..d868b69 100644 --- a/ERMine.Core/Parsing/ModelParser.cs +++ b/ERMine.Core/Parsing/ModelParser.cs @@ -10,15 +10,25 @@ namespace ERMine.Core.Parsing { static class ModelParser { + public readonly static Parser> Relationships = + ( + from binaryRelationships in RelationshipBinaryParser.Relationship.Many() + from ternaryRelationships in RelationshipTernaryParser.Relationship.Many() + select binaryRelationships + .Union(ternaryRelationships) + ); + + public readonly static Parser> Entities = + ( + from entities in EntityParser.Entity.Many() + select entities + ); + readonly static Parser> EntityRelationship = ( - from relationships in RelationshipParser.Relationship.Optional() - from entities in EntityParser.Entity.Optional() - select entities.IsDefined && relationships.IsDefined - ? Enumerable.Repeat(entities.GetOrDefault() as IEntityRelationship, 1) - .Union(Enumerable.Repeat(relationships.GetOrDefault() as IEntityRelationship, 1)) - : entities.IsDefined ? Enumerable.Repeat(entities.GetOrDefault() as IEntityRelationship, 1) - : Enumerable.Repeat(relationships.GetOrDefault() as IEntityRelationship, 1) + from relationships in Relationships.Many() + from entity in EntityParser.Entity.Optional() + select entity.IsDefined ? relationships.SelectMany(r => r).Union(Enumerable.Repeat((IEntityRelationship) entity.GetOrDefault(), 1)) : relationships.SelectMany(r => r) ); public readonly static Parser> EntityRelationships = diff --git a/ERMine.Core/Parsing/RelationshipParser.cs b/ERMine.Core/Parsing/RelationshipBinaryParser.cs similarity index 97% rename from ERMine.Core/Parsing/RelationshipParser.cs rename to ERMine.Core/Parsing/RelationshipBinaryParser.cs index 22b8fad..57acd3e 100644 --- a/ERMine.Core/Parsing/RelationshipParser.cs +++ b/ERMine.Core/Parsing/RelationshipBinaryParser.cs @@ -8,7 +8,7 @@ namespace ERMine.Core.Parsing { - static class RelationshipParser + static class RelationshipBinaryParser { readonly static Parser Cardinality = ( diff --git a/ERMine.Core/Parsing/RelationshipTernaryParser.cs b/ERMine.Core/Parsing/RelationshipTernaryParser.cs new file mode 100644 index 0000000..ca91a43 --- /dev/null +++ b/ERMine.Core/Parsing/RelationshipTernaryParser.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Sprache; +using ERMine.Core.Modeling; +using ERMine.Core.Modeling.Factory; +using System; + +namespace ERMine.Core.Parsing +{ + static class RelationshipTernaryParser + { + readonly static Parser Cardinality = + ( + from cardinality in Parse.Char('*').Return(Modeling.Cardinality.ZeroOrMore) + .Or(Parse.Char('?').Return(Modeling.Cardinality.ZeroOrOne) + .Or(Parse.Char('1').Return(Modeling.Cardinality.ExactyOne) + .Or(Parse.Char('+').Return(Modeling.Cardinality.OneOrMore) + ))) + select cardinality + ); + + public readonly static Parser Relationship = + ( + + from firstSeparator in Parse.Char('-') + from label in Grammar.Textual.Optional() + from secondSeparator in Parse.Char('-') + from firstEntity in Grammar.BracketTextual + from firstCardinality in Cardinality + from secondEntity in Grammar.BracketTextual + from secondCardinality in Cardinality + from thirdEntity in Grammar.BracketTextual + from thirdCardinality in Cardinality + select new RelationshipFactory().Create( label.GetOrDefault() + , new List>() + { + new Tuple (firstEntity, firstCardinality) + , new Tuple (secondEntity, secondCardinality) + , new Tuple (thirdEntity, thirdCardinality) + } + ) + ); + + public readonly static Parser> Relationships = + ( + Relationship.Many() + ); + + + } +} diff --git a/README.md b/README.md index ff5fa74..13133d8 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,16 @@ Derivated attributes are noted with a percentage (%). # Relationships -Currently, ERMine supports unary and binary relationships +Currently, ERMine supports unary, binary and ternary relationships -*first_entity_name* [ ? | 1 | * | + ] - *ralationship_name* - [ ? | 1 | * | + ] *second_entity_name* +*first_entity_name* [ ? | 1 | * | + ] - *relationship_name* - [ ? | 1 | * | + ] *second_entity_name* for unary relationships the name of the first and second entities must be identical. +Ternary relationships are noted differently + +*relationship_name* *first_entity_name* [ ? | 1 | * | + ] *second_entity_name* [ ? | 1 | * | + ] *third_entity_name* [ ? | 1 | * | + ] + ## Cardinalities * ```?``` stands for O..1