This tutorial will introduce another kind of concrete syntax for your models, namely a graphical syntax. Instead of writing models in a textual format, they are created by dragging elements from a palette into a two-dimensional plane. The framework we will employ here is Graphiti, which is based on the Graphical Editing Framework.
The main documentation of Graphiti is found in the Eclipse online help, which is also found in the Eclipse application (Help → Help Contents). If you don't have Graphiti yet, install it from the Juno release update site, Modeling category. The first step of this tutorial consists of defining a diagram type for Turing Machines and adding a wizard dialog for the creation of new diagrams.
TuringDiagramTypeProvider
with superclass org.eclipse.graphiti.dt.AbstractDiagramTypeProvider
.TuringDiagramTypeProvider
classTuringFeatureProvider
with superclass org.eclipse.graphiti.ui.features.DefaultFeatureProvider
.Add the following constructor to TuringDiagramTypeProvider
:
/** * Create a Turing diagram type provider. */ public TuringDiagramTypeProvider() { setFeatureProvider(new TuringFeatureProvider(this)); } |
GraphitiNewWizard
for specifying a concrete wizard dialog for your models.Add a constructor that calls a super-constructor with according parameters for configuration:
super("Turing Machine", "tudi", "turing", "turing", org.eclipse.graphiti.ui.editor.DiagramEditor.DIAGRAM_EDITOR_ID); |
Diagrams are stored in two separate files, one containing the actual Turing Machine model and one containing the specific graphical elements used to represent the model. Here it is assumed that "turing"
is the file extension for Turing Machine models (this depends on how you configured your EMF model), while "tudi"
will be the file extension for diagrams. Hint: by selecting "tuxt"
as domain model file extension your models will be stored in the textual format instead of XMI. However, this requires the created models to always be in a serializable state.
createModel
method by creating and returning an instance of the top-level element of your Turing Machines, e.g. TuringMachine
.tudi
file, right-click it → Open With→ Other...→ Graphiti Diagram Editor. This setting for tudi
files will be saved in your workspace preferences.The next step is to write so-called features for creating and adding elements to the diagrams. Each type of graphical element requires a create feature for the creation of corresponding meta model (business model) elements, and an add feature for adding a specific graphical representation to the diagram. A graphical representation is modeled with a so-called pictogram element, which contains a structure of graphics algorithms that specify how the element is rendered.
Add the following feature class to your plugin, adapting references to meta model elements to your Turing Machine definition:
import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.ICreateContext; import org.eclipse.graphiti.features.impl.AbstractCreateFeature; import org.eclipse.graphiti.mm.pictograms.Diagram; import de.cau.cs.rtprak.login.turingmodel.State; import de.cau.cs.rtprak.login.turingmodel.TuringFactory; import de.cau.cs.rtprak.login.turingmodel.TuringMachine; /** * A create feature for Turing Machine states. * * @author msp */ public class StateCreateFeature extends AbstractCreateFeature { /** * Constructor for a state create feature. * * @param fp the feature provider for which the feature is created */ public StateCreateFeature(IFeatureProvider fp) { super(fp, "State", "Create a State"); } /** * {@inheritDoc} */ public boolean canCreate(ICreateContext context) { return context.getTargetContainer() instanceof Diagram; } /** * {@inheritDoc} */ public Object[] create(ICreateContext context) { // get the container business element List<EObject> containerObjects = context.getTargetContainer().getLink().getBusinessObjects(); if (containerObjects.isEmpty() || !(containerObjects.get(0) instanceof TuringMachine)) { throw new IllegalStateException("The diagram does not contain a Turing Machine."); } TuringMachine machine = (TuringMachine) containerObjects.get(0); // create a new state State state = TuringFactory.eINSTANCE.createState(); machine.getStates().add(state); // add the corresponding graphical representation addGraphicalRepresentation(context, state); return new Object[] { state }; } } |
Add the following method to TuringFeatureProvider
, defining the content of the diagram editor's palette:
/** * {@inheritDoc} */ @Override public ICreateFeature[] getCreateFeatures() { return new ICreateFeature[] { new StateCreateFeature(this) }; } |
Add the following feature class to your plugin and implement the add
method:
import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.IAddContext; import org.eclipse.graphiti.features.impl.AbstractAddShapeFeature; import org.eclipse.graphiti.mm.pictograms.ContainerShape; import org.eclipse.graphiti.mm.pictograms.Diagram; import org.eclipse.graphiti.mm.pictograms.PictogramElement; import org.eclipse.graphiti.services.Graphiti; import org.eclipse.graphiti.services.IGaService; import de.cau.cs.rtprak.login.turingmodel.State; /** * An add feature for Turing Machine states. * * @author msp */ public class StateAddFeature extends AbstractAddShapeFeature { /** * Constructor for a state add feature. * * @param fp the feature provider for which the feature is created */ public StateAddFeature(IFeatureProvider fp) { super(fp); } /** * {@inheritDoc} */ public boolean canAdd(IAddContext context) { return context.getNewObject() instanceof State && context.getTargetContainer() instanceof Diagram; } /** * {@inheritDoc} */ public PictogramElement add(IAddContext context) { // create a pictogram element for the state ContainerShape containerShape = Graphiti.getPeCreateService().createContainerShape( context.getTargetContainer(), true); // TODO specify the concrete representation by adding at least one graphics algorithm link(containerShape, context.getNewObject()); return containerShape; } } |
Use Graphiti.getGaService()
to get a service class for creating graphics algorithms and modifying their properties. You are free to design your model elements as you like. Find more information on how to solve this task in the official Graphiti Tutorial.
Add the following method to TuringFeatureProvider
:
private IAddFeature stateAddFeature = new StateAddFeature(this); /** * {@inheritDoc} */ @Override public IAddFeature getAddFeature(IAddContext context) { if (stateAddFeature.canAdd(context)) { return stateAddFeature; } return super.getAddFeature(context); } |