User Tools

Site Tools


devel:plugins:start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
devel:plugins:start [2018/10/04 15:30] – [End result] danildevel:plugins:start [2018/10/09 11:10] (current) – removed danil
Line 1: Line 1:
-====== Plugin development ====== 
  
-This exercise aims at developing a basic plugin that enables editing of a new type of graph model in Workcraft. Follow the step-by-step instructions to proceed from the initial setup of the project all the way to the implementation of the model components. If you want to skip some of the steps, then just download the complete plugin source code: <wrap download>{{exercise01plugin.zip}}</wrap> 
- 
-===== Project setup ===== 
- 
-To begin, create a new project directory called ''_Exercise01Plugin''. Note that the directories starting with an underscore are automatically ignored both by Git and the distribution builder script, therefore this is a good way to add experimental plugins that should not become part of the main Workcraft build. 
- 
-Next either create or download and add the following ''build.gradle'' file below into the project directory to specify the project dependency on the Workcraft framework. For the purpose of our exercise, we will make our ''Exercise01Plugin'' only depend on the ''WorkcraftCore'' plugin: 
- 
-<file shell build.gradle> 
-dependencies { 
-    compile project(':WorkcraftCore') 
-} 
-</file> 
- 
-Now let us call the package for our Exercise01 plugin: ''org.workcraft.plugins.basic''. This follows Workcraft's standard of any new plugin to be packaged as ''org.workcraft.plugins.NewPluginName'' and is typically where all the plugin classes will live. For this step, we create the directory ''src/org/workcraft/plugins/basic'' under ''_Exercise01Plugin''. 
- 
-To complete the set up, go to the top directory of Workcraft and follow either step depending on your development IDE: 
- 
-For IntelliJ IDEA 
-  - Run the ''$./gradlew idea'' command to generate the IntelliJ IDEA project with the newly created plugin. 
-  - Open the ''workcraft.ipr'' file in IDEA to load the project. 
- 
-For Eclipse 
-  - Run the ''$./gradlew eclipse'' command to generate the Eclipse project with the newly created plugin. 
-  - Once opening Eclipse, ensure that you have selected ''workcraft'' as the current //Workspace// directory and imported the project as necessary. For further information on this, see the [[https://workcraft.org/devel/build#eclipse_integration|Eclipse Integration page]]. 
- 
-If everything has went smoothly, you are now set to go.  
-===== Module class ===== 
- 
-As a first step to creating any new Workcraft plugin, a module class needs to be created. This class is essentially the plugin's top-most class, which implements the ''Module'' interface that allows Workcraft to detect the plugin as a new (available) model type. For this exercise, we simply just create the ''BasicModule'' class as follows: 
- 
-<file java BasicModule.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.Framework; 
-import org.workcraft.Module; 
-import org.workcraft.PluginManager; 
- 
-public class BasicModule implements Module { 
- 
-    @Override 
-    public String getDescription() { 
-        return "Basic Module"; 
-    } 
- 
-    @Override 
-    public void init() { 
-        final Framework framework = Framework.getInstance(); 
-        final PluginManager pm = framework.getPluginManager(); 
-        pm.registerModelDescriptor(BasicDescriptor.class); 
-    } 
-} 
-</file> 
- 
-One may notice that inside the ''BasicModule'' class, the ''PluginManager'' registers a new class called ''BasicDescriptor''. The purpose of this class is to define the name of the model and how its mathematical and visual representations are given, by making it implement the ''ModelDescriptor'' class.  
- 
-In the case of the ''BasicDescriptor'' class, we simply set the display name as //Basic Model// and make it create a new ''Basic'' class that will represent its mathematical representation (which will later be discussed in the **Mathematical layer classes** section) like below: 
- 
-<file java BasicDescriptor.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.dom.ModelDescriptor; 
-import org.workcraft.dom.VisualModelDescriptor; 
-import org.workcraft.dom.math.MathModel; 
- 
-public class BasicDescriptor implements ModelDescriptor { 
- 
-    @Override 
-    public String getDisplayName() { 
-        return "Basic Model"; 
-    } 
- 
-    @Override 
-    public MathModel createMathModel() { 
-        return new Basic(); 
-    } 
- 
-    @Override 
-    public VisualModelDescriptor getVisualModelDescriptor() { 
-        return new VisualBasicDescriptor(); 
-    } 
-} 
-</file> 
- 
-However, unlike how the ''BasicDescriptor'' handles the mathematical representation directly, it directly calls the ''VisualBasicDescriptor'' class instead to handle its visual representation. Hence, it essentially specifies the visual layer that is associated with the model.   
- 
-The class does this by implementing the ''VisualModelDescriptor'' interface, which has a method that takes in the mathematical model as an argument and uses its data to create a visual model of it. In our case, the class checks if the provided math model is a ''Basic'' model and produces either a new ''VisualBasic'' model (if successful) or an exception (if unsuccessful):  
- 
-<file java VisualBasicDescriptor.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.dom.VisualModelDescriptor; 
-import org.workcraft.dom.math.MathModel; 
-import org.workcraft.dom.visual.VisualModel; 
- 
-public class VisualBasicDescriptor implements VisualModelDescriptor { 
- 
-    @Override 
-    public VisualModel create(MathModel mathModel) { 
-        if (mathModel instanceof Basic) { 
-            return new VisualBasic((Basic) mathModel); 
-        } 
-        throw new RuntimeException("Unsupported math model type"); 
-    } 
-} 
-</file> 
- 
-As a result, we have registered the ''Basic'' and ''VisualBasic'' classes as the implementations of the mathematical and visual layers of our basic model respectively. 
- 
-===== Mathematical layer classes ===== 
- 
-To describe how our mathematical model works, we need to first determine its node(s) and their functionality and interactivity with each other. Fortunately, most of the necessary core features have already been implemented within an abstract class called ''AbstractMathModel'', which can be used to describe any type of model (including those not yet created or discovered).  
- 
-For our exercise, we will simply make our ''Basic'' class extend from the mentioned abstract class above and implement a function that will mathematically connect two given nodes together. Note that the ''AbstractMathModel'' class has only defined how the connection is retrieved and not how the connection is created, as most models will (most likely) have a different meaning of what defines a connection in its concept.  
- 
-<file java Basic.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.dom.Container; 
-import org.workcraft.dom.Node; 
-import org.workcraft.dom.math.AbstractMathModel; 
-import org.workcraft.dom.math.MathConnection; 
-import org.workcraft.dom.math.MathNode; 
-import org.workcraft.dom.references.HierarchicalUniqueNameReferenceManager; 
-import org.workcraft.dom.references.ReferenceManager; 
-import org.workcraft.serialisation.References; 
-import org.workcraft.util.Hierarchy; 
- 
-public class Basic extends AbstractMathModel { 
- 
-    public Basic() { 
-        this(null); 
-    } 
- 
-    public Basic(Container root) { 
-        super(root); 
-    } 
- 
-    public MathConnection connect(Node first, Node second) { 
-        MathConnection con = new MathConnection((MathNode) first, (MathNode) second); 
-        Hierarchy.getNearestContainer(first, second).add(con); 
-        return con; 
-    } 
-} 
-</file> 
- 
-To make our example simple, this model will only make use one type of node that is implemented as a relatively trivial ''Vertex'' class (which only extends from the ''MathNode'' class with no methods or functions created).   
- 
-<file java Vertex.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.annotations.VisualClass; 
-import org.workcraft.dom.math.MathNode; 
- 
-@VisualClass(VisualVertex.class) 
-public class Vertex extends MathNode { 
-} 
-</file> 
- 
-It is also worth noting that our ''Vertex'' class has been tagged by a ''VisualClass'' annotation, which specifies how it (i.e. our model) should be visualised (to the user).   
- 
-===== Visual layer classes ===== 
- 
-When describing our visual model, we need to determine how our node(s) will be represented (visually) and how they connect with each other (using the notation provided by our mathematical model). Again, fortunately most of the necessary core features (for describing any existing/new visual model) have already been implemented within another abstract class called ''AbstractVisualModel'', which includes connect the nodes in the model, retrieving the mathematical representation, drawing the model, etc.  
- 
-This eventually leads us to creating our ''VisualBasic'' class that extends from the mentioned abstract class above, where we also implement a new function that specifies what tools will be available when editing the model - these are most notably selection, comment generation, connection and node generation. Also note that in this exercise class, we override the //connect// and //validConnection// methods. This is where we can specify how our nodes should connect to each other and what they can connect to (which is particularly useful, when we want one type of node to only connect to another specific type e.g. how Petrinets allow places and transitions connect to each other but not to themselves). 
- 
-<file java VisualBasic.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.annotations.DisplayName; 
-import org.workcraft.annotations.ShortName; 
-import org.workcraft.dom.Container; 
-import org.workcraft.dom.Node; 
-import org.workcraft.dom.math.MathConnection; 
-import org.workcraft.dom.visual.AbstractVisualModel; 
-import org.workcraft.dom.visual.VisualComponent; 
-import org.workcraft.dom.visual.VisualGroup; 
-import org.workcraft.dom.visual.connections.VisualConnection; 
-import org.workcraft.exceptions.InvalidConnectionException; 
-import org.workcraft.exceptions.NodeCreationException; 
-import org.workcraft.gui.graph.generators.DefaultNodeGenerator; 
-import org.workcraft.gui.graph.tools.*; 
-import org.workcraft.util.Hierarchy; 
- 
-import java.util.ArrayList; 
-import java.util.List; 
- 
-@DisplayName("Basic") 
-@ShortName("basic") 
-public class VisualBasic extends AbstractVisualModel { 
- 
-    public VisualBasic(Basic model) { 
-        this(model, null); 
-    } 
- 
-    public VisualBasic(Basic model, VisualGroup root) { 
-        super(model, root); 
-        setGraphEditorTools(); 
-        if (root == null) { 
-            try { 
-                createDefaultFlatStructure(); 
-            } catch (NodeCreationException e) { 
-                throw new RuntimeException(e); 
-            } 
-        } 
-    } 
- 
-    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); 
-    } 
- 
-    @Override 
-    public void validateConnection(Node first, Node second) throws InvalidConnectionException { 
-        if ((first instanceof VisualVertex) && (second instanceof VisualVertex)) return; 
- 
-        throw new InvalidConnectionException("Invalid connection."); 
-    } 
- 
-    @Override 
-    public VisualConnection connect(Node first, Node second, MathConnection mConnection) 
-            throws InvalidConnectionException { 
-        validateConnection(first, second); 
- 
-        VisualComponent v1 = (VisualComponent) first; 
-        VisualComponent v2 = (VisualComponent) second; 
-        Node m1 = v1.getReferencedComponent(); 
-        Node m2 = v2.getReferencedComponent(); 
- 
-        if (mConnection == null) { 
-            mConnection = ((Basic) getMathModel()).connect(m1, m2); 
-        } 
-        VisualConnection vConnection = new VisualConnection(mConnection, v1, v2); 
-        Container container = Hierarchy.getNearestContainer(v1, v2); 
-        container.add(vConnection); 
-        return vConnection; 
-    } 
-} 
-</file> 
- 
-As shown in the annotation tag of our ''Vertex'' class, the visualisation of the vertex node is implemented by the ''VisualVertex'' class below. It is a relatively simple class that overrides the draw method, allowing us to specify how we want our nodes to be displayed on-screen. For example, in our case we just render the vertex as a rectangle (where its name and labels are positioned as appropriate). 
- 
-<file java VisualVertex.java> 
-package org.workcraft.plugins.basic; 
- 
-import org.workcraft.dom.visual.DrawRequest; 
-import org.workcraft.dom.visual.VisualComponent; 
-import org.workcraft.gui.Coloriser; 
- 
-import java.awt.*; 
-import java.awt.geom.Rectangle2D; 
- 
-public class VisualVertex extends VisualComponent { 
- 
-    public VisualVertex(Vertex vertex) { 
-        super(vertex); 
-    } 
- 
-    @Override 
-    public void draw(DrawRequest r) { 
-        Graphics2D g = r.getGraphics(); 
- 
-        double xy = -size / 2 + strokeWidth / 2; 
-        double wh = size - strokeWidth; 
-        Shape shape = new Rectangle2D.Double(xy, xy, wh, wh); 
- 
-        Color background = r.getDecoration().getBackground(); 
-        g.setColor(Coloriser.colorise(getFillColor(), background)); 
-        g.fill(shape); 
- 
-        Color colorisation = r.getDecoration().getColorisation(); 
-        g.setColor(Coloriser.colorise(getForegroundColor(), colorisation)); 
-        g.setStroke(new BasicStroke((float) strokeWidth)); 
-        g.draw(shape); 
- 
-        drawLabelInLocalSpace(r); 
-        drawNameInLocalSpace(r); 
-    } 
-} 
-</file> 
- 
-===== End result ===== 
- 
-As a result by completing all the above tasks, you have now successfully created a plugin for Workcraft. To help illustrate how the end result looks like, some images for this have been provided below: 
- 
-{{ devel:plugins:result-1.png?nolink |When creating a new 'work' file}} 
- 
-{{ devel:plugins:result-2.png?nolink |Diagram of some drawn-out basic model}} 
Copyright © 2014-2024 workcraft.org

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki