org.jgrapht.nio.dimacs.DIMACSImporter Maven / Gradle / Ivy
/*
* (C) Copyright 2010-2023, by Michael Behrisch 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.dimacs;
import org.jgrapht.*;
import org.jgrapht.alg.util.*;
import org.jgrapht.nio.*;
import java.io.*;
import java.util.*;
import java.util.function.*;
/**
* 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 BaseEventDrivenImporter
implements GraphImporter
{
/**
* Default key used for vertex ID.
*/
public static final String DEFAULT_VERTEX_ID_KEY = "ID";
private Function vertexFactory;
private final double defaultWeight;
/**
* Construct a new DIMACSImporter
*
* @param defaultWeight default edge weight
*/
public DIMACSImporter(double defaultWeight)
{
super();
this.defaultWeight = defaultWeight;
}
/**
* Construct a new DIMACSImporter
*/
public DIMACSImporter()
{
this(Graph.DEFAULT_EDGE_WEIGHT);
}
/**
* Get the user custom vertex factory. This is null by default and the graph supplier is used
* instead.
*
* @return the user custom vertex factory
*/
public Function getVertexFactory()
{
return vertexFactory;
}
/**
* Set the user custom vertex factory. The default behavior is being null in which case the
* graph vertex supplier is used.
*
* If supplied the vertex factory is called every time a new vertex is encountered in the file.
* The method is called with parameter the vertex identifier from the file and should return the
* actual graph vertex to add to the graph.
*
* @param vertexFactory a vertex factory
*/
public void setVertexFactory(Function vertexFactory)
{
this.vertexFactory = vertexFactory;
}
/**
* 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
{
DIMACSEventDrivenImporter genericImporter =
new DIMACSEventDrivenImporter().renumberVertices(false).zeroBasedNumbering(false);
Consumers consumers = new Consumers(graph);
genericImporter.addVertexCountConsumer(consumers.nodeCountConsumer);
genericImporter.addEdgeConsumer(consumers.edgeConsumer);
genericImporter.importInput(input);
}
private class Consumers
{
private Graph graph;
private List list;
public Consumers(Graph graph)
{
this.graph = graph;
this.list = new ArrayList<>();
}
public final Consumer nodeCountConsumer = n -> {
for (int i = 1; i <= n; i++) {
V v;
if (vertexFactory != null) {
v = vertexFactory.apply(i);
graph.addVertex(v);
} else {
v = graph.addVertex();
}
list.add(v);
/*
* Notify the first time we create the node.
*/
notifyVertex(v);
notifyVertexAttribute(
v, DEFAULT_VERTEX_ID_KEY, DefaultAttribute.createAttribute(i));
}
};
public final Consumer> edgeConsumer = t -> {
int source = t.getFirst();
V from = getElement(list, source - 1);
if (from == null) {
throw new ImportException("Node " + source + " does not exist");
}
int target = t.getSecond();
V to = getElement(list, target - 1);
if (to == null) {
throw new ImportException("Node " + target + " does not exist");
}
E e = graph.addEdge(from, to);
if (graph.getType().isWeighted()) {
double weight = t.getThird() == null ? defaultWeight : t.getThird();
graph.setEdgeWeight(e, weight);
}
notifyEdge(e);
};
}
private static E getElement(List list, int index)
{
return index < list.size() ? list.get(index) : null;
}
}