/*
* 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 .
*/
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 ignoreEdgeTypes = new LinkedList<>();
// Edge types that will be included (all if empty)
static final List edgeTypes = Arrays.asList();
// static final List 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 nodeIds = Arrays.asList();
static final List ignoredNodeIds = Arrays.asList();
// =========================== END DEBUG CONFIGURATION =========================== //
// Reads the commandline options (-Dedgecontrol=false removes control edges)
static {
Map> 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 edgeFlags)
{
DotFactory.createDot(outputFile, edg, null, null, edgeFlags);
}
public static void createDot(File outputFile, EDG edg, Node slicingCriterion, Set slice)
{
DotFactory.createDot(outputFile, edg, slicingCriterion, slice, null);
}
public static void createDot(File outputFile, EDG edg, Node slicingCriterion, Set slice, Map 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 subGraph = new AsSubgraph<>(edg, edg.vertexSet(),
edg.edgeSet().stream().filter(slicedGraph.edgeFilter).collect(Collectors.toSet()));
DOTExporter 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");
// 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 slice;
private Predicate edgeFilter = null;
public SlicedGraph(Node slicingCriterion, Set slice)
{
this.slicingCriterion = slicingCriterion;
this.slice = slice;
}
public void setEdgeFilter(Predicate 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());
}
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 getNodeAttributes(Node node)
{
boolean inSlice = slice != null && slice.contains(node);
Map 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 getEdgeAttributes(Edge edge)
{
final Edge.Type edgeType = edge.getType();
Map 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;
}
}
}