18.2. Basic example

18.2.1. Overview

Upon project creation in Eclipse, the plugin development can be started. Plugins are typically implemented in accordance with the following steps:

  1. Definition of the plugin task.

  2. Implementation of the com.semture.ecoube.plugin.Action interface.

  3. Implementation of the com.semture.ecoube.plugin.ToolbarButton and/or com.semture.ecoube.plugin.MenuItem interface.

  4. Implementation of the com.semture.ecube.plugin.Plugin interface.

  5. Code deployment.

  6. Validity check.

The figure 2 shows a class structure diagram for the plugin to be developed.

Class structure diagram for the plugin to be developed

Figure 18.2. Class structure diagram for the plugin to be developed

Those considerations show that the plugin uses elements of the Cubetto-API. At the first instance level, the com.semture.ecube.plugin package is interesting for the implementation of a plugin.

The complete source code for the plugin to be developed can be downloaded here. Follow the steps hereafter to include the code in your development environment:

  1. Unpack the archive into a file in the Eclipse-workspace.

  2. Create a new project and name it "Plugin".

  3. The unpacked project is automatically recognised by Eclipse when the dialog is closed.

  4. As the reference to ecube_classes.jar is still inaccurate, the project cannot be compiled yet. Edit the path of the .jar data file under Project->Properties->Java Build Path -> Libraries (see Figure 1). The project will be compiled properly.

18.2.2. Plugin task

For this introducting example, the names of all selected model items should be displayed in a window. The plugin activation takes place when clicking a button on the toolbar and entering a record in the model editor menu. When clicking the button or the menu, a dialog displaying the names of all items selected in the model editor appears.

18.2.3. Implementation of the action interface

A plugin action is triggered by the implementation of the action interface. This interface is a starting point for the implementation of the plugin content. The action interface requires in turn the implementation of a series of methods. To begin with, the action itself needs to be defined, the methods getName(), getToolTip() and getHelp() can thereby be used. Each function returns strings that indicate the name and tooltip of the action. The name of the action is used within the tool to manage the plugins. A tooltip is displayed when the mouse cursor is over the appropriate menu item or toolbar button. From now on, tooltips will be displayed in a separate help window. They should therefore be returned as HTML-text.

The function getKeyStroke() allows to define a hotkey linked to that action. Upon creation of a corresponding menu item, the shortcut is activated, and the hotkey is implemented into the menu item description.

CAUTION: Should a key be double-assigned, the first assignment will remain valid. The implementation of the action interface can therewith be started:

/**
 * @return
 *   Namen of the action.
 */e
public String getName() {
    return "Element names";
}

/**
 * @return
 *   Tooltip of the action.
 */
public String getTooltip() {
    return "Shows the name of all selected model elements.";
}

/**
 * @return
 *   Help content of the action.
 */
public String getHelp() {
    return "<html>Shows the name of all selected model elements. " + 
        "The action is available only, when at least one element is selected.</html>";
}

/**
 * @return
 *   The key stroke.
 */
public KeyStroke getKeyStroke() {
    return KeyStroke.getKeyStroke('N', InputEvent.ALT_MASK + InputEvent.CTRL_MASK);
}

It is recommended to the constructor of the action class to save the reference to the relevant object that he/she has got. This object is an instance of the class, which implements the plugin interface (see frame below). The reference can be used lateron to exchange data between both objects:

/**
 * The related plugin.
 */
private MyPlugin plugin;

/**
 * Constructor.
 * 
 * @param plugin
 *   The plugin, we belong to.
 */
public MyPluginAction(MyPlugin plugin) {
    this.plugin = plugin;
}

A further description of the action is based on the transaction concept of Cubetto Toolset. If a plugin is to be executed during a transaction, the method isTransactional() has to yield "true". In this case, the plugin runs in its own transaction. If the run() method (cf. below) runs faultless, the transaction will be completed with a commit(). If run() is interrupted by an exception, the current transaction will roll back and all actions taken by the plugin will be discarded.

IMPORTANT: All plugins, which modify the existing model data have to be run in own transactions, so that isTransactional() yields "true". The getTransactionDescription() method will indicate the string, which describes the transaction (or more precisely the save point) upon its completion. getTransactionDescription() can only yield null if the plugin does not need to be run in a transaction.

As the plugin to be implemented only has a read access to the Cubetto Toolset data, the transaction concept is not required. This taken into account, isTransactional() and getTransactionDescription() can be implemented as indicated below:

/**
 * @return
 *   false.
 */
public boolean isTransactional() {
    return false;
}

/**
 * @return
 *   an empty string.
 */
public String getTransactionDescription() {
    return "";
}

The actual task of the plugin is implemented into the methods Run() and run(). The canRun() method yields "true" if the execution of the plugin is allowed. In this case, the linked toolbar and menu items are activated. Therewith, the validity of the plugin code depends on specific conditions, e.g. the selection of model items in the model editor. The run() method is run as soon as the user clicks the toolbar or appropriate menu item.

The plugin code validity should be provided as soon as at least one model item is selected. The items currently selected are saved in the SelectionManager that is directly sent to the plugin during the initialization (see below). The access to the SelectionManager is provided by the link to the plugin interface:

/**
 * @return
 *   true, if at least one element has been selected.
 */
public boolean canRun() {
    return !plugin.getSelectionManager().deriveE3Objects().isEmpty();
}

The last step consists in implementing the run() method that in turn determines the actual plugin functionality. In agreement with the task definition, the selected items are displayed as strings in a dialog box, using the ScriptHelper that provides windows for simple data input and output. The implementation is described in the following frame:

/**
 * Starts the plugin and shows the name of the selected elements.
 */
public void run() {
    //  ScriptHelper and NameResolver helps us here
    ScriptHelper.showInformation(
        "Selected Elements", 
        NameResolver.getNames(plugin.getSelectionManager().deriveE3Objects()));
}

18.2.4. Implementation of a toolbar button and/or menu item interface

At this step, the question arises, how the plugin should be integrated into the modeling tool. Two alternatives are possible: display a button on the model editor toolbar or display a menu item in the menu editor plugin menu. Of course, both alternatives can be implemented in parallel. The MenuItem-Interface, to begin with, works as follows: in Java, a menu item is defined by its name, its hotkey within the menu (mnemonics represented as underlined characters) as well as its icon. Accordingly, the methods getText(), getMnemonic() and getIcon() need to be implemented:

/**
 * @return
 *   Text of the menu item.
 */
public String getText() {
    return "Show names";
}

/**
 * @return
 *   The short key to the menu item.
 */
public char getMnemonic() {
    return 'N';
}

/**
 * @return
 *   Image icon of the menu item.
 */
public ImageIcon getIcon() {
    return new ImageIcon("Filename");
}

Cubetto Toolset also features a group concept. A group is a number of menu items that belong together, separated from each other by a horizontal line, a so-called separator. The groups are defined with the function getGroup. It is important to note that group numbers can be already used by other plugins, consequently, menu items of the relevant plugin can be assigned according to the structure of other plugins. The function getIndex()is used to determine the position of items within their groups:

/**
 * @return
 *   The Group, that menu item belongs to.
 */
public int getGroup() {
    return 1;
}

/**
 * @return
 *   Index of the menu item with the group.
 */
public int getIndex() {
    return 1;
}

Menu items are sorted in the Model Editor in ascending group order and within their group according to their index number. IMPORTANT: should a group number and an index number be double-assigned, only one menu item will be displayed in the plugin menu. IMPORTANT: gaps in the group and index number sequences are allowed, as a consequence, it is recommended to use one's own numbering (e.g. numbers over 1000), thereby reducing the risk of having to share it with other users.

Finally, one needs a plugin action linked to the menu item. This action is returned by getAction()(see above). It is recommended to pass the action to the constructor when initializing the menu item.

/**
 * Related action.
 */
private Action action;

/**
 * Creates an object and stores the related action.
 *
 * @param action
 *   The action that is related to that menu item.
 */
public MyPluginMenuItem(Action action) {
    this.action = action;
}

/**
 * @return
 *   The actio nof the menu item.
 */
public Action getAction() {
    return action;
}

The implementation of a button in the toolbar (ToolbarButton-Interface) takes place in a similar way. A ToolbarButton is characterized by its icon, which is returned by getIcon(). The above-mentioned group concept also exists for ToolbarButtons. This involves the implementation of getGroup() and getIndex(). getAction() yields in turn the action linked to the ToolbarButton described above. The following frame contains a short example.

/**
 * Related action.
 */
private Action action;

/**
 * Creates an object and stores the related action.
 *
 * @param action
 *   The action that is related to that button.
 */
public MyPluginToolbarButton(Action action) {
    this.action = action;
}

/**
 * @return
 *   Image icon of the button.
 */
public ImageIcon getIcon() {
    return new ImageIcon("Filename");
}

/**
 * @return
 *   The action of the button.
 */
public Action getAction() {
    return this.action;
}

/**
 * @return
 *   Group, this button belongs to.
 */
public int getGroup() {
    return 1;
}

/**
 * @return
 *   Index within the group.
 */
public int getIndex() {
    return 1;
}

18.2.5. Implementation of the plugin interface

The embedding of the plugin in Cubetto Toolset requires as a last step the implementation of the plugin interface. The methods getAuthor(), getName(), getVersion() and getDescription() do not require any explanation. The implementation yields strings as illustrated in the following frame:

/**
 * @return
 *   Versionnumber of the Plugins.
 */
public String getVersion() {
    return "0.1";
}

/**
 * @return
 *   Author of the plugin.
 */
public String getAuthor() {
    return "Max Mustermann";
}

/**
 * @return
 *   Name of the plugin.
 */
public String getName() {
    return "Testplugin";
}

/**
 * @return
 *   Description of the plugin.
 */
public String getDescription() {
    return "Shows the names of selected model elements.";
}

Furthermore, the plugin implementation can contain a parameterless constructor that initializes local variables. The example below illustrates how the constructor initializes the action that is to be run by the plugin. The initialization can also be triggered with init():

/**
 * Related action.
 */
private Action action;

/**
 * Creates a plugin and the only action.
 */
public MyPlugin() {
    action = new MyPluginAction(this);
}

In the next step, the menu and toolbar items need to be linked to the plugin. The methods getMenuItems() and getToolbarButtons()can be used to do so. Each method returns several menu and toolbar items. Those will later be sorted thanks to getGroup() and getIndex() provided by the MenuItem-Interface and the ToolbarButton-Interface (see above):

/**
 * @return
 *   The available menu items.
 */
public Collection<? extends MenuItem> getMenuItems() {
    Collection<MenuItem> result = new ArrayList<MenuItem>();
    result.add(new MyPluginMenuItem(action));
    return result;
}

/**
 * @return
 *   The available buttons.
 */
public Collection<? extends ToolbarButton> getToolbarButtons() {
    Collection<ToolbarButton> result = new ArrayList<ToolbarButton>();
    result.add(new MyPluginToolbarButton(action));
    return result;
}

Finally, init() and selectionHasChanged() need to be implemented. The first method is called upon plugin activation and passes important parameters to the plugin. These parameters include the session where the plugin is currently run as well as links to the main frame and to the SelectionManager that manages the items selected in the Model Editor.

The reference to the session is important for transaction plugins, e.g. to execute an action in a secondary transaction or to rollback a transaction. The link to the main frame is crucial to plugins running with a window system. Those plugins should use the main frame object as parent frame when implementing modal dialogs.

In the example shown, the only interesting reference in the SelectionManager is the one saved in the object from the class MyPlugin and which can be returned by getSelectionManager() if required:

/**
 * Selection Manager is needed to access the selected model elements.
 */
private SelectionManager selectionManager;

/**
 * Constructor.
 *
 * @param session
 *   not interesting here.
 * @param mainFrame
 *   not interesting here.
 * @param selectionManager
 *   used to access the selected model elements.
 */
public void init(Session session, JFrame mainFrame, SelectionManager selectionManager) {
    this.selectionManager = selectionManager;
}

/**
 * @return
 *   the selection manager.
 */
public SelectionManager getSelectionManager() {
    return selectionManager;
}

selectionHasChanged() is the last method to be implemented. It is activated as soon as a selection in the Model Editor is modified. As a result, the plugin output can directly depend on a selection, or a change in a selection in the Model Editor. As this method is not necessary in the present example, it remains blank:

/**
 * Invoked, when model elements are selected / deselected.
 *
 * @param oldSelection
 *   Old Selektion
 * @param newSelection
 *   New Selektion
 */
public void selectionHasChanged(Collection oldSelection, Collection newSelection) {
    // Will not be handled here
}

18.2.6. Deployment

A plugin needs to be saved in the plugin directory of Cubetto Toolset (Cubetto Toolset/plugins) to be activated. If it has to be available to all computer users, the plugin needs to be saved into the plugin folder of the Cubetto installation, otherwise it can be saved in the home directory (~/cubetto.workspace/plugins).

Eclipse enables an easy deployment by exporting the compiled classes as .jar-file:

Plugin deployment in Eclipse

Figure 18.3. Plugin deployment in Eclipse

18.2.7. Validity check

The plugin can be tested very easily. The menu and toolbar items are displayed when Cubetto Toolset is started. A click on one of the items triggers the execution of the plugin action.

Menu item created by the plugin

Figure 18.4. Menu item created by the plugin

Display of the plugin

Figure 18.5. Display of the plugin