Saturday, March 29, 2008

In a previous post I quickly mentioned the conceptual "Designer Bus" which is currently represented by "Backplane" in the Microsoft.VisualStudio.ToolIntegration.Backplane.dll" assembly. I stated we can use Backplane to "maintain" all kind of references between objects, model elements, designers, tool windows, etc. In this post we will dive a little deeper to see how we can use Backplane to get a reference to a  model element in a Domain Specific Language.

Anyone familiar with Domain Specific Languages (DSl Tools) probably also heard of the Designer Integration Powertoy (DIS). DIS can be used to create (and resolve) references between model elements withing or accross languages. Unfortunately the solution that DIS is offering is pretty limited. Hopefully Backplane will do a better job!

Here is high level overview of the steps I took to "investigate" this.

First I used the Microsoft DSL Tools to created a simple Domain Specific Language called "ExampleLanguage" which is based on the "Minimal Language" template. As you can see in the screenshot below this is really simple DSL but enough for this test. 

ExampleLanguage 

 Second, I created a class called "ExampleLanguageDesignerServiceManager" that inherits from "VSServiceManager" (Microsoft.VisualStudio.ToolIntegration.Backplane.VSHost.dll). Below you can see what this class looks like. 

ServiceManager

(note: "ReferencedDataContracts" and "ServiceContract" have nothing to do with the concepts we know from for example WCF)

The only important thing to remember from this class (for now) is the "CreateService" method that returns a "ExampleLanguageDesignerService" that is displayed below. This class inherits from VSService (Microsoft.VisualStudio.ToolIntegration.Backplane.VSHost.dll) and basically wraps my "ExampleLanguage" DSL. For example, you can see it has a (private) property for the "Diagram", "DocData" and "ExampleModel" which is the root of my DSL. (It does have a lot more butI skipped that in this screenshot.) 

 DesignerService

 An interesting method in the "ExampleLanguageDesignerService" class is the "GetExampleModelElements" method which returns a ReadOnlyCollection of "ReferencedExampleElement". As you can see in the screenshot of the DSL defintion above, "ReferencedExampleElement" isn't part of the "ExampleLanguage" DSL so, where does this come from?

Backplane introduces the "ReferencedObject" class (Microsoft.VisualStudio.ToolIntegration.Backplane.dll) that represents an object with the capability of "being referrenced". One of the properties of "ReferencedObject" is "Reference" that holds the "location" where Backplane can find the object. What I did is, I created a "ReferencedExampleElement" class (screenshot below) that inherits from "ReferencedObject" and "wraps" the "ExampleElement" class from the DSL. The "GetExampleModelElements" method in the "ExampleLanguageDesignerService" loops through all elements in the DSL, wraps them in a "ReferencedExampleElement", adds them to a collection and returns the collection.  

ReferencedExampleElement

 (Note: anyone interested in the source code that is needed to actually implement the above mentioned classes, have a look at "Microsoft.VisualStudio.TeamArchitect.LogicaClassDesigner.ToolAdapter.dll". This assembly contains similar classes that will help you understand the details.)

The next thing I did is I made sure the above mentioned classes get loaded by Visual Studio. To do this I included the above mentioned "ExampleLanguageDesignerManager" in one of my test factories called  "MyFirstRosarioFactory". The screenshot below shows the Software Factory Schema for this factory.

 

Schema

 As we can see I defined the "ExampleLanguage" viewpoint and registered the "ExampleLanguageDesignerManager" which ensures at some point it gets loaded by the "Software Factory Runtime". In the following code example (that I implemented in a command in my factory) we can see I am using Backplane to get a reference to the "ExampleLanguageDesignerServiceManager". After that we can use the "ExampleLanguageDesignerServiceManager" to create an instance of the "ExampleLanguageDesignerService". Then we can use the "ExampleLanguageDesignerService" to get all elements (ReferencedExampleElement) from a specific dsl model (without using DIS!)

Code2

 (note: some code is missing in the above screenshot. for example, we have to define a "scope" to define the solution, project, item, etc. where we are interacting with) 

I already mentioned my "ReferencedExampleElement" class inherits from the "ReferencedObject" class implemented in Backplane. This "ReferencedObject" object has a property called "Reference". When running the above code in debug mode I can see that the "Reference" property of "MyElement" (code snippet above) has a value of

"backplane://C:\\Dev\\MyFirstFactory\\MyFirstRosarioFactory1.pdef/MyCompany.ExampleLanguageToolAdapter.ExampleLanguageDesignerServiceManager/9d375fb7-bccd-4d03-a6ec-20340517d360\\MyModel.mydsl/6568ccec-604f-40c3-b143-9962a0901807""

 

The cool thing of Backplane is that once we know the "Reference" of an object we can ask Backplane to resolve it for us with code that looks like:

Code3

 

To be continued...