User Tools

Site Tools


devel:plugin:graph

Basic plugin for Directed Graph model

The purpose of this exercise aims at demonstrating how a new Workcraft plugin can be implemented from scratch, by using a basic graph model like Directed Graphs as an example. Note that this exercise has been intentionally made as simple as possible to reduce the framework overheads and to focus on the plugin's structure. Demonstrations of developing more advanced features of graph models are covered in the later exercises.

Before beginning this exercise, make sure that you have already set up the project for your plugin.

Module and descriptor classes

When creating a new Workcraft plugin, there are some necessary classes implementing a plugin's core interfaces that must first be considered. These are the module class (i.e. the class implementing the Module interface) and the descriptor classes (i.e. the classes implementing the ModelDescriptor and VisualModelDescriptor interfaces). For more details on this, see how the plugin is laid out.

A plugin's Module class is also known as its top-most class, which allows Workcraft to detect it as a usable model when a user creates a new .work file. This class simply provides a description of the plugin (internally) and determines how it is initialised. The example for this class (i.e. GraphModule) is shown below:

GraphModule.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.Framework;
import org.workcraft.Module;
import org.workcraft.PluginManager;
 
public class GraphModule implements Module {
 
    @Override
    public String getDescription() {
        return "Directed Graph Module (example)";
    }
 
    @Override
    public void init() {
        final Framework framework = Framework.getInstance();
        final PluginManager pm = framework.getPluginManager();
        pm.registerModelDescriptor(GraphDescriptor.class);
    }
}

Note that in the initialisation (init()) of the GraphModule example, there are two key components being used: the Framework and PluginManager classes. Whilst they can be seen to simply retrieve the running instance of Workcraft and registering the new plugin, they also actually implement the necessary functionalities in making Workcraft and its plugins work correctly (allowing one to just use the methods shown above for their new plugin(s)). Because of the technicality of these classes, they have been omitted for the purpose of this exercise.

One of the necessary plugin's descriptor classes is its ModelDescriptor class, which is used to 'describe' what its display name is (i.e. the name shown when creating a new .work file) and how its mathematical and visual models are handled (which more about this is later discussed). The example for this class (i.e. GraphDescriptor) is shown below, where the newly instanced Graph class can be found in the later Mathematical layer classes section:

GraphDescriptor.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.dom.ModelDescriptor;
import org.workcraft.dom.VisualModelDescriptor;
import org.workcraft.dom.math.MathModel;
 
public class GraphDescriptor implements ModelDescriptor {
 
    @Override
    public String getDisplayName() {
        return "Directed Graph (example)";
    }
 
    @Override
    public Graph createMathModel() {
        return new Graph();
    }
 
    @Override
    public VisualModelDescriptor getVisualModelDescriptor() {
        return new VisualGraphDescriptor();
    }
}

The other of the necessary plugin's descriptor class is its VisualModelDescriptor class, which similarly is used to only 'describe' how its visual models are handled based on what has been provided as its mathematical model. It is worth noting that the ModelDescriptor class actually calls its visual counterpart to help it handle the visual models, as shown in the example above. The example for this class (i.e. VisualGraphDescriptor) is shown below where the newly instanced VisualGraph class can be found in the later Visual layer classes section:

VisualGraphDescriptor.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.dom.VisualModelDescriptor;
import org.workcraft.dom.math.MathModel;
import org.workcraft.dom.visual.VisualModel;
 
public class VisualGraphDescriptor implements VisualModelDescriptor {
 
    @Override
    public VisualGraph create(MathModel mathModel) {
        if (mathModel instanceof Graph) {
            return new VisualGraph((Graph) mathModel);
        }
        throw new RuntimeException("Unsupported math model type");
    }
}

Mathematical layer classes

To describe how a plugin's mathematical model works, its node(s) and their functionality and interactivity with each other must first be determined using Workcraft's MathModel interface. Fortunately, most of the necessary features and functionality have already been implemented by the abstract class AbstractMathModel allowing it to describe any type of model (including those that are not yet created or even discovered). For more details on this, see how the mathematical layer is structured.

One of the few methods worth noting from the AbstractMathModel for most classes is how it validates the connection between nodes (i.e. the validateConnection method), as this determines what nodes can connect to what. This is especially important for more advanced graph models like the Petri Net model, which uses two different types of nodes that can only connect to each other. Because Directed Graphs only uses a single type of node, this method is not overridden as shown in the example (i.e. Graph) below:

Graph.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.dom.Container;
import org.workcraft.dom.math.AbstractMathModel;
import org.workcraft.serialisation.References;
 
public class Graph extends AbstractMathModel {
 
    public Graph() {
        this(null, null);
    }
 
    public Graph(Container root, References refs) {
        super(root, refs);
    }
}

For nodes, they must implement Workcraft's Node interface which essentially assigns a 'parent' node and possibly some 'child' nodes to them. Again, fortunately these necessary features have already been implemented by the abstract class MathNode allowing it to describe any type of node, where its visual counterpart can be found in the later Visual layer classes section. The example for this class (i.e. Vertex) is shown below:

Vertex.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.annotations.VisualClass;
import org.workcraft.dom.math.MathNode;
 
@VisualClass(VisualVertex.class)
public class Vertex extends MathNode {
}

Note that in the Vertex example above, the class has been tagged by a @VisualClass annotation. This annotation is specifically used to specify how the node should be visualised on the model, by referring to its visual counterpart. More annotations will be covered in the later exercises.

Visual layer classes

Similarly to describe how a plugin's visual model works, the visual representation of the node(s) and how they function (based on its mathematical counterpart) must first be determined using Workcraft's VisualModel interface. Like the abstract class AbstractMathModel, the abstract class AbstractVisualModel has already implemented most of the necessary features and functionality allowing it to represent any type of model. Again, for more details on this, see how the visual layer is structured.

One of the few methods worth noting from the AbstractVisualModel for most classes is what graphical tools are provided (i.e. the setGraphEditorTools method), as this determines what tools will be available for the user to use such as connecting nodes together, creating nodes, grouping nodes, etc. Again, this is important (and beneficial) for models like the Petri Net model since the user must somehow generate both types of nodes. It is also worth noting that different models will require different sets of tools, which does mean the usability of the model is heavily affected by these choices. The example for this class (i.e. VisualGraph) is shown below (with the selection, comment generator, connector and node generator tools being available):

VisualGraph.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.dom.visual.AbstractVisualModel;
import org.workcraft.dom.visual.VisualGroup;
import org.workcraft.gui.graph.generators.DefaultNodeGenerator;
import org.workcraft.gui.graph.tools.*;
 
import java.util.ArrayList;
import java.util.List;
 
public class VisualGraph extends AbstractVisualModel {
 
    public VisualGraph(Graph model) {
        this(model, null);
    }
 
    public VisualGraph(Graph model, VisualGroup root) {
        super(model, root);
        setGraphEditorTools();
    }
 
    private void setGraphEditorTools() {
        List<GraphEditorTool> tools = new ArrayList<>();
        tools.add(new SelectionTool());
        tools.add(new CommentGeneratorTool());
        tools.add(new ConnectionTool());
        tools.add(new NodeGeneratorTool(new DefaultNodeGenerator(Vertex.class)));
        setGraphEditorTools(tools);
    }
}

For (visual) nodes, they must extend from Workcraft's VisualComponent class which has already implemented the necessary features including how it draws the nodes and how they are visualised on-screen. Note that the class draws any new node as a rectangle by default and that it has also implemented many other important attributes of nodes such as its label, name, background colour, etc. For the purpose of this exercise, details on how the nodes are drawn and how these attributes are implemented have been omitted and will be covered in the next exercise. The example for this class (i.e. VisualVertex) is shown below:

VisualVertex.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.dom.visual.VisualComponent;
 
public class VisualVertex extends VisualComponent {
 
    public VisualVertex(Vertex vertex) {
        super(vertex);
    }
}

Solution

Download all the source code of the example classes used throughout this exercise here:

graphplugin.zip (5 KiB)