One of the things I *really* want to implement for our DSL is “round tripping”. This means in my case, (at least) generate a “Service Description diagram” out of a valid WSDL file and visa versa.
Some weeks ago I already did an attempt in this direction but eventually I ran into problems with the serializer. As you can read in this post on the DSL forum the relations between concept got lost when trying to serialize (save to disk) the in-memory representation of the generated diagram. Probably this is caused by either a mistake on my side or a bug in the DSL Tools. Some days ago while playing with the propagation rules I found a way to make “round tripping” work in our DSL. Let me explain a little further.
I added a new property to the “Service” concept in the DSL, called “WsdlDescription”. This property represents a reference to a WSDL file describing the service. I added a new propagation rule that fires whenever the “WsdlDescription” property of the Service concept changes. This propagation rule, on its turn, fires a piece of code that parses the WSDL file (referenced by the “WsdlDescription” property) and generates a diagram out of this. With this code in place I can generate a diagram out of a WSDL file by simply creating a new empty diagram. To be more precise, adding a new item to the Visual Studio solution based on the “ServiceDescription” (name of the DSL) template. This results in an (valid) empty diagram. I can set the “WsdlDescription” property (service concept) in the empty diagram to reference a valid WSDL file and the magic happens, the diagram is generated! The advantage of this approach, compared to creating an empty diagram from scratch by using custom code, is that we can rely on the DSL Tools infra structure to properly initialize the diagram.
Of course you can find another “hook” to implement your round tripping code. This depends on the DSL you are building. For this moment, in our DSL, the propagation rule on this property does the trick.
Let’s have a look at some code. Below you see the code for the propagation rule that is defined for the “Service” concept in the model. First we try to cast the input parameter of the “ElementAttributeChanged” method to a “Service”. If that succeeds and the rule is fired because of a change in the “WsdlDescription” property we can continue. To create any new concepts in the diagram we need the “Store”. This can be seen as the in- memory representation of the diagram. We can get this by using the “Store” property on the “Service”. Then we create a “ServiceInterface and ServiceOperation and register both elements under the Service by using the “ServiceArtifacts” relation. (To better understand the model of this DSL, have a look at the, slightly outdated, image of the domain model of the DSL). Finally we add the “ServiceOperation” as an “Operation” under the “ServiceInterface” and we are done. We now created two model elements and a relation between them in code. All displayed perfectly in the diagram.

[RuleOn(typeof(OurCompany.Design.ServiceDescription.DomainModel.Service),
FireTime = TimeToFire.TopLevelCommit)]
public sealed class DomainModelServicePropertiesChangesRule : ChangeRule
{
public override void ElementAttributeChanged(ElementAttributeChangedEventArgs e)
{
// cast input parameter to Service element
OurCompany.Design.ServiceDescription.DomainModel.Service service =
e.ModelElement as OurCompany.Design.ServiceDescription.DomainModel.Service;
if (service != null)
{
// only execute it when the rules is fired because of a change in the WsdlDescription property.
if (e.MetaAttribute.Id ==
OurCompany.Design.ServiceDescription.DomainModel.Service.WsdlDescriptionMetaAttributeGuid)
{
// create new ServiceInterface. Use the store of the service
ServiceInterface serviceInterface = ServiceInterface.CreateServiceInterface(service.Store);
serviceInterface.Name = "GeneratedInterface";
// add ServiceInterface as an ServiceArtifact of the service
service.ServiceArtifacts.Add(serviceInterface);
// create new ServiceOperation. Use the store of the service
ServiceOperation serviceOperation = ServiceOperation.CreateServiceOperation(service.Store);
serviceOperation.Name = "GeneratedServiceOperation";
// add ServiceOperation as an ServiceArtifact of the service
service.ServiceArtifacts.Add(serviceOperation);
// add the relation from serviceInterface to serviceOperation
serviceInterface.ServiceOperation.Add(serviceOperation);
}
}
}
}
In the above code snippet all of the diagram generating is hard coded, this is, of course, just for this example. In my situation the hard coded stuff is replaced by some calls to helper functions that parse the WSDL and translate this back in a diagram.
To make sure the rules gets executed on changes in the Service concept we need to facilitate that it can be “picked up” by the “rule engine”. This is done by reflection; therefore we need the following code.
// This class is reflected on to load rules dynamically.
// Add a variable for each custom rule.
internal static partial class GeneratedMetaModelTypes
{
internal static Type DomainModelServicePropertiesChangesRule =
typeof(DomainModelServicePropertiesChangesRule);
}
Now the diagram is generated out of the WSDL, we have to make sure the WSDL stays in synch with any changes that might be made in the diagram. It’s my intention to let this synchronisation magic happen automatically, so without any user actions (pressing menu options, etc.). To experiment with this I added the “synchronization logic” to the “OnTransactionCommiting” event of the diagram. As you can see in the code snippet below this is just a matter of overriding the “OnTransactionCommiting” method in a partial “ServiceDescriptionDiagram” class.
public partial class ServiceDescriptionDiagram
{
public override void OnTransactionCommitting(TransactionCommitEventArgs e)
{
base.OnTransactionCommitting(e);
//validate the diagram and execute synch code
}
}
Although this works perfect I am not absolutely sure if this is the right place to implement this kind of logic. I noticed that the code gets fired a lot. Within this method I first validate the diagram to make sure there is enough information in the diagram to generate a valid WSDL file. If so, the generation (synchronisation) starts. I haven’t experienced any performance issues with this approach but maybe it is better to only synch when the diagram gets saved. I have to do some more investigation on this area.
To be continued….