User Tools

Site Tools


devel:plugin:graph

Basic plugin for Directed Graph model

The purpose of this exercise is to demonstrate 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 was intentionally made to be as simple as possible to help reduce the framework overheads, as well as to mainly focus on the structure of a typical Workcraft plugin. Development of more advanced features and graph models, such as Petri Nets, will be covered in later exercises.

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

Plugin and descriptor classes

When creating a new Workcraft plugin, there are few necessary classes that needs to be considered. These are the plugin class (i.e. the class implementing the Plugin interface) and the descriptor classes (i.e. the classes implementing the ModelDescriptor and VisualModelDescriptor interfaces), which all implement a plugin's core functionality. For further information, see how the plugin is laid out.

The plugin enables Workcraft to detect its registered model whenever the user creates a new '.work' file (i.e. the file type used to save one's models/drawings in Workcraft). The roles of this class include providing an internal description of the plugin and determining how it is initialised, as shown in the GraphPlugin class:

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

Note that in the init() method of GraphPlugin there are two important classes used: the Framework and PluginManager. While they are only shown to just retrieve the running Workcraft instance and to register the new plugin, they also hold other important functionalities that make Workcraft and its plugins work correctly. Therefore, allowing one to just use the methods above for their new plugin(s).

Note the use of @SuppressWarnings("unused") annotation. This is to prevent warnings that the class in never used. Indeed, there is no compile-time reference to this class, this however is expected, as the plugin class is loaded at run-time.

Following from GraphPlugin, one of the necessary plugin's descriptor classes is its ModelDescriptor used to describe the plugin's display name (i.e. name shown when creating a new .work file) and how its math and visual models are handled (which is discussed in more detail later in the exercise). For this example, the GraphDescriptor is created and uses the 'newly' created Graph class 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 one of the necessary plugin's descriptor classes is its VisualModelDescriptor class, which is similar to the ModelDescriptor but describes and handles the visual models instead. Note that the ModelDescriptor class actually calls its visual counterpart to handle plugin's visual models, as shown in the example above. For this example, the VisualGraphDescriptor is created and uses the 'newly' created VisualGraph class 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 math model works, its node(s) and their functionality needs to be determined using Workcraft's MathModel interface. Fortunately, the abstract class AbstractMathModel has already implemented most of these necessary features and functionalities. This means it can describe any type of model, even ones that are not yet created. For further details on this, see how the mathematical layer is structured.

One of the few methods worth noting in the AbstractMathModel class is how it validates the connection between the nodes (i.e. the validateConnection method), as this is used to determine what nodes can connect to what. This is especially important for more advanced graph models like Petri Nets, 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 like in Graph:

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, these features are fortunately already implemented by the MathNode abstract class and allows one to describe any type of node. 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 Vertex has actually been tagged by the @VisualClass annotation, which essentially specifies how the node should be visualised using what visual class. For the purpose of this exercise, details about annotations have been omitted and will be covered in the later exercises.

Visual layer classes

To describe how a plugin's visual model works, its node(s) and their functionality must also be determined using Workcraft's VisualModel interface. Like the AbstractMathModel class, the AbstractVisualModel class also has already implemented most of the necessary features and functionalities for this. Again, this means it can be used to represent any type of model. For further details on this, see how the visual layer is structured.

One of the few methods worth noting in the AbstractMathModel class is what graphicals tools are provided via the setGraphEditorTools method. Essentially, as the name suggests, this method determines what tools are available for the user's use during the creation of their model e.g. connection between nodes and node generation. This is quite important (and even beneficial) for models that may require a different set of tools, e.g. like in Petri Net model where the user must be able to somehow generate both types of nodes and connect them. The example for this, i.e. VisuaGraph, is shown below:

VisualGraph.java
package org.workcraft.plugins.examples.graph;
 
import org.workcraft.dom.visual.AbstractVisualModel;
import org.workcraft.dom.visual.VisualGroup;
import org.workcraft.dom.generators.DefaultNodeGenerator;
import org.workcraft.gui.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);
    }
}

Lastly, for visual nodes, they must extend the VisualComponent class which will allow the node to be drawn and shown on screen. Note that the necessary features for these operations have already been implemented by this class and that any newly drawn node will be set as a rectangle (along with other properties like its label, name and background colour) by default. For the purpose of this exercise, details about how these nodes are drawn and how their attributes are implemented have been omitted and will be covered in the next exercise about Petri Nets. Source code of the VisualVertex class 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)