SixPairs

May 8, 2011

Visual DGML

Filed under: DGML — Tags: — Ceyhun Ciper @ 20:58

Writing DGML by hand is hard; so I developed a Visual DGML designer.

The designer is a graphic DSL; here is how I use it to manage the project itself:

image

Upon save, you get a DGML graph added to your project (click to enlarge):

image

The links point to and open UML diagrams, documents, web pages, class diagrams and source code.

April 7, 2011

Don’t do Reflection, do DGML

Filed under: DGML, Reflection — Ceyhun Ciper @ 13:02

If you are analyzing your solutions and projects via reflection, you can’t do it any better than Microsoft. Even ReSharper does not come close. What did they produce? An XML output called DGML. Why not leverage it?

In their output they provide:

  • References
  • Dependencies
  • Return values
  • Call graphs

And it is not meant just for human consumption/interpretation as it is XML with a well published schema.

I bet you can’t get call dependencies via reflection (except for using some FxCop DLLs and/or developing your own Reflector add-on).

But the data is already there if you use VS Ultimate/Premium, and as XML; the only question is how to practically use this output. In a previous article I mentioned my DGML API…

Some use-cases:

  • Find all unneeded references in a solution
  • Call graphs
  • Property usage in functions

You can also just produce your own HTML5 reporting tool via SVG (which I did); no need for Visual Studio, let alone Ultimate.

Low-Level DGML Library

Filed under: DGML — Ceyhun Ciper @ 10:24

Introduction

What is it?

This is a low-level library (Ciper.Dgml.Schema.dll) that provides an object model on top of DGML documents; as such, it can be considered as a read/write abstraction layer on top of DGML documents. I have higher-level libraries and tools that provide more functionality based on this one (notably “Ciper.Dgml.dll”, “DGMLViewer” and “VisualDGML”).

Why might you need it?

There are four principal areas where you might need it:

1. Manipulate DGML outside Visual Studio

2. Even inside Visual Studio, use code instead of XML for processing DGML documents

3. Process DGML with a lower version of Visual Studio than “Ultimate” or “Premium” (e.g. “Visual Studio Professional” or even the “Express” versions)

4. Programmatically create, access and modify DGML documents without the need for Microsoft’s non-redistributable private DLLs (a.k.a. the “Progression API”)

What can you do with it?

Here are some of the things that you can do with it:

1. Create DGML documents

2. Modify and annotate DGML documents

3. Investigate/Manipulate a DGML document without having the proper Visual Studio version or even Visual Studio at all (maybe somebody with “Ultimate” sent it to you)

4. Use Linq over DGML

5. Write your own DGML tool with no dependencies on Visual Studio or its DLLs

6. Represent a DGML diagram using another graph visualization tool (such as “GraphViz”) (I have tools for that)

7. Translate DGML to SVG, maybe for consuming in your HTML (I have tools for that too)

Tutorial

Adding Nodes

var g = new DirectedGraph();

g.Nodes = new Nodes();

var n = new Node();

n.Id = “Ciper.Dgml.Schema”;

g.Nodes.Add(n);

clip_image002

The “Nodes” collection is optional, so it must be created explicitely.

Changing Node Attributes

Adding just nodes to a graph may not be great fun but it requires some knowlegde if you want to control its disposition:

var g = new DirectedGraph(true);

g.Nodes.Add(Enumerable.Range(1,15).Select(i => Math.Pow(i,i*2/3)));

clip_image004

Here nodes different sizes; to make them all the same size:

var g = new DirectedGraph(true);

g.Nodes.Add(Enumerable.Range(1,15).Select(i => new Node(Math.Pow(i,i*2/3)) { MinWidth = 100 }));

clip_image006

var reasons = new [] {

“Manipulate DGML outside Visual Studio”,

“Even inside Visual Studio, use code instead of XML for processing DGML documents”,

“Process DGML with a lower version of Visual Studio than “Ultimate” or “Premium” (e.g. “Visual Studio Professional” or even the “Express” versions)”,

“Programmatically create, access and modify DGML documents without the need for Microsoft’s non-redistributable private DLLs (a.k.a. the “Progression API”)”,

};

var g = new DirectedGraph(true);

g.Nodes.Add(reasons);

clip_image008

var reasons = new[] {

“Manipulate DGML outside Visual Studio”,

“Even inside Visual Studio, use code instead of XML for processing DGML documents”,

“Process DGML with a lower version of Visual Studio than “Ultimate” or “Premium” (e.g. “Visual Studio Professional” or even the “Express” versions)”,

“Programmatically create, access and modify DGML documents without the need for Microsoft’s non-redistributable private DLLs (a.k.a. the “Progression API”)”,

};

var g = new DirectedGraph(true) { GraphDirection = “TopToBottom” };

g.Nodes.Add(reasons.Select(s => new Node(s)

{ MaxWidth = 150, MinWidth = 150, Background = “Transparent”, Foreground = “Black”, Stroke = “Transparent”, FontFamily = “Arial” }));

clip_image010

Chaging a Node’s Shape

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id=”Ciper.Dgml.Schema”, NodeRadius=0 });

g.Nodes.Add(new Node { Id=”Ciper.Dgml”, NodeRadius=4 });

g.Nodes.Add(new Node { Id=”DGMLViewer”, NodeRadius=50 });

clip_image012

Here, “Ciper.Dgml.Schema” is the low-level library, “Ciper.Dgml” is the proper DGML library and “DGMLViewer” is the viewer that lets you view DGML documents outside Visual Studio.

Although “Node” has a “Shape” property, it is only used to suppress the usage of any shape and thus accepts a single value: “None”.

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id=”DGMLViewer” });

g.Nodes.Add(new Node { Id=”Ciper.Dgml.Schema”, Shape = “None” });

clip_image014

This is especially useful when icons are used in nodes.

Adding Icons to Nodes

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id=”1″, Label = “File”, Icon = “File” });

g.Nodes.Add(new Node { Id=”2″, Label = “File”, Icon = “File”, Shape = “None” });

clip_image016

I don’t know why this works but “File” seems to be an intrinsic icon (not like the stock icons that come with the Progression DLLs). And I don’t know what others are there; I bumped into this one by luck.

Here are some icons:

var g = new DirectedGraph();

g.Nodes = new Nodes();

var icons = new[] { “DLL”, “Document”, “Layer”, “Library”, “Model” };

foreach (var icon in icons)

{

g.Nodes.Add(new Node

{

Id = icon,

Icon = string.Format(@”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\{0}_32x32.png”, icon),

Shape = “None”, VerticalAlignment = “Bottom”, HorizontalAlignment = “Center”

});

}

clip_image018

var g = new DirectedGraph();

g.Nodes = new Nodes();

var components = new[] {

new { Id = “Ciper.Dgml.Schema”, Icon = “DLL” },

new { Id = “DGML”, Icon = “Document” },

new { Id = “Ciper.Dgml.Svg”, Icon = “Layer” },

new { Id = “Ciper.Dgml”, Icon = “Library” },

new { Id = “DGMLViewer”, Icon = “Model” },

};

foreach (var component in components)

{

g.Nodes.Add(new Node

{

Id = component.Id,

Icon = string.Format(@”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\{0}_32x32.png”, component.Icon),

Shape = “None”, VerticalAlignment = “Bottom”, HorizontalAlignment = “Center”

});

}

clip_image020

Here, DGML is the document, Ciper.Dgml.Schema is the low-level DLL, Ciper.Dgml is the library, Ciper.Dgml.Svg is the layer that converts DGML to SVG and DGMLViewer is the tool that visualizes DGML without any need for Visual Studio.

There are some stock icons:

var g = new DirectedGraph(true);

g.Nodes.Add(new Node(“Globe”) { Icon = Icons.globe });

g.Nodes.Add(new Node(“Printer”) { Icon = Icons.printer });

g.Nodes.Add(new Node(“Monitor”) { Icon = Icons.monitor });

g.Nodes.Add(new Node(“Users”) { Icon = Icons.users });

clip_image022

Here is all of them:

var g = new DirectedGraph(true);

var icons =

from name in Icons.ProgressionIconNames

select new Node(name)

{

Icon = Icons.ProgressionIcons[name],

HorizontalAlignment = “Center”, MinWidth = 150, Shape = “None”

};

g.Nodes.Add(icons);

clip_image024

Chaging a Node’s Style

Although “Node” has a style property, it has nothing to do with DGML “Styles”; it accepts only one value: “Plain”; this is in case you don’t like or prefer the “Glass” look.

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id=”1″, Background = “Olive” });

g.Nodes.Add(new Node { Id=”2″, Background = “Olive”, Style = “Plain” });

clip_image026

Exercises

1. Show all files in a folder

2. Show all types in an assembly

Adding Links

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “Ciper.Dgml” });

g.Nodes.Add(new Node { Id = “Ciper.Dgml.Schema” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Ciper.Dgml”, Target = “Ciper.Dgml.Schema” });

clip_image028

The “Links” collection is optional, so it must be created explicitely.

References between nodes and links are resolved via nodes’ Id’s instead of node instances; this weakly-typed approach is error-prone because the existence of nodes involved is not checked; the graph will be viewed and used correctly in Visual Studio but probably not outside it (i.e. in some other consuming program, such as my “DGMLViewer” and “VisualDGML” tools). So a better approach is to use specific instances of nodes when creating links:

var g = new DirectedGraph();

g.Nodes = new Nodes();

var n1 = g.Nodes.Add(new Node { Id = ” Ciper.Dgml” });

var n2 = g.Nodes.Add(new Node { Id = ” Ciper.Dgml.Schema” });

g.Links = new Links();

g.Links.Add(new Link(n1, n2));

As the nodes do not have to be added explicitely, even the following shortcut can be taken (in Visual Studio the nodes will be created automatically when a link is created):

var g = new DirectedGraph();

g.Links = new Links();

g.Links.Add(new Link { Source = ” Ciper.Dgml”, Target = ” Ciper.Dgml.Schema” });

However, as the nodes will not exist in the resulting DGML file, consumer applications other than Visual Studio will probably fail to process it.

Adding Link Labels

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “Ciper.Dgml” });

g.Nodes.Add(new Node { Id = “Ciper.Dgml.Schema” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Ciper.Dgml”, Target = “Ciper.Dgml.Schema”, Label = “depends on” });

clip_image030

Here is the structure of the projects in the DGML panoply:

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node(“VisualDGML”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\DSL_32x32.png”,

Foreground = “#FF000080” });

g.Nodes.Add(new Node(“DGMLViewer”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\Package_32x32.png”,

Foreground = “#FF000080” });

g.Nodes.Add(new Node(“Ciper.Dgml.Svg”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\Library_32x32.png” });

g.Nodes.Add(new Node(“Ciper.Dgml.Dot”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\Library_32x32.png” });

g.Nodes.Add(new Node(“Ciper.Dgml”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\Library_32x32.png” });

g.Nodes.Add(new Node(“Ciper.Dgml.Schema”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\Library_32x32.png”,

Foreground = “#FF800000” });

g.Nodes.Add(new Node(“Dgml Document”)

{ Icon = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons\Document_32x32.png” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Ciper.Dgml.Schema”, Target = “Dgml Document”, Label = “serializes” });

g.Links.Add(new Link { Source = “Ciper.Dgml”, Target = “Ciper.Dgml.Schema”, Label = “abstracts” });

g.Links.Add(new Link { Source = “Ciper.Dgml.Dot”, Target = “Ciper.Dgml” });

g.Links.Add(new Link { Source = “Ciper.Dgml.Svg”, Target = “Ciper.Dgml.Dot” });

g.Links.Add(new Link { Source = “VisualDGML”, Target = “Ciper.Dgml.Svg” });

g.Links.Add(new Link { Source = “VisualDGML”, Target = “Ciper.Dgml” });

g.Links.Add(new Link { Source = “DGMLViewer”, Target = “Ciper.Dgml.Svg” });

g.Links.Add(new Link { Source = “DGMLViewer”, Target = “Ciper.Dgml.Dot” });

foreach (var node in g.Query.Descendants<Node>())

{

node.HorizontalAlignment = “Center”;

node.VerticalAlignment = “Bottom”;

node.Shape = “None”;

}

clip_image032

Exercises

1. Display a table

2. Show the Paris Metro

3. Show neighboring countries in Europe

Worked Exercises

Display a table

var software = new[] {

new Node(“DGMLViewer”, “Package”),

new Node(“VisualDGML”, “DSL”),

new Node(“VisualDot”, “DSL”),

new Node(“Ciper.Dgml”, “Library”),

new Node(“Ciper.Dgml.Svg”, “Library”),

new Node(“Ciper.Dgml.Dot”, “Library”),

new Node(“Ciper.Dgml.Schema”, “Library”),

new Node(“Ciper.Dot”, “Library”),

new Node(“Ciper.Svg”, “Library”),

new Node(“Ciper.Svg2Xaml”, “Library”),

new Node(“Dgml”, “Document”),

};

var g = new DirectedGraph(true) { GraphDirection = “LeftToRight” };

var dummy = g.Nodes.Add(new Node(” “) { Shape = “None” });

foreach (var s in software)

{

var catNode = g.Nodes.Add(new Node(s.Id+s.Category1) { Label = s.Category1 });

g.Links.AddPath(s, catNode, dummy);

}

g.Styles.Add(new Style

{

TargetType = “Node”,

Setters =

{

new Setter(“MinWidth”, “100”),

new Setter(“MaxWidth”, “100”),

new Setter(“NodeRadius”, “0”),

new Setter(“Background”, “Transparent”),

new Setter(“FontFamily”, “Gill Sans MT”),

new Setter(“Stroke”, “Transparent”),

new Setter(“Foreground”, “Black”),

}

});

g.Styles.Add(“Link”, “Stroke”, “Transparent”);

clip_image034

Adding Groups

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Paris” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

clip_image036

The “Group” property on “Node” does not have to be specified; if it is not, the node will look like a normal “Node” instead of a “Group”; however it can be shown as a group by choosing the “Show as Group” option in the DGML Viewer popup menu; what characterizes a “Node” as a group is actually the “Contains” value of the “Category1” property on the “Link” that has the node as source.

var g = new DirectedGraph();

g.Links = new Links();

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Paris” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

clip_image038

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Paris”, Label = “Capital” });

g.Links.Add(new Link { Source = “Town”, Target = “Lyon” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Lyon”, Category1 = “Contains” });

clip_image040

var software = new[] {

new { Id = 1000, Name = “DGMLViewer”, Kind = “Package”, Refs = new[] { 1101, 1102 } },

new { Id = 1010, Name = “VisualDGML”, Kind = “DSL”, Refs = new[] { 1100, 1101 } },

new { Id = 1011, Name = “VisualDot”, Kind = “DSL”, Refs = new[] { 1100 } },

new { Id = 1100, Name = “Ciper.Dgml”, Kind = “Library”, Refs = new[] { 1103 } },

new { Id = 1101, Name = “Ciper.Dgml.Svg”, Kind = “Library”, Refs = new[] { 1102 } },

new { Id = 1102, Name = “Ciper.Dgml.Dot”, Kind = “Library”, Refs = new[] { 1100 } },

new { Id = 1103, Name = “Ciper.Dgml.Schema”, Kind = “Library”, Refs = new[] { 1200 } },

new { Id = 1200, Name = “Dgml”, Kind = “Document”, Refs = new int[0] },

};

var iconPath = @”C:\Ceyhun\PrivateProjects\Ciper.Dgml.Schema\Icons”;

var g = new DirectedGraph { Background = “#FF004040” };

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “Packages”, Group = “Expanded”, Background=”#FF006060″ });

g.Nodes.Add(new Node { Id = “DSLs”, Group = “Expanded”, Background=”#FF606000″ });

g.Nodes.Add(new Node { Id = “Libraries”, Group = “Expanded”, Background=”#FF406060″ });

foreach (var s in software)

g.Nodes.Add(new Node(s.Id.ToString())

{

Label = s.Name, Foreground=”White”,

Icon = string.Format(@”{0}\{1}_32x32.png”, iconPath, s.Kind),

Shape = “None”, HorizontalAlignment = “Center”, VerticalAlignment = “Bottom”

});

g.Links = new Links();

var groups = from s in software

where s.Kind != “Document”

group s by s.Kind into sg

let category = Regex.Replace(sg.Key, “y$”, “ie”)+”s”

from soft in sg

let link = g.Links.Add(category, soft.Id.ToString(), true)

select link;

groups.ToList(); // Execute the query

var links = from s in software

from r in s.Refs

let link = g.Links.Add(s.Id.ToString(), r.ToString())

select link;

links.ToList(); // Execute the query

clip_image042

clip_image044

clip_image046

clip_image048[1]

Exercises

1. Show all types, methods and properties in the “System” namespace

Adding Categories

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded” });

g.Nodes.Add(new Node { Id = “Paris”, Category1 = “Fashion” });

g.Nodes.Add(new Node { Id = “Lyon”, Category1 = “Gastronomy” });

g.Nodes.Add(new Node { Id = “Bordeaux”, Category1 = “Gastronomy” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Town”, Target = “Paris”, Label = “Capital” });

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Lyon” });

g.Links.Add(new Link { Source = “Town”, Target = “Bordeaux” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Lyon”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Bordeaux”, Category1 = “Contains” });

clip_image050[1]

This approach is valid only if you are adding a single category.

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node(“France”, GroupType.Expanded));

g.Nodes.Add(new Node(“US”, GroupType.Expanded));

g.Nodes.Add(new Node(“Paris”, “Fashion”));

g.Nodes.Add(new Node(“Lyon”, “Gastronomy”));

g.Nodes.Add(new Node(“Bordeaux”, “Gastronomy”));

g.Nodes.Add(new Node(“New York”, “Finance”));

g.Links = new Links();

g.Links.Add(new Link(“Town”, “Paris”) { Label = “Capital” });

g.Links.Add(new Link(“Town”, “Washington D.C.”) { Label = “Capital” });

g.Links.AddPairs(“Country”, “France”, “Country”, “US”, “Town”, “Lyon”, “Town”, “Bordeaux”, “Town”, “New York”);

g.Links.AddContainers(“France”, “Paris”, “Lyon”, “Bordeaux”);

g.Links.AddContainers(“US”, “Washington D.C.”, “New York”);

clip_image052

var software = new[] {

new { Id = 1000, Name = “DGMLViewer”, Kind = “Package”, Refs = new[] { 1101, 1102, 1106 } },

new { Id = 1010, Name = “VisualDGML”, Kind = “DSL”, Refs = new[] { 1100, 1101 } },

new { Id = 1011, Name = “VisualDot”, Kind = “DSL”, Refs = new[] { 1104, 1105, 1106 } },

new { Id = 1100, Name = “Ciper.Dgml”, Kind = “Library”, Refs = new[] { 1103 } },

new { Id = 1101, Name = “Ciper.Dgml.Svg”, Kind = “Library”, Refs = new[] { 1102, 1105 } },

new { Id = 1102, Name = “Ciper.Dgml.Dot”, Kind = “Library”, Refs = new[] { 1100, 1104 } },

new { Id = 1103, Name = “Ciper.Dgml.Schema”, Kind = “Library”, Refs = new[] { 1200 } },

new { Id = 1104, Name = “Ciper.Dot”, Kind = “Library”, Refs = new int[0] },

new { Id = 1105, Name = “Ciper.Svg”, Kind = “Library”, Refs = new int[0] },

new { Id = 1106, Name = “Ciper.Svg2Xaml”, Kind = “Library”, Refs = new int[0] },

new { Id = 1200, Name = “Dgml”, Kind = “Document”, Refs = new int[0] },

};

var g = new DirectedGraph { Nodes = new Nodes(), Links = new Links() };

var links = from s in software

let node = g.Nodes.Add(new Node(s.Id, s.Kind) { Label=s.Name })

from r in s.Refs

select g.Links.Add(s.Id, r);

links.ToList(); // Execute the query

clip_image054

Adding Multiple Categories

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded” });

var paris = new Node { Id = “Paris”, Category1 = “Fashion” };

paris.Category.Add(new Node.CategoryLocalType { Ref = “Gastronomy” });

g.Nodes.Add(paris);

g.Nodes.Add(new Node { Id = “Lyon”, Category1 = “Gastronomy” });

g.Nodes.Add(new Node { Id = “Bordeaux”, Category1 = “Gastronomy” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Town”, Target = “Paris”, Label = “Capital” });

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Lyon” });

g.Links.Add(new Link { Source = “Town”, Target = “Bordeaux” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Lyon”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Bordeaux”, Category1 = “Contains” });

clip_image056

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded” });

var paris = new Node { Id = “Paris”, Category1 = “Fashion” };

paris.Category.Add(new Node.CategoryLocalType { Ref = “Gastronomy” });

paris.Category.Add(new Node.CategoryLocalType { Ref = “Capital” });

g.Nodes.Add(paris);

g.Nodes.Add(new Node { Id = “Lyon”, Category1 = “Gastronomy” });

g.Nodes.Add(new Node { Id = “Bordeaux”, Category1 = “Gastronomy” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Town”, Target = “Paris”, Label = “Capital” });

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Lyon” });

g.Links.Add(new Link { Source = “Town”, Target = “Bordeaux” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Lyon”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Bordeaux”, Category1 = “Contains” });

clip_image058

Note that the styles of different towns are set manually here; later we will see how to apply these styles programmatically.

But why make “Country” and “Town” nodes when we can assign categories to corresponding nodes?

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded”, Category1 = “Country” });

g.Nodes.Add(new Node(“Paris”)).AddCategories(“Gastronomy”, “Capital”, “Fashion”, “Town”);

g.Nodes.Add(new Node(“Lyon”)).AddCategories(“Gastronomy”, “Town”);

g.Nodes.Add(new Node(“Bordeaux”)).AddCategories(“Gastronomy”, “Town”);

g.Nodes.Add(new Node(“Nice”)).AddCategories(“Town”);

g.Links = new Links();

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Lyon”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Bordeaux”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Nice”, Category1 = “Contains” });

clip_image060

Adding Custom Properties

var g = new DirectedGraph();

g.Nodes = new Nodes();

var n = new Node { Id = “1” };

g.Nodes.Add(n);

g.Properties = new Properties();

g.Properties.Add(new Property { Id = “Minimum”, DataType = “System.Byte” });

g.Properties.Add(new Property { Id = “Maximum”, DataType = “System.Byte” });

n.Untyped.Add(new XAttribute(“Minimum”, byte.MinValue));

n.Untyped.Add(new XAttribute(“Maximum”, byte.MaxValue));

As not all possible properties have been defined on all graph elements, we have to add the custom property as an attribute to the element’s Xml definition (“element.Untyped”). We also optionally add the property’s definition to the graph.

Applying Custom Styles

Applying Custom Styles to all Nodes or Links

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “1” });

g.Nodes.Add(new Node { Id = “2” });

g.Styles=new Styles();

var setter = new Setter();

setter.Property=”Foreground”;

setter.Untyped.SetAttributeValue(“Value”, “Magenta”);

g.Styles.Add(new Style { TargetType=”Node”, Setter = { setter } });

Notice that you cannot just use ‘setter.Value = “Magenta”;’.

var g = new DirectedGraph();

g.Links = new Links();

g.Links.Add(new Link { Source = “1”, Target = “2” });

g.Links.Add(new Link { Source = “2”, Target = “3” });

g.Links.Add(new Link { Source = “3”, Target = “4” });

g.Styles=new Styles();

var setter = new Setter();

setter.Property=”Stroke”;

setter.Untyped.SetAttributeValue(“Value”, “Transparent”);

g.Styles.Add(new Style { TargetType=”Link”, Setter = { setter } });

clip_image062

Applying Custom Styles to Nodes or Links based on certain Conditions

var g = new DirectedGraph();

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id = “France”, Group = “Expanded” });

var paris = new Node { Id = “Paris”, Category1 = “Fashion” };

paris.Category.Add(new NodeCategory { Ref = “Gastronomy” });

paris.Category.Add(new NodeCategory { Ref = “Capital” });

g.Nodes.Add(paris);

g.Nodes.Add(new Node { Id = “Lyon”, Category1 = “Gastronomy” });

g.Nodes.Add(new Node { Id = “Bordeaux”, Category1 = “Gastronomy” });

g.Links = new Links();

g.Links.Add(new Link { Source = “Town”, Target = “Paris”, Label = “Capital” });

g.Links.Add(new Link { Source = “Country”, Target = “France” });

g.Links.Add(new Link { Source = “Town”, Target = “Lyon” });

g.Links.Add(new Link { Source = “Town”, Target = “Bordeaux” });

g.Links.Add(new Link { Source = “France”, Target = “Paris”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Lyon”, Category1 = “Contains” });

g.Links.Add(new Link { Source = “France”, Target = “Bordeaux”, Category1 = “Contains” });

g.Styles = new Styles();

var gastroSetter = new Setter { Property = “Background” };

gastroSetter.SetValue(“#CCCC00”);

var gastroStyle = g.Styles.Add(new Style { TargetType = “Node”, Setters = { gastroSetter } });

gastroStyle.Condition = new Condition();

gastroStyle.Condition.SetExpression(“HasCategory(‘Gastronomy’)”);

var fashionSetter = new Setter { Property = “Foreground” };

fashionSetter.SetValue(“#880088”);

var fashionStyle = g.Styles.Add(new Style { TargetType = “Node”, Setters = { fashionSetter } });

fashionStyle.Condition = new Condition();

fashionStyle.Condition.SetExpression(“HasCategory(‘Fashion’)”);

var borderSetter = new Setter { Property = “Stroke” };

borderSetter.SetValue(“#000088”);

var thicknessSetter = new Setter { Property = “StrokeThickness” };

thicknessSetter.SetValue(“1.5”);

var capitalStyle = g.Styles.Add(new Style { TargetType = “Node”, Setters = { borderSetter, thicknessSetter } });

capitalStyle.Condition = new Condition();

capitalStyle.Condition.SetExpression(“HasCategory(‘Capital’)”);

clip_image064

var software = new[] {

new { Id = 1000, Name = “DGMLViewer”, Kind = “Package”, Refs = new[] { 1101, 1102, 1106 } },

new { Id = 1010, Name = “VisualDGML”, Kind = “DSL”, Refs = new[] { 1100, 1101 } },

new { Id = 1011, Name = “VisualDot”, Kind = “DSL”, Refs = new[] { 1104, 1105, 1106 } },

new { Id = 1100, Name = “Ciper.Dgml”, Kind = “Library”, Refs = new[] { 1103 } },

new { Id = 1101, Name = “Ciper.Dgml.Svg”, Kind = “Library”, Refs = new[] { 1102, 1105 } },

new { Id = 1102, Name = “Ciper.Dgml.Dot”, Kind = “Library”, Refs = new[] { 1100, 1104 } },

new { Id = 1103, Name = “Ciper.Dgml.Schema”, Kind = “Library”, Refs = new[] { 1200 } },

new { Id = 1104, Name = “Ciper.Dot”, Kind = “Library”, Refs = new int[0] },

new { Id = 1105, Name = “Ciper.Svg”, Kind = “Library”, Refs = new int[0] },

new { Id = 1106, Name = “Ciper.Svg2Xaml”, Kind = “Library”, Refs = new int[0] },

new { Id = 1200, Name = “Dgml”, Kind = “Document”, Refs = new int[0] },

};

var g = new DirectedGraph(true);

var links = from s in software

let node = g.Nodes.Add(new Node(s.Id, s.Kind) { Label=s.Name })

from r in s.Refs

select new Link(s.Id, r);

g.Links.Add(links);

g.Styles.Add(new Style

{

TargetType = “Node”,

Setters =

{

new Setter(“Background”, “#600060”),

},

Condition = new Condition(“HasCategory(‘DSL’)”),

});

g.Styles.Add(new Style

{

TargetType = “Node”,

Setters =

{

new Setter(“Background”, “#606000”),

},

Condition = new Condition(“HasCategory(‘Package’)”),

});

g.Styles.Add(new Style

{

TargetType = “Node”,

Setters =

{

new Setter(“Background”, “#006060”),

},

Condition = new Condition(“HasCategory(‘Library’)”),

});

clip_image066

var reasons = new[] {

“Why you might need this Library?”,

“Manipulate DGML outside Visual Studio”,

“Even inside Visual Studio, use code instead of XML for processing DGML documents”,

“Process DGML with a lower version of Visual Studio than “Ultimate” or “Premium” (e.g. “Visual Studio Professional” or even the “Express” versions)”,

“Programmatically create, access and modify DGML documents without the need for Microsoft’s non-redistributable private DLLs (a.k.a. the “Progression API”)”,

};

var g = new DirectedGraph(true) { GraphDirection = “LeftToRight” };

g.Nodes.Add(new Node(reasons[0], “Question”));

g.Nodes.Add(reasons.Skip(1));

var links = from r in reasons.Skip(1)

let q = reasons[0]

select new Link(q, r);

g.Links.Add(links);

g.Styles.Add(new Style(“Node”, “Icon”, Icons.help, “HasCategory(‘Question’)”));

g.Styles.Add(new Style(“Node”, “FontFamily”, “Times New Roman”, “HasCategory(‘Question’)”));

g.Styles.Add(new Style(“Node”, “FontStyle”, “Italic”, “HasCategory(‘Question’)”));

g.Styles.Add(new Style

{

TargetType = “Node”,

Setters =

{

new Setter(“MaxWidth”, “150”),

new Setter(“MinWidth”, “150”),

new Setter(“Shape”, “None”),

new Setter(“FontFamily”, “Century Schoolbook”),

new Setter(“Icon”, Icons.kpi_green_sym2_large),

},

});

g.Styles.Add(new Style(“Link”, “Stroke”, “Transparent”));

clip_image068

var what = new[] {

“What can you do with this library?”,

“Create DGML documents”,

“Modify and annotate DGML documents”,

“Investigate/Manipulate a DGML document without having the proper Visual Studio version or even Visual Studio at all (maybe somebody with “Ultimate” sent it to you)”,

“Use Linq over DGML”,

“Write your own DGML tool with no dependencies on Visual Studio or its DLLs”,

“Represent a DGML diagram using another graph visualization tool (such as “GraphViz”) (I have tools for that)”,

“Translate DGML to SVG, maybe for consuming in your HTML (I have tools for that too)”,

};

var g = new DirectedGraph(true) { GraphDirection = “LeftToRight” };

g.Nodes.Add(new Node(what[0], “Question”));

g.Nodes.Add(what.Skip(1));

var links = from r in what.Skip(1)

let q = what[0]

select new Link(q, r);

g.Links.Add(links);

g.Styles.Add(new Style(“Node”, “Icon”, Icons.help, “HasCategory(‘Question’)”));

g.Styles.Add(new Style(“Node”, “FontFamily”, “Times New Roman”, “HasCategory(‘Question’)”));

g.Styles.Add(new Style(“Node”, “FontStyle”, “Italic”, “HasCategory(‘Question’)”));

g.Styles.Add(new Style

{

TargetType = “Node”,

Setters =

{

new Setter(“MaxWidth”, “150”),

new Setter(“MinWidth”, “150”),

new Setter(“Shape”, “None”),

new Setter(“FontFamily”, “Century Schoolbook”),

new Setter(“Icon”, Icons.kpi_green_sym2_large),

},

});

g.Styles.Add(“Link”, “Stroke”, “Transparent”);

clip_image070

Troubleshooting

Frequent Mistakes

Forgetting to create collections before adding graph elements:

g.Nodes = new Nodes();

g.Nodes.Add(new Node { Id=”1″ });

g.Styles = new Styles();

g.Styles.Add(new Style());

December 4, 2010

DGML Builder

Filed under: DGML — Ceyhun Ciper @ 17:25

There currently is no way to generate DGML in a visual way; so I developed a DSL to generate DGML from a visual designer.

image

Of course, the hardest part is creating and managing groups:

image

October 27, 2010

DGQL Tutorial

Filed under: DGML, DGQL — Tags: , — Ceyhun Ciper @ 17:24

This is a short tutorial on DGQL using Visual Studio 2010.

Pre-requisite: Understanding Directed Graph Query Language (DGQL)

Simplest query:

*

Create a ClassLibrary1 and add a text file (“Graph.dgql”) containing the above query to it; you will end-up with the following in “Architecture Explorer”:

image

Get the solution view:

+”Solution View”

Get all the elements of a solution (essentially, projects & solution folders):

+”Solution View”//

image

Get the class view:

+”Class View”

Get all the namespaces:

+”Class View”//

image

Select only “projects” in the solution:

+”Solution View”//
+Category.Is(“CodeSchema_Project”)image

Select only “solution folders” in the solution:

+”Solution View”//
+Category.Is(“SolutionFolder”)
image

Get all “project folders” in “all projects” in the solution:

+”Solution View”//
+Category.Is(“CodeSchema_Project”)/”Node:Both:ProjectFolder”

image

September 21, 2010

A ToDo Language for DGML

Filed under: DGML — Ceyhun Ciper @ 00:24

 

I developed a small DSL (a ToDo language) in order to quickly create todo lists in DGML because I find that the DGML viewer control in VS is much better than TreeView. I hope Microsoft will make it as ubiquitous as the latter; it is already there; it is only a question of packaging.

Example (To Do items are tab-indented):

Grocery List
    Bread
    Beer
    Kids
        Milk
        Pampers
Projects
    To Do List in DGML
    MSBuild Generation
        T4 Templates
            Stand-Alone Host
        ‘.Targets’ Libraries
            File System
            ‘Group By’ tasks
    Documentation
        User’s Guides
        Design Guides

Which gives:

image

By default, I generate groups for task containment, but you can view any group as links:

image

Or all of them as links:

image

Maybe bottom-up is preferable:

image

Or left-to-right:

image

ToDo items don’t have to be unique:

Grocery List
    Bread
    Beer
    Kids
        Milk
        Pampers
Projects
    To Do List in DGML
    MSBuild Generation
        T4 Templates
            Stand-Alone Host
        ‘.Targets’ Libraries
            File System
            ‘Group By’ tasks
            Documentation
                User’s Guides
                Design Guides
    Documentation
        User’s Guides
        Design Guides

image

image

Here is a Visual Studio screenshot (click to enlarge):

VSScreenShot-01-Small

September 19, 2010

Hyperlinks in DGML

Filed under: DGML — Ceyhun Ciper @ 23:49

 

They are really very useful for keeping all your documents and artifacts accessible via a double-click inside Visual Studio. You don’t even have to use VS for development anymore; you can use it as a shell (I should say window) to whatever is important to you; you can even use DGML as an alternative to Windows Explorer.

Here is how I do it with my projects; the one below is the (internal) ReadMe document for a script library that generates DGML using AWK (self-documented, should I say?):

image

The first hyperlink takes me to the To Do list:

image

where I can see what to do first: Learning Architecture Explorer (it is not a trivial tool to get a grasp on).

The third one to the Specifications document:

image

From there I can go to the Design guide as well as to the scripts involved:

image

Here is some code from the dgml_print.gawk script:

function printlinks(links, r, parent, child, groupstr)
{
    for (r in links) {
        split(r, pc, SUBSEP)    # pc: parentchild
        parent = pc[1]
        child = pc[2]
        groupstr = ""
        if (parent in groups) groupstr = " Category=’Contains’ "
        printf("<Link Source=’%s’ Target=’%s’ %s/>n", parent, child, groupstr)
    }
}

I can also have a more detailed view of the functionality involved:

image

If we look at the “To Deploy…” box:

image

we have access to all the documents that we have to check before deployment; e.g. Dependencies:

image

with two boxes expanded:

image

So VS gives me instant access to all code albeit written in a non-VS language, as well as to all other artifacts.

September 10, 2010

Software Engineering with DGML

Filed under: DGML — Ceyhun Ciper @ 01:46

 

DGML brings a new vista to software engineering because it frees the developer from the constraints of tools such as Visio, Word, UML tools etc. Not only the constraints, but these tools are used for documentation purposes after the fact (which is development). Yes, even, and mostly, UML.

Why is DGML is so strong for quickly sketching requirements, specifications, low- and high-level design elements and even implementation details?

Answer: it is supported by a tool, namely Visual Studio.

Otherwise, other graph languages exist and the venerable GraphViz is better than DGML in its layout richness; but the new vista that DGML brings is interactivity and real-time scalability; you can have just one document where you accumulate 37 requirements, 263 specifications and 1,456 design ideas (all categorized and prioritized) and you can still travel in it with ease and find whatever you want. Try that with a 400-page Word document.

Why a graph language? Because people have been creating wonders with a language that supported lists as its sole data structure; a list is a directed graph; so the same can be done with a graph language.

August 22, 2010

Simplified DGML – Scenario: Software Deployment

Filed under: DGML — Ceyhun Ciper @ 02:21

 

Goal: To enhance the process that you currently use to automate the deployment of your software.

Current state: You have a linear model of tasks to run one after another.

Desired state: You’d like to extend the current state to allow the tasks to be represented by a directed graph of their dependencies.

DGML Shortcomings: A directed graph is tricky to represent in XML in a way that is easy to understand at first glance. Ideally, you want developers to be able to construct the dependency graph of their deployment tasks easily through some form of input. You could use Visio for this, but you’d rather it was XML based, as your current system uses XML, and you don’t really want to be reading from a Visio diagram programmatically. DGML could be the answer as it produces very nice looking DAGs from an XML file. However, you don’t really want the developers to be messing around with the DGML XML file, as it is pretty complex. There is currently no way to directly edit the DAG in the designer of Visual Studio (add node, add link between nodes), but you would like some way of editing the DGML.

Let’s consider this set of tasks to be written by your developers in the DGML DSL:

Prepare –> {Create Folders;Copy Images}
{ Create Folders; Copy Images } –> Build
Build –> { Check Resources; Copy Dependencies }
{ Check Resources; Copy Dependencies } –> Compile
Compile –> Link –> Deploy
Deploy –> {Deploy Folders; Deploy Dependencies; Deploy Executables; Patch Configs}

This generates the following DGML:

image

Generated DGML (that you don’t want your developers to write by hand):

<DirectedGraph xmlns=’http://schemas.microsoft.com/vs/2009/dgml’>
<Nodes>
<Node Id=’Patch Configs’ Label=’Patch Configs’ />
<Node Id=’Compile’ Label=’Compile’ />
<Node Id=’Create Folders’ Label=’Create Folders’ />
<Node Id=’Prepare’ Label=’Prepare’ />
<Node Id=’Deploy Executables’ Label=’Deploy Executables’ />
<Node Id=’Deploy’ Label=’Deploy’ />
<Node Id=’Link’ Label=’Link’ />
<Node Id=’Copy Images’ Label=’Copy Images’ />
<Node Id=’Deploy Folders’ Label=’Deploy Folders’ />
<Node Id=’Build’ Label=’Build’ />
<Node Id=’Check Resources’ Label=’Check Resources’ />
<Node Id=’Deploy Dependencies’ Label=’Deploy Dependencies’ />
<Node Id=’Copy Dependencies’ Label=’Copy Dependencies’ />
</Nodes>
<Links>
<Link Source=’Prepare’ Target=’Copy Images’ />
<Link Source=’Create Folders’ Target=’Build’ />
<Link Source=’Deploy’ Target=’Deploy Executables’ />
<Link Source=’Check Resources’ Target=’Compile’ />
<Link Source=’Prepare’ Target=’Create Folders’ />
<Link Source=’Deploy’ Target=’Deploy Folders’ />
<Link Source=’Link’ Target=’Deploy’ />
<Link Source=’Copy Dependencies’ Target=’Compile’ />
<Link Source=’Build’ Target=’Copy Dependencies’ />
<Link Source=’Deploy’ Target=’Deploy Dependencies’ />
<Link Source=’Compile’ Target=’Link’ />
<Link Source=’Copy Images’ Target=’Build’ />
<Link Source=’Deploy’ Target=’Patch Configs’ />
<Link Source=’Build’ Target=’Check Resources’ />
</Links>
</DirectedGraph>

You can also group tasks easily:

Prepare->{ Create Folders; Copy Images }
{Create Folders;Copy Images}->Build
Build->{Check Resources;Copy Dependencies}
{Check Resources;Copy Dependencies}->Compile
Compile->Link->Deploy
Deploy->{Deploy Folders; Deploy Dependencies; Deploy Executables; Patch Configs}
group Prepare
group Build
group Deploy

Which gives:

image

You can classify tasks by color:

Prepare->{ Create Folders; Copy Images }
{Create Folders;Copy Images}->Build
Build->{Check Resources;Copy Dependencies}
{Check Resources;Copy Dependencies}->Compile
Compile->Link->Deploy
Deploy->{Deploy Folders; Deploy Dependencies; Deploy Executables; Patch Configs}
group Prepare
group Build
group Deploy
category Copy Dependencies(Dependencies)
category Deploy Dependencies(Dependencies)
categorycolor Dependencies(Blue)

image

You can show task relations cross-cutting task groups:

Prepare->{ Create Folders; Copy Images }
{Create Folders;Copy Images}->Build
Build->{Check Resources;Copy Dependencies}
{Check Resources;Copy Dependencies}->Compile
Compile->Link->Deploy
Deploy->{Deploy Folders; Deploy Dependencies; Deploy Executables; Patch Configs}
group Prepare
group Build
group Deploy
category Copy Dependencies(Dependencies)
category Deploy Dependencies(Dependencies)
categorycolor Dependencies(Blue)
Copy Dependencies->Deploy Dependencies

image

August 21, 2010

DGML Simplified – Data Structures

Filed under: DGML — Ceyhun Ciper @ 23:32

 

Here are the data structures used in the DGML DSL (written in itself):

image

Here is a collapsed view:

image

Older Posts »

Create a free website or blog at WordPress.com.