Newer
Older
/*
* EDG, a library to generate and slice Expression Dependence Graphs.
* Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package edg;
import java.io.File;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import edg.constraint.EdgeConstraint;
import edg.graph.EDG;
import edg.graph.Edge;
import edg.graph.Node;
import org.jgrapht.graph.AsSubgraph;
import org.jgrapht.io.Attribute;
import org.jgrapht.io.DOTExporter;
import org.jgrapht.io.DefaultAttribute;
import org.jgrapht.io.ExportException;
public class DotFactory {
// ============================= DEBUG CONFIGURATION ============================= //
// This configuration will get applied to filter out some edges from the dot
// representation, to ease the debugging of graphs.
// Edge types that will be ignored (none if empty)
static final List<Edge.Type> ignoreEdgeTypes = new LinkedList<>();
// Edge types that will be included (all if empty)
static final List<Edge.Type> edgeTypes = Arrays.asList();
// static final List<Edge.Type> edgeTypes = Arrays.asList(Edge.Type.ControlFlow);
// Lower and upper bound for node inclusion (both ends of an edge must be included for
// the edge to be included)
static final int lowerBound = Integer.MIN_VALUE;
static final int upperBound = Integer.MAX_VALUE;
// Specific nodes, for which all edges must be included (all if empty)
static final List<Integer> nodeIds = Arrays.asList();
static final List<Integer> ignoredNodeIds = Arrays.asList();
// =========================== END DEBUG CONFIGURATION =========================== //
// Reads the commandline options (-Dedgecontrol=false removes control edges)
static {
Map<String, List<Edge.Type>> edgeNameMap = new HashMap<>();
edgeNameMap.put("structural", List.of(Edge.Type.Structural));
edgeNameMap.put("controlflow", List.of(Edge.Type.ControlFlow, Edge.Type.NonExecControlFlow));
edgeNameMap.put("control", List.of(Edge.Type.Control));
edgeNameMap.put("flow", List.of(Edge.Type.Flow));
edgeNameMap.put("objectflow", List.of(Edge.Type.ObjectFlow));
edgeNameMap.put("value", List.of(Edge.Type.Value));
edgeNameMap.put("totaldefinition", List.of(Edge.Type.TotalDefinition));
edgeNameMap.put("call", List.of(Edge.Type.Call));
edgeNameMap.put("input", List.of(Edge.Type.Input));
edgeNameMap.put("output", List.of(Edge.Type.Output));
edgeNameMap.put("summary", List.of(Edge.Type.Summary));
for (String key : edgeNameMap.keySet())
if (System.getProperty("edge" + key, "true").equalsIgnoreCase("false"))
ignoreEdgeTypes.addAll(edgeNameMap.get(key));
}
public static void createDot(File outputFile, EDG edg)
{
DotFactory.createDot(outputFile, edg, null, null, null);
}
public static void createDot(File outputFile, EDG edg, Map<Edge.Type, Boolean> edgeFlags)
{
DotFactory.createDot(outputFile, edg, null, null, edgeFlags);
}
public static void createDot(File outputFile, EDG edg, Node slicingCriterion, Set<Node> slice)
{
DotFactory.createDot(outputFile, edg, slicingCriterion, slice, null);
}
public static void createDot(File outputFile, EDG edg, Node slicingCriterion, Set<Node> slice, Map<Edge.Type, Boolean> edgeFlags)
{
SlicedGraph slicedGraph = new SlicedGraph(slicingCriterion, slice);
slicedGraph.setEdgeFilter(edge -> {
Edge.Type edgeType = edge.getType();
int idFrom = edg.getEdgeSource(edge).getId();
int idTo = edg.getEdgeTarget(edge).getId();
// Structural edges are always kept!
if (edgeType == Edge.Type.Structural)
return true;
// SHOW ONLY VALUE EDGES WITH ACCESS CONSTRAINTS
//if (edgeType == Edge.Type.Value)
// return edge.getConstraint() instanceof AccessConstraint;
return (edgeFlags == null || edgeFlags.get(edgeType)) &&
!ignoreEdgeTypes.contains(edgeType) &&
(edgeTypes.isEmpty() || edgeTypes.contains(edgeType)) &&
(idFrom >= lowerBound && idTo >= lowerBound && idFrom <= upperBound && idTo <= upperBound) &&
(nodeIds.isEmpty() || nodeIds.contains(idFrom) || nodeIds.contains(idTo))
&& ((!ignoredNodeIds.contains(idFrom) && !ignoredNodeIds.contains(idTo))
&& !(edgeType == Edge.Type.Control && (edg.getNode(idFrom).getType() == Node.Type.Clause || edg.getNode(idFrom).getType() == Node.Type.Parameters))); //IGNORE CLAUSE CONTROL EDGES TO ALL ELEMENTS
AsSubgraph<Node, Edge> subGraph = new AsSubgraph<>(edg, edg.vertexSet(),
edg.edgeSet().stream().filter(slicedGraph.edgeFilter).collect(Collectors.toSet()));
DOTExporter<Node, Edge> exporter = new DOTExporter<>(
n -> String.valueOf(n.getId()), // Node --> id
slicedGraph::getNodeLabel,
slicedGraph::getEdgeLabel,
slicedGraph::getNodeAttributes,
slicedGraph::getEdgeAttributes);
try
{
exporter.exportGraph(subGraph, outputFile);
}
catch (ExportException e)
{
System.err.println("Error generating dot from EDG and writing it to " + outputFile.getPath());
e.printStackTrace();
}
}
// ======================== DOT ATTRIBUTES ======================== //
// Colors
private static final Attribute BLACK = DefaultAttribute.createAttribute("black");
private static final Attribute BLUE = DefaultAttribute.createAttribute("blue");
private static final Attribute GREEN = DefaultAttribute.createAttribute("green");
private static final Attribute GREEN4 = DefaultAttribute.createAttribute("green4");
private static final Attribute GREEN3 = DefaultAttribute.createAttribute("green3");
private static final Attribute GRAY = DefaultAttribute.createAttribute("gray");
private static final Attribute RED = DefaultAttribute.createAttribute("red");
private static final Attribute ORANGE = DefaultAttribute.createAttribute("orange");
private static final Attribute PINK = DefaultAttribute.createAttribute("pink");
private static final Attribute BROWN = DefaultAttribute.createAttribute("brown");
private static final Attribute TURQUOISE = DefaultAttribute.createAttribute("turquoise");
private static final Attribute DEEPPINK = DefaultAttribute.createAttribute("deeppink");
private static final Attribute SKYBLUE = DefaultAttribute.createAttribute("skyblue");
private static final Attribute STEELBLUE = DefaultAttribute.createAttribute("steelblue1");
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Numbers
private static final Attribute ONE = DefaultAttribute.createAttribute(1);
private static final Attribute TWO = DefaultAttribute.createAttribute(2);
private static final Attribute THREE = DefaultAttribute.createAttribute(3);
private static final Attribute FOUR = DefaultAttribute.createAttribute(4);
// Booleans
private static final Attribute FALSE = DefaultAttribute.createAttribute(false);
// Shapes
private static final Attribute ELLIPSE = DefaultAttribute.createAttribute("ellipse");
// Styles
private static final Attribute FILLED = DefaultAttribute.createAttribute("filled");
private static final Attribute DASHED = DefaultAttribute.createAttribute("dashed");
private static final Attribute DOTTED = DefaultAttribute.createAttribute("dotted");
private static final Attribute INVISIBLE = DefaultAttribute.createAttribute("invis");
private static class SlicedGraph {
private final Node slicingCriterion;
private final Set<Node> slice;
private Predicate<Edge> edgeFilter = null;
public SlicedGraph(Node slicingCriterion, Set<Node> slice)
{
this.slicingCriterion = slicingCriterion;
this.slice = slice;
}
public void setEdgeFilter(Predicate<Edge> edgeFilter)
{
this.edgeFilter = edgeFilter;
}
//private String getNodeLabel(Node node) { return String.format("Id = %d\n%s", node.getId(), node.getLabel()); }
private String getNodeLabel(Node node)
{
return String.format("Id = %d\nSDGId = %d\n%s", node.getId(), node.getSDGId(), node.getLabel());
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
}
private String getEdgeLabel(Edge edge)
{
final Edge.Type edgeType = edge.getType();
final EdgeConstraint constraint = edge.getConstraint();
if (constraint != null && edgeType != Edge.Type.Structural && edgeType != Edge.Type.Control)
return constraint.toString();
return null;
}
private Map<String, Attribute> getNodeAttributes(Node node)
{
boolean inSlice = slice != null && slice.contains(node);
Map<String, Attribute> attrs = new HashMap<>();
attrs.put("shape", ELLIPSE);
attrs.put("style", FILLED);
attrs.put("color", node == slicingCriterion ? BLUE : GRAY);
attrs.put("penwidth", node == slicingCriterion ? FOUR : ONE);
attrs.put("fontcolor", inSlice ? BLUE : BLACK);
attrs.put("fillcolor", inSlice ? GREEN : GRAY);
return attrs;
}
private Map<String, Attribute> getEdgeAttributes(Edge edge)
{
final Edge.Type edgeType = edge.getType();
Map<String, Attribute> attrs = new HashMap<>();
switch (edgeType)
{
case Structural:
attrs.put("color", edge.isMarked() ? GREEN : BLACK);
attrs.put("penwidth", THREE);
break;
case ControlFlow:
attrs.put("color", RED);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
break;
case NonExecControlFlow:
attrs.put("color", RED);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
attrs.put("style", DASHED);
break;
case Control:
attrs.put("color", ORANGE);
attrs.put("constraint", FALSE);
attrs.put("penwidth", THREE);
break;
case Value:
attrs.put("color", RED);
attrs.put("constraint", FALSE);
attrs.put("style", DOTTED);
break;
case Flow:
attrs.put("color", BLUE);
attrs.put("constraint", FALSE);
attrs.put("style", DOTTED);
break;
case ObjectFlow:
attrs.put("color", GREEN4);
attrs.put("penwidth", TWO);
attrs.put("constraint", FALSE);
attrs.put("style", DASHED);
break;
case Call:
case Input:
attrs.put("color", GREEN3);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
attrs.put("style", DASHED);
break;
case CallReq:
attrs.put("color", SKYBLUE);
attrs.put("penwidth", TWO);
attrs.put("constraint", FALSE);
attrs.put("style", DASHED);
break;
case Output:
attrs.put("color", PINK);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
attrs.put("style", DASHED);
break;
case Summary:
attrs.put("color", BROWN);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
break;
case Exception:
attrs.put("color", ORANGE);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
break;
case TotalDefinition:
attrs.put("color", TURQUOISE);
attrs.put("penwidth", THREE);
attrs.put("constraint", FALSE);
break;
case Class:
attrs.put("color", DEEPPINK);
attrs.put("penwidth", TWO);
attrs.put("constraint", FALSE);
attrs.put("style", DASHED);
break;
default:
throw new RuntimeException("Edge type not contemplated: " + edgeType);
}
return attrs;
}
}