Difference between revisions of "Extending the Rodin Event-B Explorer (How to extend Rodin Tutorial)"

From Event-B
Jump to navigationJump to search
imported>Tommy
m (New page: {{Navigation|Previous= Extending_the_Rodin_Structured_Editor_(How_to_extend_Rodin_Tutorial) | Up= How to extend Rodin Tutorial (Index) | Next= }} === In this part...)
 
imported>Pascal
m
 
(17 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{Navigation|Previous= [[Extending_the_Rodin_Structured_Editor_(How_to_extend_Rodin_Tutorial)]] | Up= [[Plug-in_Tutorial|How to extend Rodin Tutorial (Index)]] | Next= }}
+
{{Navigation|Previous= [[Extending_the_Rodin_Structured_Editor_(How_to_extend_Rodin_Tutorial)|Extending the Rodin Structured Editor]]| Next=[[Extending_Rodin_Pretty_Print_Page(How_to_extend_Rodin_Tutorial)|Displaying added elements in the Pretty Print Page]] | Up= [[Plug-in_Tutorial|How to extend Rodin Tutorial (Index)]]}}
  
 
=== In this part ===
 
=== In this part ===
  
We will continue our work on the UI integration of our plug-in and extend the Event-B Explorer view. This view provides a view of the structure of Event-B projects, represented as a tree. As we introduced a new element "''Bound''", we want it to appear in this view as any other machine element such as ''variables'' or ''invariants''.
+
We continue our work on the UI integration of our plug-in and extend the Event-B Explorer view. This view allows to display the structure of Event-B projects, represented as a tree. As we introduced a new element "''Bound''", we want it to appear in this view as any other machine element such as ''variables'' or ''invariants''.
The Event-B Explorer uses the Common Navigator Framework (extension point <tt>org.eclipse.ui.navigator</tt>), thus we will not use a Rodin extension point but directly eclipse extension points.
+
The Event-B Explorer uses the Common Navigator Framework (extension point <tt>org.eclipse.ui.navigator</tt>), thus we will not use a Rodin extension point but directly Eclipse extension points.
  
 
''What we want to get :''
 
''What we want to get :''
We want our bound element to appear in the Event-B Explorer view as a direct child (as there will be only one bound element per machine), as described in the picture below:
+
We want our Bound element to appear in the Event-B Explorer view as a direct child (as there will be a single bound element per machine), as described in the picture below:
  
 
[[Image:Extend_Rodin_Tuto_1_9_Bound_in_EventB_Explorer.png]]
 
[[Image:Extend_Rodin_Tuto_1_9_Bound_in_EventB_Explorer.png]]
  
First we will create Bound element contents to fill the navigator view with, and then we will bind those contents to the navigator view.
+
First, we will create Bound element contents to fill the navigator view with, and then we will bind those contents to the navigator view.
  
 
=== Step 1 ===
 
=== Step 1 ===
  
Add a dependency to the package <tt>org.eclipse.ui.navigator</tt>.<br>
+
Add a dependency to the package <tt>org.eclipse.ui.navigator</tt> (This is the required package to use the navigator extension points).<br>
Add the extension point <tt>org.eclipse.ui.navigator.navigatorContent</tt>.
+
Extend <tt>org.eclipse.ui.navigator.navigatorContent</tt> (This extension point allows to extend the navigator by adding contents to it).
  
 
=== Step2 ===
 
=== Step2 ===
Add a new <tt>navigatorContent</tt> extension using the newly added extension point <tt>org.eclipse.ui.navigator.navigatorContent</tt>.
+
Add a new <tt>navigatorContent</tt> extension by using the newly added extension point <tt>org.eclipse.ui.navigator.navigatorContent</tt>.
In a first time, we will add 2 clause expressions to the <tt>navigatorContent</tt> extension to explain in which context this content shall be displayed and what to display (''item A''). In a second time, we will give contents to this extension (''item B''). The two clauses expressions we will use from <tt>navigatorContent</tt> extension are :
+
In this step, we will add two clause expressions to the <tt>navigatorContent</tt> extension to explain in which context this content shall be displayed and what to display. In the following step, we will give contents to this extension. The two clause expressions that we will use from <tt>navigatorContent</tt> extension are:
 
:- ''triggerPoints''. The <tt>triggerPoints</tt> expression defines the nodes to be used when resolving the Common Navigator content provider's <tt>getElements()</tt> or <tt>getChildren()</tt>. This is also used to select the extension for providing labels, images, descriptions and for sorting.
 
:- ''triggerPoints''. The <tt>triggerPoints</tt> expression defines the nodes to be used when resolving the Common Navigator content provider's <tt>getElements()</tt> or <tt>getChildren()</tt>. This is also used to select the extension for providing labels, images, descriptions and for sorting.
  
:- ''possibleChildren''. The <tt>possibleChildren</tt> expression defines the node to be used when resolving the Common Navigator content provider's <tt>getParent()</tt>. It is also used to help determine the parent when handling drop operations.
+
:- ''possibleChildren''. The <tt>possibleChildren</tt> expression defines the nodes to be used when resolving the Common Navigator content provider's <tt>getParent()</tt>. It is also used to help to determine the parent when handling drop operations.
  
''(A)'' First, add a clause ''triggerPoints'' to the <tt>navigatorContent</tt> extension, and add  
+
We want the navigator to display the Bound element when expanding a machine structure. Hence, we need to add a trigger calling a content provider associated to the Bound element. To do this, first, add a clause ''triggerPoints'' to the <tt>navigatorContent</tt> extension, and in this clause add a clause <tt>instanceof</tt>. Then fill the ''value'' field of the <tt>instanceof</tt> clause with <tt>org.eventb.core.IMachineRoot</tt>. By doing this, we tell the navigator to call a Bound element content provider when we want to display the children element of a machine.
  
 +
Moreover, we expect Bound elements to be displayed at the top level in the Event-B explorer tree (where appears the variables "group" node, or invariants "group" node). This means that possible children for the node to be used by the navigator are Bound elements themselves. Add a clause ''possibleChildren'' to the <tt>navigatorContent</tt> extension, and add the following <tt>instanceof</tt> clause <tt>fr.systerel.rodinextension.sample.basis.IBound</tt>.
  
 +
=== Step 3 ===
 +
Now let's fill the contents of our extension <tt>navigatorContent</tt> for the Bound element.
  
 +
[[Image:Extend_Rodin_Tuto_1_10_boundContentExtension.png|400px]]
  
 +
1. Give a unique ID for the content extension (here we use <tt>fr.systerel.rodinextension.sample.ui.boundContent</tt>).<br>
 +
2. Give a readable name for this extension of the navigator (such as "Bound Element" or "Bound Element Navigator Node").<br>
 +
3. The field ''priority'' is linked to the field ''appearsBefore''. These two fields allow to define where our content extension should take place in the tree structure. We used here the lower priority, to avoid to display the Bound element at the top of the machine children hierarchy.<br>
 +
4. In ''appearsBefore'', we put the <tt>fr.systerel.explorer.navigator.content.event</tt> which is the identifier of the content extension for events (To examine the core extensions for the contents of the navigator, go to the <tt>fr.systerel.explorer</tt> plug-in and look at <tt>navigatorContent</tt> extensions).<br>
 +
5. Define a new class for the content provider, implementing the interface <tt>ITreeContentProvider</tt>, to be used to display the Bound element (using Eclipse's "new java class wizard").<br>
 +
We have created one, named <tt>BoundContentProvider</tt>, and implemented as follows :
  
 +
public class BoundContentProvider implements ITreeContentProvider {
 +
 +
@Override
 +
public void dispose() {
 +
// ignore
 +
}
 +
 +
@Override
 +
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
 +
// ignore
 +
}
 +
 +
@Override
 +
public Object[] getElements(Object inputElement) {
 +
return getChildren(inputElement);
 +
}
 +
 +
@Override
 +
public Object[] getChildren(Object parent) {
 +
Object[] objects = new Object[0];
 +
if (parent instanceof IMachineRoot) {
 +
try {
 +
objects = ((IMachineRoot)parent).getChildrenOfType(
 +
IBound.ELEMENT_TYPE);
 +
} catch (RodinDBException e) {
 +
e.printStackTrace();
 +
}
 +
}
 +
return objects;
 +
}
 +
 +
@Override
 +
public Object getParent(Object element) {
 +
if (element instanceof IBound) {
 +
((IBound) element).getParent();
 +
}
 +
return null;
 +
}
 +
 +
@Override
 +
public boolean hasChildren(Object element) {
 +
return getChildren(element).length > 0;
 +
}
 +
 +
}
  
 +
This content provider will be called when fetching <tt>IMachineRoot</tt> elements to provide the corresponding contents to be computed for our <tt>Bound</tt> element.<br>
 +
As we want the <tt>Bound</tt> elements to appears directly in the tree (e.g. we do not want to introduce a intermediary tree node for the <tt>Bound</tt> elements), the method <tt>getElements(Object inputElement)</tt> shall then return the <tt>Bound</tt> elements themselves. This is done via the method <tt>getChildren(Object parent)</tt>.<br>
 +
In <tt>getChildren(Object parent)</tt>, we check that the parent element is an <tt>IMachineRoot</tt> and we return all <tt>IBound</tt> elements. This is done by calling the <tt>getChildrenOfType()</tt> method available on all <tt>IInternalElement</tt>s (i.e. <tt>IInternalElement</tt> extends the interface <tt>IParent</tt>).<br>
 +
The method <tt>getParent(Object element)</tt> should check if the given element is an <tt>IBound</tt> and then return the its parent, or <tt>null</tt> if it's not the case.<br>
 +
The method <tt>hasChildren(Object element)</tt> returns <tt>true</tt> if there is <tt>Bound</tt> children (use <tt>getChildren()</tt>), and return <tt>false</tt> otherwise.<br>
  
 +
This is all what we have to do to provide contents for our Bound elements. Now let's define how they will be displayed in the tree.
  
 +
6. Now we need to provide labels:
 +
public class BoundLabelProvider implements ILabelProvider, IImageProvider {
 +
 +
private static final String BOUND_LABEL = "Bound";
 +
 +
@Override
 +
public Image getImage(Object element) {
 +
                if (element instanceof IBound) {
 +
return getImage();
 +
}
 +
                return null;
 +
}
 +
 +
public static Image getImage() {
 +
final ImageRegistry reg = EventBUIPlugin.getDefault()
 +
.getImageRegistry();
 +
return reg.get(IEventBSharedImages.IMG_CONSTANT);
 +
}
 +
 +
@Override
 +
public String getText(Object element) {
 +
if (element instanceof IBound) {
 +
return BOUND_LABEL;
 +
}
 +
return null;
 +
}
 +
 +
@Override
 +
public ImageDescriptor getImageDescriptor(IRodinElement element) {
 +
final Image image = getImage(element);
 +
if (image == null) {
 +
return null;
 +
}
 +
final ImageDescriptor desc = ImageDescriptor.''createFromImage''(image);
 +
return desc;
 +
}
 +
 
 +
    // other methods can be ignored...
 +
 +
}
  
{{Navigation|Previous= [[Extending_the_Rodin_Structured_Editor_(How_to_extend_Rodin_Tutorial)]] | Up= [[Plug-in_Tutorial|How to extend Rodin Tutorial (Index)]] | Next= }}
+
Methods <tt>getImage(Object element)</tt> and <tt>getText(Object element)</tt> need to be implemented from the <tt>ILabelProvider</tt> interface.<br>
 +
In <tt>getImage(Object element)</tt>, we return the icon registered for constants, if the element is an instance of <tt>IBound</tt> (see <tt>org.eventb.ui.IEventBSharedImages</tt> to get the ID of shared images in Rodin).<br>
 +
In <tt>getText(Object element)</tt>, we return the <tt>BOUND_LABEL</tt> string constant for <tt>IBound</tt> instances, as <tt>IBound</tt> elements have no label, and there is only one bound element per machine. The corresponding text "Bound" will then appear next to the ''Bound'' element in the explorer tree.
  
 +
Method <tt>getImageDescriptor(IRodinElement element)</tt> need to be implemented from the interface <tt>IImageProvider</tt>.<br>
 +
We simply create a fresh descriptor using the static method <tt>ImageDescriptor.createFromImage(Image image)</tt> from the image that we get from <tt>getImage(Object element)</tt>.
 +
 +
The other methods can be ignored.
 +
 +
----
 +
 +
That's all! You are now able to view ''Bound'' elements in the Event-B Explorer view.
 +
 +
{{Navigation|Previous= [[Extending_the_Rodin_Structured_Editor_(How_to_extend_Rodin_Tutorial)|Extending the Rodin Structured Editor]]| Next=[[Extending_Rodin_Pretty_Print_Page(How_to_extend_Rodin_Tutorial)|Displaying added elements in the Pretty Print Page]]  | Up= [[Plug-in_Tutorial|How to extend Rodin Tutorial (Index)]]}}
  
 
[[Category:Developer documentation|*Index]]
 
[[Category:Developer documentation|*Index]]
 
[[Category:Rodin Platform|*Index]]
 
[[Category:Rodin Platform|*Index]]
 
[[Category:Tutorial|*Index]]
 
[[Category:Tutorial|*Index]]

Latest revision as of 07:15, 7 September 2010

In this part

We continue our work on the UI integration of our plug-in and extend the Event-B Explorer view. This view allows to display the structure of Event-B projects, represented as a tree. As we introduced a new element "Bound", we want it to appear in this view as any other machine element such as variables or invariants. The Event-B Explorer uses the Common Navigator Framework (extension point org.eclipse.ui.navigator), thus we will not use a Rodin extension point but directly Eclipse extension points.

What we want to get : We want our Bound element to appear in the Event-B Explorer view as a direct child (as there will be a single bound element per machine), as described in the picture below:

Extend Rodin Tuto 1 9 Bound in EventB Explorer.png

First, we will create Bound element contents to fill the navigator view with, and then we will bind those contents to the navigator view.

Step 1

Add a dependency to the package org.eclipse.ui.navigator (This is the required package to use the navigator extension points).
Extend org.eclipse.ui.navigator.navigatorContent (This extension point allows to extend the navigator by adding contents to it).

Step2

Add a new navigatorContent extension by using the newly added extension point org.eclipse.ui.navigator.navigatorContent. In this step, we will add two clause expressions to the navigatorContent extension to explain in which context this content shall be displayed and what to display. In the following step, we will give contents to this extension. The two clause expressions that we will use from navigatorContent extension are:

- triggerPoints. The triggerPoints expression defines the nodes to be used when resolving the Common Navigator content provider's getElements() or getChildren(). This is also used to select the extension for providing labels, images, descriptions and for sorting.
- possibleChildren. The possibleChildren expression defines the nodes to be used when resolving the Common Navigator content provider's getParent(). It is also used to help to determine the parent when handling drop operations.

We want the navigator to display the Bound element when expanding a machine structure. Hence, we need to add a trigger calling a content provider associated to the Bound element. To do this, first, add a clause triggerPoints to the navigatorContent extension, and in this clause add a clause instanceof. Then fill the value field of the instanceof clause with org.eventb.core.IMachineRoot. By doing this, we tell the navigator to call a Bound element content provider when we want to display the children element of a machine.

Moreover, we expect Bound elements to be displayed at the top level in the Event-B explorer tree (where appears the variables "group" node, or invariants "group" node). This means that possible children for the node to be used by the navigator are Bound elements themselves. Add a clause possibleChildren to the navigatorContent extension, and add the following instanceof clause fr.systerel.rodinextension.sample.basis.IBound.

Step 3

Now let's fill the contents of our extension navigatorContent for the Bound element.

Extend Rodin Tuto 1 10 boundContentExtension.png

1. Give a unique ID for the content extension (here we use fr.systerel.rodinextension.sample.ui.boundContent).
2. Give a readable name for this extension of the navigator (such as "Bound Element" or "Bound Element Navigator Node").
3. The field priority is linked to the field appearsBefore. These two fields allow to define where our content extension should take place in the tree structure. We used here the lower priority, to avoid to display the Bound element at the top of the machine children hierarchy.
4. In appearsBefore, we put the fr.systerel.explorer.navigator.content.event which is the identifier of the content extension for events (To examine the core extensions for the contents of the navigator, go to the fr.systerel.explorer plug-in and look at navigatorContent extensions).
5. Define a new class for the content provider, implementing the interface ITreeContentProvider, to be used to display the Bound element (using Eclipse's "new java class wizard").
We have created one, named BoundContentProvider, and implemented as follows :

public class BoundContentProvider implements ITreeContentProvider {

	@Override
	public void dispose() {
		// ignore
	}

	@Override
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		// ignore
	}

	@Override
	public Object[] getElements(Object inputElement) {
		return getChildren(inputElement);
	}

	@Override
	public Object[] getChildren(Object parent) {
		Object[] objects = new Object[0];
		if (parent instanceof IMachineRoot) {
			try {
				objects = ((IMachineRoot)parent).getChildrenOfType(
						IBound.ELEMENT_TYPE);
			} catch (RodinDBException e) {
				e.printStackTrace();
			}
		}
		return objects;
	}

	@Override
	public Object getParent(Object element) {
		if (element instanceof IBound) {
			((IBound) element).getParent();
		}
		return null;
	}

	@Override
	public boolean hasChildren(Object element) {
		return getChildren(element).length > 0;
	}

}

This content provider will be called when fetching IMachineRoot elements to provide the corresponding contents to be computed for our Bound element.
As we want the Bound elements to appears directly in the tree (e.g. we do not want to introduce a intermediary tree node for the Bound elements), the method getElements(Object inputElement) shall then return the Bound elements themselves. This is done via the method getChildren(Object parent).
In getChildren(Object parent), we check that the parent element is an IMachineRoot and we return all IBound elements. This is done by calling the getChildrenOfType() method available on all IInternalElements (i.e. IInternalElement extends the interface IParent).
The method getParent(Object element) should check if the given element is an IBound and then return the its parent, or null if it's not the case.
The method hasChildren(Object element) returns true if there is Bound children (use getChildren()), and return false otherwise.

This is all what we have to do to provide contents for our Bound elements. Now let's define how they will be displayed in the tree.

6. Now we need to provide labels:

public class BoundLabelProvider implements ILabelProvider, IImageProvider {

	private static final String BOUND_LABEL = "Bound";

	@Override
	public Image getImage(Object element) {
               if (element instanceof IBound) {
			return getImage();
		}
               return null;
	}

	public static Image getImage() {
		final ImageRegistry reg = EventBUIPlugin.getDefault()
				.getImageRegistry();
		return reg.get(IEventBSharedImages.IMG_CONSTANT);
	}

	@Override
	public String getText(Object element) {
		if (element instanceof IBound) {
			return BOUND_LABEL;
		}
		return null;
	}

	@Override
	public ImageDescriptor getImageDescriptor(IRodinElement element) {
		final Image image = getImage(element);
		if (image == null) {
			return null;
		}
		final ImageDescriptor desc = ImageDescriptor.createFromImage(image);
		return desc;
	}
 
   // other methods can be ignored...

}

Methods getImage(Object element) and getText(Object element) need to be implemented from the ILabelProvider interface.
In getImage(Object element), we return the icon registered for constants, if the element is an instance of IBound (see org.eventb.ui.IEventBSharedImages to get the ID of shared images in Rodin).
In getText(Object element), we return the BOUND_LABEL string constant for IBound instances, as IBound elements have no label, and there is only one bound element per machine. The corresponding text "Bound" will then appear next to the Bound element in the explorer tree.

Method getImageDescriptor(IRodinElement element) need to be implemented from the interface IImageProvider.
We simply create a fresh descriptor using the static method ImageDescriptor.createFromImage(Image image) from the image that we get from getImage(Object element).

The other methods can be ignored.


That's all! You are now able to view Bound elements in the Event-B Explorer view.