{"id":142,"date":"2008-05-21T03:13:38","date_gmt":"2008-05-21T01:13:38","guid":{"rendered":"http:\/\/www.ticklishtechs.net\/2008\/05\/21\/connectors-between-compartment-shape-entries-with-dsl-tools-version-2\/"},"modified":"2020-08-13T20:46:43","modified_gmt":"2020-08-13T18:46:43","slug":"connectors-between-compartment-shape-entries-with-dsl-tools-version-2","status":"publish","type":"post","link":"https:\/\/www.ticklishtechs.net\/2008\/05\/21\/connectors-between-compartment-shape-entries-with-dsl-tools-version-2\/","title":{"rendered":"Connectors between compartment shape entries with DSL Tools – Version 2"},"content":{"rendered":"
Few months ago I wrote a series of articles<\/a> and released a library to create connectors between the entries of compartment shapes in Microsoft DSL Tools. In the meantime I found a setting that wasn’t working with my library and now it is time for version 2. I will release this new version as part of JaDAL – Just another DSL-Tools Addon Library<\/a> on CodePlex<\/a>.<\/p>\n If you don’t have any idea what I’m talking about please take a look at (at least) the first part<\/a> of the series, but be careful and DO NOT read the second part! The second part contains obsolete information that will be corrected here. Part 3<\/a> and part 4<\/a> describe the inside of the library and they are still valid for the new version.<\/p>\n In the original version you have to decide (using the xml file) for the target and source of the connection to be mapped to a compartment or a regular shape. I thought this wouldn’t be an issue since you must know the mapping of your classes. I believed it until I created my DSL with the use of inheritance and some base class that can be mapped to regular or compartment shapes. For this base class I couldn’t create a mapping that goes from a regular or a compartment shape to a regular or compartment shape. <\/p>\n I removed this constraint and in version 2 you needn’t care about it. The library can handle all these cases (even the trivial mapping from regular to regular shape). And even the best: it turned out that removing this constraint made the code much clearer and simpler (both the code of the library itself and the code you have to write). Before this change there were many different cases (first regular to compartment, then compartment to regular, then compartment to compartment and at the end regular to regular which throws an exception) and now everything works the same way.<\/p>\n Lesson learned: If you can do something in an abstract and general way, don’t create different cases and handle only some of them!<\/em><\/p>\n In the 2nd part<\/a> of this series I presented a user guide with a step by step walkthrough of creating a DSL using the compartment mapping. I will now create the same DSL with version 2. Most steps are still the same but I repeat them for completeness. The main difference is the xml file format at step 7 and the class you have to write yourself in steps 9 to 11.<\/p>\n You will find this example (and a more advanced one) in the release download over at CodePlex.<\/p>\n (If you place the xsd file in the same directory you get IntelliSense and error checking for free.)<\/p>\n<\/li>\n (Be sure you use the right xml file name here.)<\/p>\n<\/li>\n <\/li>\n <\/a><\/li>\n Always care for (If you have already some custom rules, you can use an overloaded version of the In the <\/a><\/a><\/p>\n<\/li>\n<\/ol>\n That’s all. The mapping from compartment entry to compartment entry should be working after compiling the solution.<\/p>\n As I mention above you can mix compartment and regular shapes. You only need to create a DSL with these shapes and configure the xml file another way. As you see, inside the If one of source or target is always a regular shape, please use At the bottom of the xml file you have to list all shapes that play a role in your mapping. Use the xml elements Inside your <\/a> <\/p>\n
Changes from version 1 to 2<\/h3>\n
And again: the user guide<\/h3>\n
\n
Parent1<\/code>,
Entry1<\/code>,
Parent2<\/code> and
Entry2<\/code>.
The entries need a unique identifier. You could use a name property for this, but there will be some difficulties if the user creates two entries in one shape with the same name, so I will use a guid. (Don’t forget to create a new guid in the constructor or another proper place.) <\/li>\nParent1<\/code> to
Parent2<\/code>. This will be automatically named
Parent1ReferencesParent2<\/code>. We will use this reference to present the relationship from one entry to another. I would like to create Reference Relationships<\/em> from this relationship to the entries, but relationships of relationships are not supported. We have to store the guids of the entries in the relationship and add two Domain Properties<\/em> for this purpose to it. I named them
fromEntry<\/code> and
toEntry<\/code>.
<\/a> <\/li>\n
Allows Duplicates<\/code> property of the
Parent1ReferencesParent2<\/code> relationship to
true<\/code>. <\/li>\n
Is Custom<\/code> property of the Connection Builder<\/em> (
Parent1ReferencesParent2Builder<\/code>) in the DSL Explorer<\/em> to
true<\/code>. <\/li>\n
JaDAL.dll<\/code> to both the
Dsl<\/code> and
DslPackage<\/code> project. <\/li>\n
CompartmentMappings.xml<\/code>) to your solution and write the following code in it:\n
<?<\/span>xml <\/span>version<\/span>=<\/span>"1.0<\/span>" encoding<\/span>=<\/span>"utf-8<\/span>" ?>\r\n<<\/span>CompartmentMappings <\/span>xmlns:xsi<\/span>=<\/span>"http:\/\/www.w3.org\/2001\/XMLSchema-instance<\/span>"\r\n xsi:noNamespaceSchemaLocation<\/span>=<\/span>"CompartmentMappings.xsd<\/span>"\r\n namespace<\/span>=<\/span>"BenjaminSchroeter.CompartmentMapping<\/span>">\r\n \r\n <<\/span>Connection <\/span>name<\/span>=<\/span>"Parent1ReferencesParent2<\/span>" \r\n allowSelfReference<\/span>=<\/span>"true<\/span>"\r\n suppressEntryDeletePropagation<\/span>=<\/span>"false<\/span>">\r\n \r\n <<\/span>Source <\/span>allowHead<\/span>=<\/span>"false<\/span>">\r\n <<\/span>DomainClass <\/span>name<\/span>=<\/span>"Parent1<\/span>" \/>\r\n <<\/span>EntryDomainClass <\/span>name<\/span>=<\/span>"Entry1<\/span>"\/>\r\n <\/<\/span>Source<\/span>>\r\n \r\n <<\/span>Target <\/span>allowHead<\/span>=<\/span>"true<\/span>">\r\n <<\/span>DomainClass <\/span>name<\/span>=<\/span>"Parent2<\/span>"\/>\r\n <<\/span>EntryDomainClass <\/span>name<\/span>=<\/span>"Entry2<\/span>"\/>\r\n <\/<\/span>Target<\/span>>\r\n \r\n <<\/span>Connector <\/span>name<\/span>=<\/span>"Connector<\/span>"\/>\r\n <\/<\/span>Connection<\/span>>\r\n\r\n <<\/span>CompartmentShape <\/span>name<\/span>=<\/span>"CompartmentShape1<\/span>"\/>\r\n <<\/span>CompartmentShape <\/span>name<\/span>=<\/span>"CompartmentShape2<\/span>"\/>\r\n<\/<\/span>CompartmentMappings<\/span>>
<\/span><\/pre>\nCompartmentMappings.tt<\/code> file in the same directory, right-click on the xml file and choose Generate xGen Template<\/em>. A
CompartmentMappings_xGen.tt<\/code> file will be created below the xml file and there you have to add the following lines:\n
<#@<\/span> ParentFileInjector processor=<\/span>"TTxGenDirectiveProcessor<\/span>" \r\n requires=<\/span>"fileName='CompartmentMappings.xml'<\/span>" #>\r\n<#@<\/span> <\/span>include <\/span>file=<\/span>"CompartmentMappings.tt<\/span>" #><\/span><\/pre>\n
CompartmentMappings_xGen.cs<\/code>) there will be a class named
Parent1ReferencesParent2BuilderInstance <\/code>with three missing methods you have to override. Create a partial class in a new file and implement these methods (see 10 and 11).\n
CreateElementLink()<\/code> method in this file. Here you have to add code to store the selected compartment entries for source and target in the properties of the relationship (see 3).\n
protected override <\/span>ElementLink <\/span>CreateElementLink\r\n (Parent1 <\/span>source, \r\n SelectedCompartmentPartType <\/span>sourcePartType,\r\n Entry1 <\/span>sourceEntry, \r\n Parent2 <\/span>target,\r\n SelectedCompartmentPartType <\/span>targetPartType, \r\n Entry2 <\/span>targetEntry)\r\n{\r\n Parent1ReferencesParent2 <\/span>result;\r\n result = new <\/span>Parent1ReferencesParent2<\/span>(source, target);\r\n if<\/span>(sourcePartType== SelectedCompartmentPartType<\/span>.Head)\r\n \/\/ use a empty guid for the Head\r\n <\/span>result.fromEntry = Guid<\/span>.Empty; \r\n else\r\n <\/span>result.fromEntry = sourceEntry.Guid;\r\n\r\n if<\/span>(targetPartType== SelectedCompartmentPartType<\/span>.Head)\r\n \/\/ use a empty guid for the Head\r\n <\/span>result.toEntry = Guid<\/span>.Empty;\r\n else\r\n <\/span>result.toEntry = targetEntry.Guid;\r\n\r\n return <\/span>result;\r\n}<\/pre>\n
CreateElementLink()<\/code> method you have stored the source and target entry information somewhere; my library does not know about this location. So we need two additional methods to compare an entry with a relationship and answer the question "Is this entry the source of a given relationship?" and the same for the target. These code resists in the same file:\n
public override bool <\/span>IsEntryConnectionSource\r\n (Entry1 <\/span>entry, \r\n Parent1ReferencesParent2 <\/span>connection)\r\n{\r\n if <\/span>(entry == null<\/span>)\r\n return <\/span>connection.fromEntry.Equals(Guid<\/span>.Empty);\r\n\r\n return <\/span>connection.fromEntry.Equals(entry.Guid);\r\n}\r\n\r\npublic override bool <\/span>IsEntryConnectionTarget\r\n (Entry2 <\/span>entry, \r\n Parent1ReferencesParent2 <\/span>connection)\r\n{\r\n if <\/span>(entry == null<\/span>)\r\n return <\/span>connection.toEntry.Equals(Guid<\/span>.Empty);\r\n\r\n return <\/span>connection.toEntry.Equals(entry.Guid);\r\n}<\/pre>\n
entry == null<\/code>, this will be used to check whether the head of the shape is meant. Even if you do not allow the head to be source or target this method will be called with a
null<\/code> parameter from time to time.<\/p>\n<\/li>\n
partial class<\/code> for your Domain Model <\/em>(
CompartmentMappingExampleDomainModel<\/code>) and add the following methods.\n
protected override <\/span>Type<\/span>[] GetCustomDomainModelTypes()\r\n{\r\n return <\/span>CompartmentMappingUtil<\/span>.AllCompartmentMappingRules(this<\/span>);\r\n}<\/pre>\n
AllCompartmentMappingRules()<\/code> method.)<\/p>\n<\/li>\n
DslPackage<\/code> project create a
partial class<\/code> for the generated Command Set<\/em> and add the following code:<\/p>\n
protected override <\/span>IList<\/span><MenuCommand<\/span>> GetMenuCommands()\r\n{\r\n return <\/span>CompartmentMappingUtil<\/span>.RemoveRerouteCommand\r\n (base<\/span>.GetMenuCommands());\r\n}<\/pre>\n
Advanced Features<\/h3>\n
<Connection><\/code> element there is no longer a reference to the diagram elements, only the domain classes are used here. Obviously if you use inheritance all your classes need to have the same base class for
<DomainClass><\/code> itself and for
<EntryDomainClass><\/code>. If the concrete class is mapped to a compartment shape, the compartment mappings, otherwise a mapping of regular shapes, will be used.<\/p>\n
ModelElement<\/code> for
<EntryDomainClass><\/code>.<\/p>\n
<CompartmentShape><\/code> and
<RegularShape><\/code> therefore.<\/p>\n
Parent1ReferencesParent2BuilderInstance<\/code> class you can override some more methods to allow or forbid the creation of connectors. I think the names and signatures speak for themselves:<\/p>\n
\n
bool <\/span>CanAcceptAsCompartmentSource
(SOURCE_ELEMENT candidate,
SelectedCompartmentPartType <\/span>partType,
SOURCE_COMPARTMENT_ENTRY candidateEntry)<\/pre>\n<\/li>\n