org.jgrapht.nio.graphml.GraphMLExporter Maven / Gradle / Ivy
The newest version!
/*
* (C) Copyright 2006-2023, by Trevor Harmon and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.nio.graphml;
import org.jgrapht.*;
import org.jgrapht.nio.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.stream.*;
import java.io.*;
import java.util.*;
import java.util.Map.*;
import java.util.function.*;
/**
* Exports a graph as GraphML.
*
*
* For a description of the format see
* http://en.wikipedia.org/wiki/ GraphML.
*
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Trevor Harmon
* @author Dimitrios Michail
*/
public class GraphMLExporter
extends BaseExporter
implements GraphExporter
{
// registered attributes
private Map registeredAttributes = new LinkedHashMap<>();
private static final String ATTRIBUTE_KEY_PREFIX = "key";
private int totalAttributes = 0;
// special attributes
private static final String VERTEX_LABEL_DEFAULT_ATTRIBUTE_NAME = "VertexLabel";
private static final String EDGE_WEIGHT_DEFAULT_ATTRIBUTE_NAME = "weight";
private static final String EDGE_LABEL_DEFAULT_ATTRIBUTE_NAME = "EdgeLabel";
private String vertexLabelAttributeName = VERTEX_LABEL_DEFAULT_ATTRIBUTE_NAME;
private String edgeWeightAttributeName = EDGE_WEIGHT_DEFAULT_ATTRIBUTE_NAME;
private String edgeLabelAttributeName = EDGE_LABEL_DEFAULT_ATTRIBUTE_NAME;
/**
* Whether to print edge weights in case the graph is weighted.
*/
private boolean exportEdgeWeights = false;
/**
* Whether to try to print vertex labels. They must be found in the corresponding attribute
* provider.
*/
private boolean exportVertexLabels = false;
/**
* Whether to try to print edge labels. They must be found in the corresponding attribute
* provider.
*/
private boolean exportEdgeLabels = false;
/**
* Constructs a new GraphMLExporter with integer id provider for the vertices.
*/
public GraphMLExporter()
{
this(new IntegerIdProvider<>());
}
/**
* Constructs a new GraphMLExporter.
*
* @param vertexIdProvider for generating vertex identifiers. Must not be null.
*/
public GraphMLExporter(Function vertexIdProvider)
{
super(vertexIdProvider);
}
/**
* Denotes the category of a GraphML-Attribute.
*/
public enum AttributeCategory
{
GRAPH("graph"),
NODE("node"),
EDGE("edge"),
ALL("all");
private String name;
private AttributeCategory(String name)
{
this.name = name;
}
/**
* Get a string representation of the attribute category
*
* @return the string representation of the attribute category
*/
public String toString()
{
return name;
}
}
/**
* Register a GraphML-Attribute
*
* @param name the attribute name
* @param category the attribute category
* @param type the attribute type
*/
public void registerAttribute(String name, AttributeCategory category, AttributeType type)
{
registerAttribute(name, category, type, null);
}
/**
* Register a GraphML-Attribute
*
* @param name the attribute name
* @param category the attribute category
* @param type the attribute type
* @param defaultValue default value
*/
public void registerAttribute(
String name, AttributeCategory category, AttributeType type, String defaultValue)
{
if (name == null) {
throw new IllegalArgumentException("Attribute name cannot be null");
}
if (name.equals(vertexLabelAttributeName) || name.equals(edgeWeightAttributeName)
|| name.equals(edgeLabelAttributeName))
{
throw new IllegalArgumentException("Reserved attribute name");
}
if (category == null) {
throw new IllegalArgumentException(
"Attribute category must be one of node, edge, graph or all");
}
String nextKey = ATTRIBUTE_KEY_PREFIX + (totalAttributes++);
registeredAttributes.put(name, new AttributeDetails(nextKey, category, type, defaultValue));
}
/**
* Unregister a GraphML-Attribute
*
* @param name the attribute name
*/
public void unregisterAttribute(String name)
{
if (name == null) {
throw new IllegalArgumentException("Attribute name cannot be null");
}
if (name.equals(vertexLabelAttributeName) || name.equals(edgeWeightAttributeName)
|| name.equals(edgeLabelAttributeName))
{
throw new IllegalArgumentException("Reserved attribute name");
}
registeredAttributes.remove(name);
}
/**
* Whether the exporter will print edge weights.
*
* @return {@code true} if the exporter prints edge weights, {@code false} otherwise
*/
public boolean isExportEdgeWeights()
{
return exportEdgeWeights;
}
/**
* Set whether the exporter will print edge weights.
*
* @param exportEdgeWeights value to set
*/
public void setExportEdgeWeights(boolean exportEdgeWeights)
{
this.exportEdgeWeights = exportEdgeWeights;
}
/**
* Whether the exporter will print vertex labels.
*
* @return {@code true} if the exporter prints vertex labels, {@code false} otherwise
*/
public boolean isExportVertexLabels()
{
return exportVertexLabels;
}
/**
* Set whether the exporter will print vertex labels.
*
* @param exportVertexLabels value to set
*/
public void setExportVertexLabels(boolean exportVertexLabels)
{
this.exportVertexLabels = exportVertexLabels;
}
/**
* Whether the exporter will print edge labels.
*
* @return {@code true} if the exporter prints edge labels, {@code false} otherwise
*/
public boolean isExportEdgeLabels()
{
return exportEdgeLabels;
}
/**
* Set whether the exporter will print edge labels.
*
* @param exportEdgeLabels value to set
*/
public void setExportEdgeLabels(boolean exportEdgeLabels)
{
this.exportEdgeLabels = exportEdgeLabels;
}
/**
* Get the attribute name for vertex labels
*
* @return the attribute name
*/
public String getVertexLabelAttributeName()
{
return vertexLabelAttributeName;
}
/**
* Set the attribute name to use for vertex labels.
*
* @param vertexLabelAttributeName the attribute name
*/
public void setVertexLabelAttributeName(String vertexLabelAttributeName)
{
if (vertexLabelAttributeName == null) {
throw new IllegalArgumentException("Vertex label attribute name cannot be null");
}
String key = vertexLabelAttributeName.trim();
if (registeredAttributes.containsKey(key)) {
throw new IllegalArgumentException("Reserved attribute name");
}
this.vertexLabelAttributeName = key;
}
/**
* Get the attribute name for edge labels
*
* @return the attribute name
*/
public String getEdgeLabelAttributeName()
{
return edgeLabelAttributeName;
}
/**
* Set the attribute name to use for edge labels.
*
* @param edgeLabelAttributeName the attribute name
*/
public void setEdgeLabelAttributeName(String edgeLabelAttributeName)
{
if (edgeLabelAttributeName == null) {
throw new IllegalArgumentException("Edge label attribute name cannot be null");
}
String key = edgeLabelAttributeName.trim();
if (registeredAttributes.containsKey(key)) {
throw new IllegalArgumentException("Reserved attribute name");
}
this.edgeLabelAttributeName = key;
}
/**
* Get the attribute name for edge weights
*
* @return the attribute name
*/
public String getEdgeWeightAttributeName()
{
return edgeWeightAttributeName;
}
/**
* Set the attribute name to use for edge weights.
*
* @param edgeWeightAttributeName the attribute name
*/
public void setEdgeWeightAttributeName(String edgeWeightAttributeName)
{
if (edgeWeightAttributeName == null) {
throw new IllegalArgumentException("Edge weight attribute name cannot be null");
}
String key = edgeWeightAttributeName.trim();
if (registeredAttributes.containsKey(key)) {
throw new IllegalArgumentException("Reserved attribute name");
}
this.edgeWeightAttributeName = key;
}
/**
* Exports a graph in GraphML format.
*
* @param g the graph
* @param writer the writer to export the graph
* @throws ExportException in case any error occurs during export
*/
@Override
public void exportGraph(Graph g, Writer writer)
{
try {
// Prepare an XML file to receive the GraphML data
SAXTransformerFactory factory =
(SAXTransformerFactory) SAXTransformerFactory.newInstance();
TransformerHandler handler = factory.newTransformerHandler();
handler.getTransformer().setOutputProperty(OutputKeys.ENCODING, "UTF-8");
handler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
handler.setResult(new StreamResult(new PrintWriter(writer)));
// export
handler.startDocument();
writeHeader(handler);
writeKeys(handler);
writeGraphStart(handler, g);
writeNodes(handler, g);
writeEdges(handler, g);
writeGraphEnd(handler);
writeFooter(handler);
handler.endDocument();
// flush
writer.flush();
} catch (Exception e) {
throw new ExportException("Failed to export as GraphML", e);
}
}
private void writeHeader(TransformerHandler handler)
throws SAXException
{
handler.startPrefixMapping("xsi", "http://www.w3.org/2001/XMLSchema-instance");
handler.endPrefixMapping("xsi");
AttributesImpl attr = new AttributesImpl();
attr.addAttribute(
"", "", "xsi:schemaLocation", "CDATA",
"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
handler.startElement("http://graphml.graphdrawing.org/xmlns", "", "graphml", attr);
}
private void writeGraphStart(TransformerHandler handler, Graph g)
throws SAXException
{
//
AttributesImpl attr = new AttributesImpl();
attr.addAttribute(
"", "", "edgedefault", "CDATA", g.getType().isDirected() ? "directed" : "undirected");
handler.startElement("", "", "graph", attr);
}
private void writeGraphEnd(TransformerHandler handler)
throws SAXException
{
handler.endElement("", "", "graph");
}
private void writeFooter(TransformerHandler handler)
throws SAXException
{
handler.endElement("", "", "graphml");
}
private void writeKeys(TransformerHandler handler)
throws SAXException
{
if (exportVertexLabels) {
writeAttribute(
handler, vertexLabelAttributeName, new AttributeDetails(
"vertex_label_key", AttributeCategory.NODE, AttributeType.STRING, null));
}
if (exportEdgeLabels) {
writeAttribute(
handler, edgeLabelAttributeName, new AttributeDetails(
"edge_label_key", AttributeCategory.EDGE, AttributeType.STRING, null));
}
if (exportEdgeWeights) {
writeAttribute(
handler, edgeWeightAttributeName,
new AttributeDetails(
"edge_weight_key", AttributeCategory.EDGE, AttributeType.DOUBLE,
Double.toString(Graph.DEFAULT_EDGE_WEIGHT)));
}
for (String attributeName : registeredAttributes.keySet()) {
AttributeDetails details = registeredAttributes.get(attributeName);
writeAttribute(handler, attributeName, details);
}
}
private void writeData(TransformerHandler handler, String key, String value)
throws SAXException
{
AttributesImpl attr = new AttributesImpl();
attr.addAttribute("", "", "key", "CDATA", key);
handler.startElement("", "", "data", attr);
handler.characters(value.toCharArray(), 0, value.length());
handler.endElement("", "", "data");
}
private void writeAttribute(TransformerHandler handler, String name, AttributeDetails details)
throws SAXException
{
AttributesImpl attr = new AttributesImpl();
attr.addAttribute("", "", "id", "CDATA", details.key);
attr.addAttribute("", "", "for", "CDATA", details.category.toString());
attr.addAttribute("", "", "attr.name", "CDATA", name);
attr.addAttribute("", "", "attr.type", "CDATA", details.type.toString());
handler.startElement("", "", "key", attr);
if (details.defaultValue != null) {
handler.startElement("", "", "default", null);
handler
.characters(details.defaultValue.toCharArray(), 0, details.defaultValue.length());
handler.endElement("", "", "default");
}
handler.endElement("", "", "key");
}
private void writeNodes(TransformerHandler handler, Graph g)
throws SAXException
{
// Add all the vertices as elements...
for (V v : g.vertexSet()) {
//
AttributesImpl attr = new AttributesImpl();
attr.addAttribute("", "", "id", "CDATA", getVertexId(v));
handler.startElement("", "", "node", attr);
Optional vertexLabelAttribute =
getVertexAttribute(v, vertexLabelAttributeName);
if (exportVertexLabels) {
if (vertexLabelAttribute.isPresent()) {
writeData(handler, "vertex_label_key", vertexLabelAttribute.get().getValue());
} else {
writeData(handler, "vertex_label_key", String.valueOf(v));
}
}
// find vertex attributes
Map vertexAttributes =
getVertexAttributes(v).orElse(Collections.emptyMap());
// check all registered
for (Entry e : registeredAttributes.entrySet()) {
AttributeDetails details = e.getValue();
if (details.category.equals(AttributeCategory.NODE)
|| details.category.equals(AttributeCategory.ALL))
{
String name = e.getKey();
String defaultValue = details.defaultValue;
if (vertexAttributes.containsKey(name)) {
Attribute attribute = vertexAttributes.get(name);
String value = attribute.getValue();
if (defaultValue == null || !defaultValue.equals(value)) {
if (value != null) {
writeData(handler, details.key, value);
}
}
}
}
}
handler.endElement("", "", "node");
}
}
private void writeEdges(TransformerHandler handler, Graph g)
throws SAXException
{
// Add all the edges as elements...
for (E e : g.edgeSet()) {
//
AttributesImpl attr = new AttributesImpl();
getEdgeId(e).ifPresent(eId -> {
attr.addAttribute("", "", "id", "CDATA", eId);
});
attr.addAttribute("", "", "source", "CDATA", getVertexId(g.getEdgeSource(e)));
attr.addAttribute("", "", "target", "CDATA", getVertexId(g.getEdgeTarget(e)));
handler.startElement("", "", "edge", attr);
Optional edgeLabelAttribute = getEdgeAttribute(e, edgeLabelAttributeName);
if (exportEdgeLabels) {
if (edgeLabelAttribute.isPresent()) {
writeData(handler, "edge_label_key", edgeLabelAttribute.get().getValue());
} else {
writeData(handler, "edge_label_key", String.valueOf(e));
}
}
if (exportEdgeWeights) {
Double weight = g.getEdgeWeight(e);
if (!weight.equals(Graph.DEFAULT_EDGE_WEIGHT)) { // not
// default
// value
writeData(handler, "edge_weight_key", String.valueOf(weight));
}
}
// find edge attributes
Map edgeAttributes =
getEdgeAttributes(e).orElse(Collections.emptyMap());
// check all registered
for (Entry entry : registeredAttributes.entrySet()) {
AttributeDetails details = entry.getValue();
if (details.category.equals(AttributeCategory.EDGE)
|| details.category.equals(AttributeCategory.ALL))
{
String name = entry.getKey();
String defaultValue = details.defaultValue;
if (edgeAttributes.containsKey(name)) {
Attribute attribute = edgeAttributes.get(name);
String value = attribute.getValue();
if (defaultValue == null || !defaultValue.equals(value)) {
if (value != null) {
writeData(handler, details.key, value);
}
}
}
}
}
handler.endElement("", "", "edge");
}
}
private class AttributeDetails
{
public String key;
public AttributeCategory category;
public AttributeType type;
public String defaultValue;
public AttributeDetails(
String key, AttributeCategory category, AttributeType type, String defaultValue)
{
this.key = key;
this.category = category;
this.type = type;
this.defaultValue = defaultValue;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy