aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java
blob: f7f6b5ed2fb0b699d6ed3b8551326e5915ac6f1c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*******************************************************************************
 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-v20.html.
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
package tools.refinery.viatra.runtime.rete.itc.graphimpl;

import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC;
import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCCResult;
import tools.refinery.viatra.runtime.matchers.util.IMemoryView;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * This class contains utility methods to generate dot representations for {@link Graph} instances.
 *
 * @author Tamas Szabo
 * @since 2.3
 */
public class DotGenerator {

    private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" };

    private DotGenerator() {

    }

    /**
     * Generates the dot representation for the given graph.
     *
     * @param graph
     *            the graph
     * @param colorSCCs
     *            specifies if the strongly connected components with size greater than shall be colored
     * @param nameFunction
     *            use this function to provide custom names to nodes, null if the default toString shall be used
     * @param colorFunction
     *            use this function to provide custom color to nodes, null if the default white color shall be used
     * @param edgeFunction
     *            use this function to provide custom edge labels, null if no edge label shall be printed
     * @return the dot representation as a string
     */
    public static <V> String generateDot(final Graph<V> graph, final boolean colorSCCs,
            final Function<V, String> nameFunction, final Function<V, String> colorFunction,
            final Function<V, Function<V, String>> edgeFunction) {
        final Map<V, String> colorMap = new HashMap<V, String>();

        if (colorSCCs) {
            final SCCResult<V> result = SCC.computeSCC(graph);
            final Set<Set<V>> sccs = result.getSccs();

            int i = 0;
            for (final Set<V> scc : sccs) {
                if (scc.size() > 1) {
                    for (final V node : scc) {
                        final String color = colorMap.get(node);
                        if (color == null) {
                            colorMap.put(node, colors[i % colors.length]);
                        } else {
                            colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]);
                        }
                    }
                    i++;
                }
            }

            // if a node has no color yet, then make it white
            for (final V node : graph.getAllNodes()) {
                if (!colorMap.containsKey(node)) {
                    colorMap.put(node, "white");
                }
            }
        } else {
            for (final V node : graph.getAllNodes()) {
                colorMap.put(node, "white");
            }
        }

        if (colorFunction != null) {
            for (final V node : graph.getAllNodes()) {
                colorMap.put(node, colorFunction.apply(node));
            }
        }

        final StringBuilder builder = new StringBuilder();
        builder.append("digraph g {\n");

        for (final V node : graph.getAllNodes()) {
            final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node);
            builder.append("\"" + nodePresentation + "\"");
            builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]");
            builder.append(";\n");
        }

        for (final V source : graph.getAllNodes()) {
            final IMemoryView<V> targets = graph.getTargetNodes(source);
            if (!targets.isEmpty()) {
                final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source);
                for (final V target : targets.distinctValues()) {
                    String edgeLabel = null;
                    if (edgeFunction != null) {
                        final Function<V, String> v1 = edgeFunction.apply(source);
                        if (v1 != null) {
                            edgeLabel = v1.apply(target);
                        }
                    }

                    final String targetPresentation = nameFunction == null ? target.toString()
                            : nameFunction.apply(target);

                    builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\"");
                    if (edgeLabel != null) {
                        builder.append("[label=\"" + edgeLabel + "\"]");
                    }
                    builder.append(";\n");
                }
            }
        }

        builder.append("}");
        return builder.toString();
    }

    /**
     * Generates the dot representation for the given graph. No special pretty printing customization will be applied.
     *
     * @param graph
     *            the graph
     * @return the dot representation as a string
     */
    public static <V> String generateDot(final Graph<V> graph) {
        return generateDot(graph, false, null, null, null);
    }

    /**
     * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability.
     * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because
     * grahpviz will treat different nodes as the same if their shortened names are the same.
     *
     * @param maxLength
     *            the maximum length of the text that is kept from the toString of the objects in the graph
     * @return the shrunk toString value
     */
    public static <V> Function<V, String> getNameShortener(final int maxLength) {
        return new Function<V, String>() {
            @Override
            public String apply(final V obj) {
                final String value = obj.toString();
                return value.substring(0, Math.min(value.length(), maxLength));
            }
        };
    }

}