Skip to content

Commit 4898143

Browse files
committed
SaveAsDgml now uses dgmlManager to generate graph with node positions from the vis.js network.
1 parent 1b3c5b5 commit 4898143

File tree

5 files changed

+90
-40
lines changed

5 files changed

+90
-40
lines changed

src/commands/generateDependencyInjectionGraph.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class GenerateDependencyInjectionGraph extends ShowHierarchyBase {
1616
this.saveAsPng('DependencyInjectionGraph.png', message.text);
1717
return;
1818
case 'saveAsDgml':
19-
this.saveAsDgml(this.config.dgmlGraphFilename, message.text);
19+
this.saveAsDgml(this.config.dgmlGraphFilename, message.text, "'The components hierarchy has been analyzed and a Directed Graph Markup Language (dgml) file has been created\nThe DependencyInjectionGraph.dgml file can now be viewed in Visual Studio'");
2020
return;
2121
}
2222
},

src/commands/showComponentHierarchy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class ShowComponentHierarchy extends ShowHierarchyBase {
1717
this.saveAsPng(this.config.moduleHierarchyFilename, message.text);
1818
return;
1919
case 'saveAsDgml':
20-
this.saveAsDgml(this.config.dgmlGraphFilename, message.text);
20+
this.saveAsDgml(this.config.dgmlGraphFilename, message.text, "'The component hierarchy has been analyzed and a Directed Graph Markup Language (dgml) file has been created\nThe ComponentHierarchy.dgml file can now be viewed in Visual Studio'");
2121
return;
2222
}
2323
},

src/commands/showHierarchyBase.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { CommandBase } from '@commands';
2-
import { Config, FileSystemUtils } from '@src';
3-
import { Edge, Node } from '@model';
2+
import { Config, DgmlManager, FileSystemUtils } from '@src';
3+
import { Category, Edge, Node } from '@model';
44
import { Base64 } from 'js-base64';
55
import * as fs from 'fs';
66
import * as path from 'path';
77
import * as vscode from 'vscode';
8+
import * as xmldom from 'xmldom';
89

10+
const prettifyXml = require('prettify-xml');
11+
const xmlSerializer = require('xmlserializer');
912

1013
export class ShowHierarchyBase extends CommandBase {
1114
protected fsUtils: FileSystemUtils = new FileSystemUtils();
@@ -60,8 +63,27 @@ export class ShowHierarchyBase extends CommandBase {
6063
}
6164
}
6265

63-
protected saveAsDgml(dgmlGraphFilename: string, messageText: string) {
66+
protected saveAsDgml(dgmlGraphFilename: string, messageText: string, popMessageText: string) {
67+
const message = JSON.parse(messageText);
68+
const direction = message.direction;
69+
const domImpl = new xmldom.DOMImplementation();
70+
const dgmlManager = new DgmlManager();
71+
const xmlDocument = dgmlManager.createNewDirectedGraph(domImpl, direction, "", "-1");
72+
dgmlManager.addNodesAndLinks(xmlDocument, this.nodes, message.nodes, this.edges);
73+
const categories: Category[] = [];
74+
dgmlManager.addCategories(xmlDocument, categories);
75+
dgmlManager.addProperties(xmlDocument);
76+
dgmlManager.addStyles(xmlDocument);
77+
// Serialize the xml into a string
78+
const xmlAsString = xmlSerializer.serializeToString(xmlDocument.documentElement);
79+
let fileContent = prettifyXml(xmlAsString);
80+
fileContent = fileContent.replace('HasCategory('RootComponent')', "HasCategory('RootComponent')");
6481

82+
// Write the prettified xml string to the ReadMe-ProjectStructure.dgml file.
83+
var directoryPath: string = this.fsUtils.getWorkspaceFolder();
84+
this.fsUtils.writeFile(path.join(directoryPath, dgmlGraphFilename), fileContent, () => {
85+
vscode.window.showInformationMessage(popMessageText);
86+
});
6587
}
6688

6789
protected generateHtmlContent(webview: vscode.Webview, outputJsFilename: string): string {

src/commands/showModuleHierarchy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class ShowModuleHierarchy extends ShowHierarchyBase {
1616
this.saveAsPng(this.config.moduleHierarchyFilename, message.text);
1717
return;
1818
case 'saveAsDgml':
19-
this.saveAsDgml(this.config.dgmlGraphFilename, message.text);
19+
this.saveAsDgml(this.config.dgmlGraphFilename, message.text, "'The modules hierarchy has been analyzed and a Directed Graph Markup Language (dgml) file has been created\nThe ModuleHierarchy.dgml file can now be viewed in Visual Studio'");
2020
return;
2121
}
2222
},

src/dgmlManager.ts

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Component } from "@src";
1+
import { BoundingBox, Category, Edge, NetworkNode, Node, Position } from "@model";
2+
3+
export class DgmlManager {
24

3-
export class DGMLManager {
4-
55
public createNewDirectedGraph(domImpl: DOMImplementation, direction: string, layout: string, zoomLevel: string) {
66
let xmlDoc: Document = domImpl.createDocument('', null, null);
77
const root = xmlDoc.createElement("DirectedGraph");
@@ -70,53 +70,81 @@ export class DGMLManager {
7070
}
7171
}
7272

73-
public addNodesAndLinks(xmlDoc: Document, componentHash: { [selector: string]: Component; }) {
73+
public addNodesAndLinks(xmlDoc: Document, nodes: Node[], nodeInfos: NetworkNode[], edges: Edge[]) {
74+
const nodeInfoDictionary = Object.assign({}, ...nodeInfos.map((nodeInfo) => ({ [nodeInfo.id]: nodeInfo })));
7475
const nodesElement = this.addNodeToRoot(xmlDoc, "Nodes");
7576
const linksElement = this.addNodeToRoot(xmlDoc, "Links");
76-
for (let selector in componentHash) {
77-
const component = componentHash[selector];
78-
if (component.isRoot) {
79-
this.generateDirectedGraphNodesXml(xmlDoc, component.subComponents, component, true, nodesElement);
80-
this.generateDirectedGraphLinksXml(xmlDoc, component.subComponents, selector, "", linksElement);
77+
nodes.forEach(node => {
78+
if (node.id in nodeInfoDictionary) {
79+
this.enrichNode(node, nodeInfoDictionary[node.id]);
8180
}
81+
this.generateDirectedGraphNodesXml(xmlDoc, node, nodesElement);
82+
});
83+
edges.forEach(edge => {
84+
this.generateDirectedGraphLinksXml(xmlDoc, edge, linksElement);
85+
});
86+
}
87+
88+
private enrichNode(node: Node, networkNode: NetworkNode) {
89+
if (networkNode.label) {
90+
node.name = networkNode.label;
91+
}
92+
if (networkNode.position) {
93+
node.position = networkNode.position;
94+
}
95+
if (networkNode.boundingBox) {
96+
node.boundingBox = networkNode.boundingBox;
8297
}
8398
}
8499

85-
private generateDirectedGraphNodesXml(xmlDoc: Document, components: Component[], component: Component, isRoot: boolean, nodesElement: Element | null) {
100+
private generateDirectedGraphNodesXml(xmlDoc: Document, node: Node, nodesElement: Element | null) {
86101
const nodeElement = xmlDoc.createElement("Node");
87-
nodeElement.setAttribute("ComponentFilename", component.tsFilename);
88-
nodeElement.setAttribute("Label", component.selector);
89-
nodeElement.setAttribute("Id", component.selector);
90-
if (isRoot) {
91-
nodeElement.setAttribute("Category", "RootComponent");
102+
nodeElement.setAttribute("Label", node.name);
103+
nodeElement.setAttribute("Id", node.id);
104+
if (node.boundingBox !== undefined && node.position !== undefined) {
105+
nodeElement.setAttribute("Bounds", this.calculateBounds(node.position, node.boundingBox));
106+
nodeElement.setAttribute("UseManualLocation", "True");
92107
}
93-
this.addNode(nodesElement, nodeElement);
94-
if (components.length > 0) {
95-
components.forEach((subComponent) => {
96-
this.generateDirectedGraphNodesXml(xmlDoc, subComponent.subComponents, subComponent, subComponent.isRoot, nodesElement);
108+
if (node.attributes && node.attributes.length > 0) {
109+
node.attributes.forEach(attribute => {
110+
nodeElement.setAttribute(attribute.name, attribute.value);
97111
});
98112
}
113+
this.addNode(nodesElement, nodeElement);
114+
}
115+
116+
private calculateBounds(position: Position, boundingBox: BoundingBox): string {
117+
const width = boundingBox.right - boundingBox.left;
118+
const height = boundingBox.bottom - boundingBox.top;
119+
return `${position.x},${position.y},${width},${height}`;
99120
}
100121

101-
private generateDirectedGraphLinksXml(xmlDoc: Document, subComponents: Component[], displayName: string, parentDisplayName: string, linksElement: Element | null) {
102-
if (parentDisplayName.length > 0) {
103-
this.addLinkNode(xmlDoc, linksElement, parentDisplayName, displayName);
104-
}
105-
if (subComponents.length > 0) {
106-
subComponents.forEach((subComponent) => {
107-
this.generateDirectedGraphLinksXml(xmlDoc, subComponent.subComponents, subComponent.selector, displayName, linksElement);
108-
});
122+
private generateDirectedGraphLinksXml(xmlDoc: Document, edge: Edge, linksElement: Element | null) {
123+
this.addLinkNode(xmlDoc, linksElement, edge.source, edge.target);
124+
}
125+
126+
private addCategory(xmlDoc: Document, categoriesElement: Element | null, category: Category) {
127+
if (categoriesElement !== null) {
128+
const categoryElement = xmlDoc.createElement("Category");
129+
categoryElement.setAttribute("Id", category.id);
130+
categoryElement.setAttribute("Label", category.label);
131+
categoryElement.setAttribute("Background", category.backgroundColor);
132+
categoryElement.setAttribute("IsTag", "True");
133+
this.addNode(categoriesElement, categoryElement);
109134
}
110135
}
111136

112-
public addCategory(xmlDoc: Document, id: string, label: string, backgroundColor: string) {
137+
public addCategories(xmlDoc: Document, categories: Category[]) {
113138
const categoriesElement = this.addNodeToRoot(xmlDoc, "Categories");
114-
const categoryElement = xmlDoc.createElement("Category");
115-
categoryElement.setAttribute("Id", id);
116-
categoryElement.setAttribute("Label", label);
117-
categoryElement.setAttribute("Background", backgroundColor);
118-
categoryElement.setAttribute("IsTag", "True");
119-
this.addNode(categoriesElement, categoryElement);
139+
// const categoryElement = xmlDoc.createElement("Category");
140+
// categoryElement.setAttribute("Id", "RootComponent");
141+
// categoryElement.setAttribute("Label", "Root component");
142+
// // categoryElement.setAttribute("Background", this.config.rootNodeBackgroundColor);
143+
// categoryElement.setAttribute("IsTag", "True");
144+
// this.addNode(categoriesElement, categoryElement);
145+
categories.forEach(category => {
146+
this.addCategory(xmlDoc, categoriesElement, category);
147+
});
120148
}
121149

122150
public addProperties(xmlDoc: Document) {

0 commit comments

Comments
 (0)