• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { BaseEdge, BaseNode, NodeID } from '../core/graph/BaseExplicitGraph';
17import { GraphTraits } from '../core/graph/GraphTraits';
18import { Printer } from './Printer';
19
20function escapeStr(input: string): string {
21    let str = input;
22    for (let i = 0; i < str.length; ++i) {
23        switch (str[i]) {
24            case '\n':
25                str = str.substring(0, i) + '\\n' + str.substring(i + 1);
26                ++i;
27                break;
28            case '\t':
29                str = str.substring(0, i) + '  ' + str.substring(i + 1);
30                ++i;
31                break;
32            case '\\':
33                if (i + 1 < str.length) {
34                    switch (str[i + 1]) {
35                        case 'l':
36                            continue; // don't disturb \l
37                        case '|':
38                        case '{':
39                        case '}':
40                            str = str.substring(0, i) + str.substring(i + 1);
41                            continue;
42                        default:
43                            break;
44                    }
45                }
46                str = str.substring(0, i) + '\\\\' + str.substring(i + 1);
47                ++i;
48                break;
49            case '{':
50            case '}':
51            case '<':
52            case '>':
53            case '|':
54            case '"':
55                str = str.substring(0, i) + '\\' + str[i] + str.substring(i + 1);
56                ++i;
57                break;
58            default:
59        }
60    }
61    return str;
62}
63
64export class GraphPrinter<GraphType extends GraphTraits<BaseNode>> extends Printer {
65    graph: GraphType;
66    title!: string;
67    startID: NodeID | undefined = undefined;
68
69    constructor(g: GraphType, t?: string) {
70        super();
71        this.graph = g;
72        if (t) {
73            this.title = t;
74        }
75    }
76
77    public setStartID(n: NodeID): void {
78        this.startID = n;
79    }
80
81    public dump(): string {
82        this.printer.clear();
83        this.writeGraph();
84        return this.printer.toString();
85    }
86
87    public writeGraph(): void {
88        this.writeHeader();
89        this.writeNodes();
90        this.writeFooter();
91    }
92
93    public writeNodes(): void {
94        let itor: IterableIterator<BaseNode> = this.graph.nodesItor();
95        if (this.startID) {
96            // from start id
97            let nodes = new Set<BaseNode>();
98            let startNode = this.graph.getNode(this.startID)!;
99            let worklist = [startNode];
100            while (worklist.length > 0) {
101                let n = worklist.shift()!;
102                if (nodes.has(n)) {
103                    continue;
104                }
105                nodes.add(n);
106                n.getOutgoingEdges()?.forEach(e => worklist.push(e.getDstNode()));
107            }
108            itor = nodes.values();
109        }
110
111        for (let node of itor) {
112            let nodeAttr = node.getDotAttr();
113            if (nodeAttr === '') {
114                continue;
115            }
116            let nodeLabel = escapeStr(node.getDotLabel());
117
118            this.printer.writeLine(`\tNode${node.getID()} [shape=recode,${nodeAttr},label="${nodeLabel}"];`);
119
120            for (let edge of node.getOutgoingEdges()) {
121                this.writeEdge(edge);
122            }
123        }
124    }
125
126    public writeEdge(edge: BaseEdge): void {
127        let edgeAttr = edge.getDotAttr();
128        if (edgeAttr === '') {
129            return;
130        }
131        this.printer.writeLine(`\tNode${edge.getSrcID()} -> Node${edge.getDstID()}[${edgeAttr}]`);
132    }
133
134    public writeHeader(): void {
135        const GraphName = this.graph.getGraphName();
136
137        let graphNameStr = `digraph "${escapeStr(this.title || GraphName || 'unnamed')}" {\n`;
138        this.printer.writeLine(graphNameStr);
139
140        let labelStr = `\tlabel="${escapeStr(this.title || GraphName)}";\n`;
141        this.printer.writeLine(labelStr);
142
143        // TODO: need graph attr?
144    }
145
146    public writeFooter(): void {
147        this.printer.writeLine('}\n');
148    }
149}
150