org.jgrapht.io.DIMACSImporter Maven / Gradle / Ivy
/*
* (C) Copyright 2010-2017, by Michael Behrisch, Joris Kinable, Dimitrios
* Michail 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 java.io.*;
import java.util.*;
import org.jgrapht.*;
/**
* Imports a graph specified in DIMACS format.
*
*
* See {@link DIMACSFormat} for a description of all the supported DIMACS formats.
*
*
* In summary, one of the most common DIMACS formats was used in the
* 2nd DIMACS challenge and follows
* the following structure:
*
*
* {@code
* DIMACS G {
* c ignored during parsing of the graph
* p edge
* e
* e
* e
* e
* ...
* }
* }
*
*
* Although not specified directly in the DIMACS format documentation, this implementation also
* allows for the a weighted variant:
*
*
* {@code
* e
* }
*
*
* Note: the current implementation does not fully implement the DIMACS specifications! Special
* (rarely used) fields specified as 'Optional Descriptors' are currently not supported (ignored).
*
* @author Michael Behrisch (adaptation of GraphReader class)
* @author Joris Kinable
* @author Dimitrios Michail
*
* @param the graph vertex type
* @param the graph edge type
*/
public class DIMACSImporter
extends AbstractBaseImporter
implements GraphImporter
{
private final double defaultWeight;
// ~ Constructors ----------------------------------------------------------
/**
* Construct a new DIMACSImporter
*
* @param vertexProvider provider for the generation of vertices. Must not be null.
* @param edgeProvider provider for the generation of edges. Must not be null.
* @param defaultWeight default edge weight
*/
public DIMACSImporter(
VertexProvider vertexProvider, EdgeProvider edgeProvider, double defaultWeight)
{
super(vertexProvider, edgeProvider);
this.defaultWeight = defaultWeight;
}
/**
* Construct a new DIMACSImporter
*
* @param vertexProvider provider for the generation of vertices. Must not be null.
* @param edgeProvider provider for the generation of edges. Must not be null.
*/
public DIMACSImporter(VertexProvider vertexProvider, EdgeProvider edgeProvider)
{
this(vertexProvider, edgeProvider, Graph.DEFAULT_EDGE_WEIGHT);
}
// ~ Methods ---------------------------------------------------------------
/**
* Import a graph.
*
*
* The provided graph must be able to support the features of the graph that is read. For
* example if the file contains self-loops then the graph provided must also support self-loops.
* The same for multiple edges.
*
*
* If the provided graph is a weighted graph, the importer also reads edge weights. Otherwise
* edge weights are ignored.
*
* @param graph the output graph
* @param input the input reader
* @throws ImportException in case an error occurs, such as I/O or parse error
*/
@Override
public void importGraph(Graph graph, Reader input)
throws ImportException
{
// convert to buffered
BufferedReader in;
if (input instanceof BufferedReader) {
in = (BufferedReader) input;
} else {
in = new BufferedReader(input);
}
// add nodes
final int size = readNodeCount(in);
Map map = new HashMap();
for (int i = 0; i < size; i++) {
Integer id = Integer.valueOf(i + 1);
V vertex = vertexProvider.buildVertex(id.toString(), new HashMap());
map.put(id, vertex);
graph.addVertex(vertex);
}
// add edges
String[] cols = skipComments(in);
while (cols != null) {
if (cols[0].equals("e") || cols[0].equals("a")) {
if (cols.length < 3) {
throw new ImportException("Failed to parse edge:" + Arrays.toString(cols));
}
Integer source;
try {
source = Integer.parseInt(cols[1]);
} catch (NumberFormatException e) {
throw new ImportException(
"Failed to parse edge source node:" + e.getMessage(), e);
}
Integer target;
try {
target = Integer.parseInt(cols[2]);
} catch (NumberFormatException e) {
throw new ImportException(
"Failed to parse edge target node:" + e.getMessage(), e);
}
String label = "e_" + source + "_" + target;
V from = map.get(source);
if (from == null) {
throw new ImportException("Node " + source + " does not exist");
}
V to = map.get(target);
if (to == null) {
throw new ImportException("Node " + target + " does not exist");
}
try {
E e = edgeProvider.buildEdge(from, to, label, new HashMap());
graph.addEdge(from, to, e);
if (graph.getType().isWeighted()) {
double weight = defaultWeight;
if (cols.length > 3) {
weight = Double.parseDouble(cols[3]);
}
graph.setEdgeWeight(e, weight);
}
} catch (IllegalArgumentException e) {
throw new ImportException("Failed to import DIMACS graph:" + e.getMessage(), e);
}
}
cols = skipComments(in);
}
}
private String[] split(final String src)
{
if (src == null) {
return null;
}
return src.split("\\s+");
}
private String[] skipComments(BufferedReader input)
{
String[] cols = null;
try {
cols = split(input.readLine());
while ((cols != null)
&& ((cols.length == 0) || cols[0].equals("c") || cols[0].startsWith("%")))
{
cols = split(input.readLine());
}
} catch (IOException e) {
// ignore
}
return cols;
}
private int readNodeCount(BufferedReader input)
throws ImportException
{
final String[] cols = skipComments(input);
if (cols[0].equals("p")) {
if (cols.length < 3) {
throw new ImportException("Failed to read number of vertices.");
}
Integer nodes;
try {
nodes = Integer.parseInt(cols[2]);
} catch (NumberFormatException e) {
throw new ImportException("Failed to read number of vertices.");
}
if (nodes < 0) {
throw new ImportException("Negative number of vertices.");
}
return nodes;
}
throw new ImportException("Failed to read number of vertices.");
}
}