This commit is contained in:
2020-04-27 18:57:23 +02:00
parent 2e34757a1a
commit 73db0afc1f
5 changed files with 117 additions and 47 deletions

View File

@ -13,6 +13,9 @@ export class Element {
}
public constructor (name: string, parent = '') {
const regex = /^[a-z_][a-z_0-9]+$/iu;
if (!regex.test (name))
throw new Error ('invalid name specified');
this.name = name;
this.parent_name = parent;
}

View File

@ -1,9 +1,16 @@
import { Element } from './Element';
import { Edge } from './Edge';
import { Node } from './Node';
import { GraphStyles } from './Styles';
import { GraphStyles, NodeStyles } from './Styles';
import { Color } from './Color';
interface NodeOptions {
name: string;
label: string;
style: NodeStyles;
color: Color;
}
export class Graph extends Element {
public children: Array<Graph> = [];
public nodes: Array<Node> = [];
@ -13,7 +20,7 @@ export class Graph extends Element {
public color?: Color;
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (level = 0): string {
public toString (): string {
const header = this.parent
? `subgraph cluster_${this.full_name}`
: `digraph ${this.full_name}`;
@ -23,54 +30,61 @@ export class Graph extends Element {
if (this.style)
attributes.push ({ name: 'style', value: this.style.toString () });
let attrs = `\n ${attributes.map ((v) => `${v.name} = ${v.value}`)
.join ('\n ')}\n`;
let children = `\n ${this.children.map ((c) => c.toString (level + 1))
.join ('\n ')}\n`;
let nodes = `\n ${this.nodes.map ((c) => c.toString ())
.join ('\n ')}\n`;
let edges = `\n ${this.edges.map ((c) => c.toString ())
.join ('\n ')}\n`;
let attrs = `\n${attributes.map ((v) => `${v.name} = ${v.value}`)
.join ('\n')}\n`;
let children = `\n${this.children.map ((c) => c.toString ())
.join ('\n')}\n`;
let nodes = `\n${this.nodes.map ((c) => c.toString ())
.join ('\n')}\n`;
let edges = `\n${this.edges.map ((c) => c.toString ())
.join ('\n')}\n`;
if (attrs === '\n \n')
if (attrs === '\n\n')
attrs = '';
if (children === '\n \n')
if (children === '\n\n')
children = '';
if (nodes === '\n \n')
if (nodes === '\n\n')
nodes = '';
if (edges === '\n \n')
if (edges === '\n\n')
edges = '';
return `${header} {${attrs}${children}${nodes}${edges}}`
.replace (/\n/gu, `\n${' '.repeat (level)}`)
const indented = `${attrs}${children}${nodes}${edges}`
.replace (/\n$/u, '')
.replace (/^/gmu, ' ')
.split ('\n')
.map ((v) => v.trimRight ())
.join ('\n');
return `${header} {${indented}\n}`
.replace (/^\s+$/gmu, '');
}
public add_node (constructor: ((g: Node) => void) | string): string {
if (typeof constructor === 'string') {
this.nodes.push (new Node (constructor, this.full_name, constructor));
return this.nodes[this.nodes.length - 1].full_name;
}
public add_node (constructor: ((n: Node) => void) | string): string {
const node = new Node ('unnamed', this.full_name);
constructor (node);
if (typeof constructor === 'string') {
node.name = constructor;
node.label = constructor;
}
else { constructor (node); }
this.nodes.push (node);
return node.full_name;
}
public add_graph (constructor: ((g: Graph) => void) | string): string {
if (typeof constructor === 'string') {
this.children.push (new Graph (constructor, this.full_name));
return this.children[this.children.length - 1].full_name;
}
const graph = new Graph ('unnamed', this.full_name);
constructor (graph);
if (typeof constructor === 'string')
graph.name = constructor;
else
constructor (graph);
this.children.push (graph);
return graph.full_name;
}
public add_edge (origin: string, target: string): void {
this.edges.push (
new Edge (`${this.full_name}_${origin}`, `${this.full_name}_${target}`)
);
this.edges.push (new Edge (origin, target));
}
}

View File

@ -9,7 +9,7 @@ export class Node extends Element {
public style?: NodeStyles;
public color?: Color;
public constructor (name: string, parent?: string, label?: string) {
public constructor (name: string, parent: string, label?: string) {
super (name, parent);
this.label = label;
}
@ -28,19 +28,36 @@ export class Node extends Element {
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (): string {
const attributes = [];
if (this.label || this.is_table) {
attributes.push ({
name: 'label',
value: this.is_table
? this.serialized_table
: this.label
});
}
if (this.style)
attributes.push ({ name: 'style', value: this.style.toString () });
if (this.color)
attributes.push ({ name: 'color', value: this.color.toString () });
const attrs = attributes.map ((v) => `${v.name}="${v.value}"`)
.join (',');
const attrs = attributes.map ((v) => {
const d = (/\n/u).test (v.value as string)
? [
'<',
'>'
]
: [
'"',
'"'
];
return `${v.name}=${d[0]}${v.value}${d[1]}`;
})
.join (', ');
if (attributes.length > 0)
return `${this.full_name} [${attrs}]`;
if (this.is_table || typeof this.label !== 'undefined') {
return `${this.full_name} [label=<${this.is_table
? this.serialized_table
: this.label}>${attributes.length > 0 ? `,${attrs}` : ''}]`;
}
return `${this.full_name}`;
}
}