All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.io.DOTExporter Maven / Gradle / Ivy

There is a newer version: 1.5.2
Show newest version
/*
 * (C) Copyright 2006-2017, by Trevor Harmon and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * This program and the accompanying materials are dual-licensed under
 * either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation, or (at your option) any
 * later version.
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation.
 */
package org.jgrapht.io;

import org.jgrapht.*;

import java.io.*;
import java.util.*;
import java.util.Map.*;
import java.util.regex.*;

/**
 * Exports a graph into a DOT file.
 *
 * 

* For a description of the format see * http://en.wikipedia.org/wiki/DOT_language. *

* * The user can adjust the behavior using {@link ComponentNameProvider} and * {@link ComponentAttributeProvider} instances given through the constructor. * * @param the graph vertex type * @param the graph edge type * * @author Trevor Harmon */ public class DOTExporter extends AbstractBaseExporter implements GraphExporter { /** * Default graph id used by the exporter. */ public static final String DEFAULT_GRAPH_ID = "G"; private final ComponentNameProvider> graphIDProvider; private final ComponentNameProvider vertexLabelProvider; private final ComponentNameProvider edgeLabelProvider; private final ComponentAttributeProvider vertexAttributeProvider; private final ComponentAttributeProvider edgeAttributeProvider; private final Map graphAttributes; private final Map vertexIds; private static final String INDENT = " "; /** * Constructs a new DOTExporter object with an integer name provider for the vertex IDs and null * providers for the vertex and edge labels. */ public DOTExporter() { this(new IntegerComponentNameProvider<>(), null, null, null, null, null); } /** * Constructs a new DOTExporter object with the given ID and label providers. * * @param vertexIDProvider for generating vertex IDs. Must not be null. * @param vertexLabelProvider for generating vertex labels. If null, vertex labels will not be * written to the file. * @param edgeLabelProvider for generating edge labels. If null, edge labels will not be written * to the file. */ public DOTExporter( ComponentNameProvider vertexIDProvider, ComponentNameProvider vertexLabelProvider, ComponentNameProvider edgeLabelProvider) { this(vertexIDProvider, vertexLabelProvider, edgeLabelProvider, null, null, null); } /** * Constructs a new DOTExporter object with the given ID, label, and attribute providers. Note * that if a label provider conflicts with a label-supplying attribute provider, the label * provider is given precedence. * * @param vertexIDProvider for generating vertex IDs. Must not be null. * @param vertexLabelProvider for generating vertex labels. If null, vertex labels will not be * written to the file (unless an attribute provider is supplied which also supplies * labels). * @param edgeLabelProvider for generating edge labels. If null, edge labels will not be written * to the file. * @param vertexAttributeProvider for generating vertex attributes. If null, vertex attributes * will not be written to the file. * @param edgeAttributeProvider for generating edge attributes. If null, edge attributes will * not be written to the file. */ public DOTExporter( ComponentNameProvider vertexIDProvider, ComponentNameProvider vertexLabelProvider, ComponentNameProvider edgeLabelProvider, ComponentAttributeProvider vertexAttributeProvider, ComponentAttributeProvider edgeAttributeProvider) { this( vertexIDProvider, vertexLabelProvider, edgeLabelProvider, vertexAttributeProvider, edgeAttributeProvider, null); } /** * Constructs a new DOTExporter object with the given ID, label, attribute, and graph id * providers. Note that if a label provider conflicts with a label-supplying attribute provider, * the label provider is given precedence. * * @param vertexIDProvider for generating vertex IDs. Must not be null. * @param vertexLabelProvider for generating vertex labels. If null, vertex labels will not be * written to the file (unless an attribute provider is supplied which also supplies * labels). * @param edgeLabelProvider for generating edge labels. If null, edge labels will not be written * to the file. * @param vertexAttributeProvider for generating vertex attributes. If null, vertex attributes * will not be written to the file. * @param edgeAttributeProvider for generating edge attributes. If null, edge attributes will * not be written to the file. * @param graphIDProvider for generating the graph ID. If null the graph is named * {@link #DEFAULT_GRAPH_ID}. */ public DOTExporter( ComponentNameProvider vertexIDProvider, ComponentNameProvider vertexLabelProvider, ComponentNameProvider edgeLabelProvider, ComponentAttributeProvider vertexAttributeProvider, ComponentAttributeProvider edgeAttributeProvider, ComponentNameProvider> graphIDProvider) { super(vertexIDProvider); this.vertexLabelProvider = vertexLabelProvider; this.edgeLabelProvider = edgeLabelProvider; this.vertexAttributeProvider = vertexAttributeProvider; this.edgeAttributeProvider = edgeAttributeProvider; this.graphIDProvider = (graphIDProvider == null) ? any -> DEFAULT_GRAPH_ID : graphIDProvider; this.graphAttributes = new LinkedHashMap<>(); this.vertexIds = new HashMap<>(); } /** * Exports a graph into a plain text file in DOT format. * * @param g the graph to be exported * @param writer the writer to which the graph to be exported */ @Override public void exportGraph(Graph g, Writer writer) { PrintWriter out = new PrintWriter(writer); out.println(computeHeader(g)); // graph attributes for (Entry attr : graphAttributes.entrySet()) { out.print(INDENT); out.print(attr.getKey()); out.print('='); out.print(attr.getValue()); out.println(";"); } // vertex set for (V v : g.vertexSet()) { out.print(INDENT); out.print(getVertexID(v)); String labelName = null; if (vertexLabelProvider != null) { labelName = vertexLabelProvider.getName(v); } Map attributes = null; if (vertexAttributeProvider != null) { attributes = vertexAttributeProvider.getComponentAttributes(v); } renderAttributes(out, labelName, attributes); out.println(";"); } String connector = computeConnector(g); // edge set for (E e : g.edgeSet()) { String source = getVertexID(g.getEdgeSource(e)); String target = getVertexID(g.getEdgeTarget(e)); out.print(INDENT); out.print(source); out.print(connector); out.print(target); String labelName = null; if (edgeLabelProvider != null) { labelName = edgeLabelProvider.getName(e); } Map attributes = null; if (edgeAttributeProvider != null) { attributes = edgeAttributeProvider.getComponentAttributes(e); } renderAttributes(out, labelName, attributes); out.println(";"); } out.println(computeFooter(g)); out.flush(); } /** * Compute the header * * @param graph the graph * @return the header */ private String computeHeader(Graph graph) { StringBuilder headerBuilder = new StringBuilder(); if (!graph.getType().isAllowingMultipleEdges()) { headerBuilder.append(DOTUtils.DONT_ALLOW_MULTIPLE_EDGES_KEYWORD).append(" "); } if (graph.getType().isDirected()) { headerBuilder.append(DOTUtils.DIRECTED_GRAPH_KEYWORD); } else { headerBuilder.append(DOTUtils.UNDIRECTED_GRAPH_KEYWORD); } headerBuilder.append(" ").append(computeGraphId(graph)).append(" {"); return headerBuilder.toString(); } /** * Clear a graph attribute. * * @param key the graph attribute key */ public void removeGraphAttribute(String key) { Objects.requireNonNull(key, "Graph attribute key cannot be null"); graphAttributes.remove(key); } /** * Set a graph attribute. * * @param key the graph attribute key * @param value the graph attribute value */ public void putGraphAttribute(String key, String value) { Objects.requireNonNull(key, "Graph attribute key cannot be null"); Objects.requireNonNull(value, "Graph attribute value cannot be null"); graphAttributes.put(key, value); } /** * Compute the footer * * @param graph the graph * @return the footer */ private String computeFooter(Graph graph) { return "}"; } /** * Compute the connector * * @param graph the graph * @return the connector */ private String computeConnector(Graph graph) { StringBuilder connectorBuilder = new StringBuilder(); if (graph.getType().isDirected()) { connectorBuilder.append(" ").append(DOTUtils.DIRECTED_GRAPH_EDGEOP).append(" "); } else { connectorBuilder.append(" ").append(DOTUtils.UNDIRECTED_GRAPH_EDGEOP).append(" "); } return connectorBuilder.toString(); } /** * Get the id of the graph. * * @param graph the graph * @return the graph id */ private String computeGraphId(Graph graph) { String graphId = graphIDProvider.getName(graph); if (graphId == null || graphId.trim().isEmpty()) { graphId = DEFAULT_GRAPH_ID; } if (!DOTUtils.isValidID(graphId)) { throw new RuntimeException( "Generated graph ID '" + graphId + "' is not valid with respect to the .dot language"); } return graphId; } private void renderAttributes( PrintWriter out, String labelName, Map attributes) { if (labelName == null && attributes == null) { return; } out.print(" [ "); if (labelName == null) { Attribute labelAttribute = attributes.get("label"); if (labelAttribute != null) { labelName = labelAttribute.getValue(); } } if (labelName != null) { out.print("label=\"" + escapeDoubleQuotes(labelName) + "\" "); } if (attributes != null) { for (Map.Entry entry : attributes.entrySet()) { String name = entry.getKey(); if (name.equals("label")) { // already handled by special case above continue; } out.print(name + "=\"" + escapeDoubleQuotes(entry.getValue().getValue()) + "\" "); } } out.print("]"); } private static String escapeDoubleQuotes(String labelName) { return labelName.replaceAll("\"", Matcher.quoteReplacement("\\\"")); } /** * Return a valid vertex ID (with respect to the .dot language definition as described in * http://www.graphviz.org/doc/info/lang.html Quoted from above mentioned source: An ID is valid * if it meets one of the following criteria: * *
    *
  • any string of alphabetic characters, underscores or digits, not beginning with a digit; *
  • a number [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? ); *
  • any double-quoted string ("...") possibly containing escaped quotes (\"); *
  • an HTML string (<...>). *
* * @throws RuntimeException if the given vertexIDProvider didn't generate a valid * vertex ID. */ private String getVertexID(V v) { String vertexId = vertexIds.get(v); if (vertexId == null) { /* * use the associated id provider for an ID of the given vertex */ String idCandidate = vertexIDProvider.getName(v); /* * test if it is a valid ID */ if (!DOTUtils.isValidID(idCandidate)) { throw new RuntimeException( "Generated id '" + idCandidate + "'for vertex '" + v + "' is not valid with respect to the .dot language"); } vertexIds.put(v, idCandidate); vertexId = idCandidate; } return vertexId; } } // End DOTExporter.java




© 2015 - 2024 Weber Informatics LLC | Privacy Policy