Search code examples
javagenericsgraphgraphvizjgrapht

Using the JGraphT library's EdgeProvider class


I've looked all 'round for a solution, but due to my not having much experience with Java's generic programming style, I'm a bit stumped on what words to even use to search this. I am trying to use the DOTImporter (http://jgrapht.org/javadoc/org/jgrapht/ext/DOTImporter.html) from the JGraphT library, which in turn requires me to use the EdgeProvider interface (http://jgrapht.org/javadoc/org/jgrapht/ext/EdgeProvider.html). I have tried myriad ways of implementing this, but the fact that buildEdge uses a generic return type has me quite confused. I'm trying to use their DefaultEdge as well (http://jgrapht.org/javadoc/org/jgrapht/graph/DefaultEdge.html) and that's not much better, given that their API specifies two methods for getting a couple of fields (source and target), but no means by which to set those fields, and since they are not public, and the constructor has no parameters, I'm not quite sure how I'm supposed to set them.

To contextualize this, I am trying to load a .dot file and all I really care about are the labels, so my graph will really be composed of String vertices, and the therefore the Edges will be a (String, String) structure as well. It's quite simple really, and therefore I wouldn't half mind implementing my own Graph structure to use with that, but I really do want to take advantage of DOTImporter.

If anyone could offer me some help on how to properly implement a concrete class for EdgeProvider and how to use the DefaultEdge class, I would be extremely grateful.


Solution

  • Short answer: Here's a complete example of a DOTImporter (it is pretty easy...):

    Example code for JGraphT versions until 1.4.x:

    public class DOTTest {
    
        public static void main(String[] args) throws ImportException {
            //Example graph
            String input =
                    "digraph graphname {\r\n" + "     a -> b -> c;\r\n" + "     b -> d;\r\n" + " }";
    
            GraphImporter<String, DefaultEdge> importer = new DOTImporter<>(
                    (label, attributes) -> label, 
                    (from, to, label, attributes) -> new DefaultEdge()
            );
    
            Graph<String, DefaultEdge> result = new SimpleDirectedGraph<>(DefaultEdge.class);
            importer.importGraph(result, new StringReader(input));
    
            System.out.println(result);
        }
    }
    

    New example code for JGraphT version 1.5.0 and higher:

    public class DOTTest {
    
        public static void main(String[] args) throws ImportException {
            //Example graph
            String input =
                    "digraph graphname {\r\n" + "     a -> b -> c;\r\n" + "     b -> d;\r\n" + " }";
    
        Graph<String, DefaultEdge> result = new SimpleDirectedGraph<>(DefaultEdge.class);
    
        DOTImporter<String, DefaultEdge> dotImporter = new DOTImporter<>();
        dotImporter.setVertexFactory(label -> label);
        dotImporter.importGraph(result, new StringReader(input);
    
        System.out.println(result);
    }
    

    }

    Note that in JGraphT every single class comes with a corresponding test class, containing many examples.

    Longer answer: In JGraphT every vertex and every edge is an object. This allows for very flexible graphs. The disadvantage is that you need to provide factory methods. For instance, if you invoke graph.addEdge(u,v), a new object has to be created which represents the edge between u and v. For this purpose, an edge factory is needed. In many practical cases, the user does not need custom edge objects. For those cases, JGraphT offers the DefaultEdge class. A practical example where you would like to have custom edges, is for instance when you build a graph representing a road network. A vertex would be an Intersection, an edge would be a Street. The street object would for instance store the number of lanes, driving speed, direction of travel, etc.

    So what's the purpose of an IntrusiveEdge? Unless you intend to contribute code to JGraphT, this is not really important. You should never use IntrusiveEdge directly. The IntrusiveEdge is the base type of most edges. Under the hood, it stores the source,target and weight of an edge. However, these fields are hidden, i.e. they cannot be accessed directly! Instead, the graph class provides methods to access these fields, e.g. graph.getEdgeSource(myEdge).