/* * 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; } } }