Jump to: navigation, search

Table Developer Documentation

Contents

General Presentation

The Papyrus Table uses the NatTable widget. Its documentation is available here and here. This kind of table is available since the Papyrus Release 0.10 in June 2013. The NatTable source code can be downloaded with git (http://git.eclipse.org/gitroot/nattable/org.eclipse.nebula.widgets.nattable.git).

Before continuing reading this document, we advise you to read the User documentation here. In particular, you should pay special attention to the next points:

Papyrus_User_Guide/Table_Documentation#Glossary
Papyrus_User_Guide/Table_Documentation#Table areas
Papyrus_User_Guide/Table_Documentation#General presentation

Papyrus provides tables to edit features of UML Elements. In general, UML Elements are displayed in rows and their features are displayed in columns. These tables can be flat or hierarchical, and they can be filled by the user or synchronized with the context of the table. The Papyrus integration should allow creating a Matrix, but Papyrus does not provide Matrix editors yet.

The Papyrus team created the nattable.ecore metamodel to describe the Tabular editor and to store it. This metamodel (also known as "the table metamodel") is provided by the plugin org.eclipse.papyrus.infra.nattable.model and declares all the elements used to manipulate the table concept. The next simplified diagram shows some key elements and relationships:

PartOfPapyrusNattableMM.png

  1. NamedStyle
    • Named object to store information. Implementations of this element allow you to store boolean, int, list, string, etc. It works is the same way than in the GMF notation metamodel used by the Papyrus Diagram.
  2. StyledElement
    • Abstract element that allows to reference style NamedStyle. Each element provided by the table metamodel must inherit from this one, so developers can add easily information to element, without modifying the table metamodel.
  3. TableConfiguration
    • Element that contains all the information required to create a table. The user can't modify it.
  4. Table
    • Element that is created from a TableConfiguration. Users can modify it using the tabular editor. It always references a TableConfiguration.
  5. IAxis
    • Abstract object used to reference elements represented by columns and rows.

How to create your own table configuration to edit features of UML Elements

Introduction

This section describes how to create types of tables. We assume that developers have already a good knowledge of the EMF framework. During this tutorial the developer could use any of the two main editors:

  • The nattableconfiguration Model Editor (provided by Papyrus)
  • The Sample Reflective Model Editor, provided by EMF.

We advice to use the Sample Reflective Model Editor because it allows you the load the UML Resource http://www.eclipse.org/uml2/5.0.0/UML which is required to create a new table configuration. However, there are two known problems with EMF editors that developers should keep in mind:

  • Often, we don't have enough information in the Property View to edit reference, because the label doesn’t us give enough information to distinguish element. In the following tutorial you will need to create several instances of the same object. The only way to be sure of referencing the right one is to open the file with the text editor.
  • The path of referenced element coming from external resource is ugly, for example, the EMF Editor will serialize (look at the href ):
<ownedAxisConfigurations xsi:type="nattableaxisconfiguration:EStructuralFeatureValueFillingConfiguration">
         <listenFeature xsi:type="ecore:EReference" href="../../plugin/org.eclipse.uml2.uml/model/UML.ecore#//Namespace/ownedMember"/>
</ownedAxisConfigurations>

instead of

<ownedAxisConfigurations xsi:type="nattableaxisconfiguration:EStructuralFeatureValueFillingConfiguration">
         <listenFeature xsi:type="ecore:EReference" href="http://www.eclipse.org/uml2/5.0.0/UML#//Namespace/ownedMember"/>
</ownedAxisConfigurations>

so you should think about checking the file in a Text Editor.

Goal

The aim of this topic is to provide a tutorial for customizing a UML table within Papyrus. The Papyrus Table can be synchronized or not and can be flat or hierarchical. In this tutorial, we will describe the creation of these 3 kinds of tables to edit SysML Block:

  • A not synchronized table, so filled by the user, called UMLDnDBlockTable in this documentation
    • This table will only accept Class stereotyped by SysML Block
    • Its allowed context will be Package
    • 3 columns will be provided: name and visibility (UML features) and isEncapsulated (SysML feature)
  • A synchronized table, called UMLSynchronizedBlockTable in this documentation
    • This table will only accept Class stereotype by Block
    • Its allowed context will be Package
    • 3 columns will be provided: name and visibility (UML features) and isEncapsulated (SysML feature)
  • A tree table, called UMLSynchronizedBlockTreeTable in this documentation
    • This table will only accepts Class as root elements
    • Its allowed context will be Package
    • 3 columns will be provided: name and visibility (UML features) and isEncapsulated (SysML feature)

Create a new Table Configuration

For this step, first of all, you must to create a new Eclipse Plugin Project, called org.eclipse.papyrus.sysml.nattable.block for this example. In this one, you have to create a new Nattableconfiguration Model file.

As this tutorial is done to create three kinds of tables, please, create three files:

  • UMLDnDBlockTable : create the file UMLDnDBlockTable.nattableconfiguration
  • UMLSynchronizedBlockTable : create the file UMLSynchronizedBlockTable.nattableconfiguration
  • UMLSynchronizedBlockTreeTable : create the file UMLSynchronizedBlockTreeTable.nattableconfiguration

One way to create these files is to use a creation wizard.

  • Select from the Eclipse menu: "File" > "New" > "Other..."
  • Select "Example EMF Model Creation Wizards" and "nattableconfiguration Model". Click "Next".
  • Select your empty plugin "org.eclipse.papyrus.sysml.nattable.block" and write the name of the file that contains your new model. The file should must have the extension "nattableconfiguration" (e.g., you can write "UMLDnDBlockTable.nattableconfiguration"). Click "Finish".

Set the kind of table (Flat or Tree)

You must declare a Table Display Style as child of the Table Configuration.

ChooseTableDisplayStyle.png

  • UMLSynchronizedBlockTreeTable : it is a tree table, so the value must be HIERARCHIC_SINGLE_TREE_COLUMN to display hierarchy on a single column or HIERARCHIC_MULTI_TREE_COLUMN to display the hierarchy on several columns
  • UMLSynchronizedBlockTable and UMLDnDBlockTable  : they are Flat Table, so the value must be NORMAL (if no display style is defined, the configuration will be interpreted than a NORMAL table.


Define the allowed creation context of the table

    1. You must declare a Java Table Tester as as child of the Table Configuration to filter the allowed context to create a table.
      Note: Since the implementation of the viewpoint, the tester is not yet called, but it could be better to continue to provide it!

      Click with the right mouse button on the Table Configuration item and create a New Child of type Java Table Tester.
      BlockNattableconfiguration.png

    2. Once added, define its properties:
      JavaTableTester.png
      • id : this is a string identifying your java tester. Here decided to call it org.eclipse.papyrus.sysml.nattable.block.tester.

      Do these two first steps for each table that we are creating: UMLDnDBlockTable, UMLSynchronizedBlockTable and UMLSynchronizedBlockTreeTable.

    3. Create a java class named BlockTableTester implementing org.eclipse.papyrus.infra.nattable.tester.ITableTester. If you have an 'unresolvable reference' error, add 'org.eclipse.papyrus.infra.nattable' to the list of required JARs and class folders in the Java build path or your plugin.
       
       
      /*****************************************************************************
       * Copyright (c) 2013 CEA LIST.
       *
       *
       * All rights reserved. This program and the accompanying materials
       * are made available under the terms of the Eclipse Public License v1.0
       * which accompanies this distribution, and is available at
       * http://www.eclipse.org/legal/epl-v10.html
       *
       * Contributors:
       *  Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
       *
       *****************************************************************************/
      package org.eclipse.papyrus.sysml.nattable.block.tester;
       
      import org.eclipse.core.runtime.IStatus;
      import org.eclipse.core.runtime.Status;
      import org.eclipse.papyrus.infra.nattable.tester.ITableTester;
      import org.eclipse.papyrus.sysml.blocks.BlocksPackage;
      import org.eclipse.papyrus.sysml.nattable.block.Activator;
      import org.eclipse.uml2.uml.Element;
      import org.eclipse.uml2.uml.Package;
      import org.eclipse.uml2.uml.util.UMLUtil;
       
      /**
       * The tester used to know if we can create block table
       *
       * @author Vincent Lorenzo
       *
       */
      public class BlockTableTester implements ITableTester {
       
      	public IStatus isAllowed(Object context) {
      		if (context instanceof Element) {
      			Element el = (Element) context;
      			boolean result = context instanceof Package ;
      			if (result) {
      				final String packageQN = UMLUtil.getProfile(BlocksPackage.eINSTANCE, el).getQualifiedName();
      				result = result && el.getNearestPackage().getAppliedProfile(packageQN, true) != null;
      				if (result) {
      					return new Status(IStatus.OK, Activator.PLUGIN_ID, "The context allows to create a Block Table");
      				} else {
      					return new Status(IStatus.ERROR, Activator.PLUGIN_ID, String.format("The profile {0} is not applied on the model", packageQN));
      				}
      			}
      		}
      		return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "The context is not an UML Element");
      	}
       
      }
    4. Register this class in the file plugin.xml of your plugin project.
      DeclarePropertyTesterInPLuginXML.png.png
      • id : the same id than you declared for the Java table Tester in the nattableconfiguration field.
      • class : the qualified name of your BlockTableTester

Define Row Header Axis Configuration

Create Row Header Axis Configuration

This object allows to:

  • define the row header appearance
  • store the configuration to use to manage rows

Click with the right mouse button on the Table Configuration item and create a New Child of type Table Header Axis Configuration. Once added, define its properties:

RowTableHeaderAxisConfiguration.png

  • Display Filter : value ignored for rows.
  • Display Index : the Row Index Header, will be hidden if true..
  • Display Label : the Row Label Header will be hidden if true..
  • Index Style : Alphabetic or Numeric.

We often use Numeric for Rows (and Alphabetic for Columns).

Define Label Provider Configuration for Row Label

This object provides the configuration used to display Row Label in the Row Header Label Area. As the rows represent edited object, we will use an Object Label Provider Configuration.

Click with the right mouse button on your new created Table Header Axis Configuration item and create a New Child of type Object Label Provider Configuration. Once added, define its properties:

ObjectLabelProviderConfiguration.png

  • Display Icon : it true, an icon will be displayed at the left of the label in the Row Label Header area
  • Display Label : if true, the label of the row will be displayed in the Row Label Header area

Add specific configurations

This is in this part than we will configure the paste and the synchronization of our tables.

Define the Paste Configuration

Create a new Paste EObject Configuration as children of your Table Header Axis Configuration. Once created define its properties

PasteBlockConfiguration.png
  • Detached Mode : if true :
    • the paste will be done ignoring the Papyrus Edit Service
    • less safe
    • quicker
  • Pasted Element Id : the Papyrus id used to described the pasted element, here to paste SysML Block:
    • org.eclipse.papyrus.sysml.Block if detached mode=false (if true, in fact, we will paste org.eclipse.papyrus.uml.Class)
    • org.eclipse.papyrus.uml.Class if detached mode = true (in fact org.eclipse.papyrus.sysml.Block will be interpreted as org.eclipse.papyrus.uml.Class)
  • Paste Element Containment Feature : define the feature of the context of the table which will own the created element. Set it to http://www.eclipse.org/uml2/4.0.0/UML#//Namespace/ownedMember
  • Post Actions : actions to do after the paste, like applied required stereotypes
    • if detached mode=false, there is nothing to do
    • if detached mode=true, a post action is required to apply the stereotype Block on the created elements. Set the value to applyStereotype:/SysML::Blocks::Block to apply it.
Paste Configuration for UMLDnDBlockTable and UMLSynchronizedBlockTable

There is nothing to add to the previous description. You should do it in the 2 nattableconfiguration file of these tables

Paste Configuration for UMLSynchronizedBlockTreeTable

The previous description will works fine in a Tree Table but with 2 restrictions :

  • It define the paste only for root element of the tree table
  • It will be used only if your table is filled by DnD by the User.

So you need to complete the Table Header Axis Configuration in your file UMLSynchronizedBlockTreeTable.nattableconfiguration. You must create a new Paste Eobject Configuration for each category listen by the table. The configuration previously created to paste Block could be reused (but you can also duplicate it in your file), so you need to create 2 others Paste Eobject Configuration.

Set these values to paste UML Operation:

Set these values to paste UML Parameter:

Synchronization

In this section, we will describe how to configure the synchronization of your table with the context of the table

UMLDnDBlockTable

There is nothing to do, it is not a synchronized table.

UMLSynchronizedBlockTable

We need to provide a FillingConfiguration to know which feature to listen to fill the table. You must create an EStructural Feature Value Filling Configuration as child of the Table Header Axis Configuration
EStructuralFeatureValueFillingConfiguration.PNG

UMLSynchronizedBlockTreeTable

The Tree table listen several features (at least one be depth). Here, we call these features categories.

    1. Firstly, you need to declare an additional label configuration : a Feature Label Configuration. This one will be used to configure the display of the categories in the tree.
      Of course, you could also create an Object Filling Configuration instead, but you get less possibility of configuration. Moreover, you can create a Feature Filling Configuration for each Tree Filling Configuration than you will create in in the next steps.
      Create a Feature Label provider Configuration as child of Table Header Axis Configuration, then edit these properties:
      FeaturelabelconfigurationForCategories.PNG
      • Display Icon : if true, an icon will be displayed at the left of the label for the category
      • Display Is Derived : if truea / will be displayed in the label if the represented feature is derived
      • Display Label : if true, the label will be displayed
      • Display Multiplicity, if true, the multiplicity will be displayed for the category in the label
      • Display Name : if true, the name of the category will be displayed in the label
      • Display Type : if true, the type of the category will be display in the label
    2. Now you need to create 3 Tree Filling Configuration, one by categories. So you need to create one with depth=0, one with depth=1 and one with depth=2.
      TreeFillingConfiguration.PNG
      • Depth
        • the depth on which the configuration is applied
        • starts to 0, if there is no configuration declared with depth=0, the root of the tree table will be filled by DnD by the user
      • Label provider : references a Label Provider Configuration declared as children of Row Header Axis Configuration. This label provider will be used to display the categories. Here, you must reference the Feature Label Provider Configuration that you came to define.
      • Label provider context : org.eclipse.papyrus.infra.nattable.header.treefilling.feature.labelprovider
      • Paste Configuration : references the paste configuration to use to paste element in this category
    3. Now you need to define the category (the feature) which is listen by each of these Tree Filling Configurations. You must create an IAxis as child of each configuration. For this example, the IAxis must be an EStructural Feature Axis.
      TreeFillingConfigurationIAxis.PNG
      • Alias : a string which will be displayed instead of the name of the referenced feature in the label of the categories
      • Element : the feature to listen
      • Manager : keep null for this usage
      You should create one EStructural Feature Axis as child of each Tree Filling Configuration' Once the Axis has been created, set these properties:

Define the Row Manager

Now you need to create an Axis Manager Representation for Rows. You can declare one or several axis manager representation. This element allows to declare the java class to used to manage rows in the table. Moreover, it references the configuration to use for the rows and declare the context of the label provider. Created an Axis Manager Representation item as child of your Table Header Axis Configuration. Once added, define its properties:

RowAxisManagerRepresentation.PNG

  • Axis Manager Id: define the id of the java class which will manage your rows
  • Header Label Configuration : the label configuration to use in the Row header Label area.
  • Label Provider context : the id of the label provider context to use
  • Specific Axis Configuration : to reference all others required configurations for your axis


UMLDnDBlockTable

    1. Axis Manager Id: org.eclipse.papyrus.sysml.nattable.BlockAxisManager
    2. Header Label Configuration: Your must reference your Object Label Configuration
    3. Label Provider context: org.eclipse.papyrus.infra.nattable.header.labelprovider
    4. Specific Axis Configuration: you must reference your Paste EObject Configuration

Now, you need to create a java class called for example BlockAxisManager. This axis manager must implement org.eclipse.papyrus.infra.nattable.manager.axis.IAxisManager, but we advice you to extends org.eclipse.papyrus.uml.nattable.manager.axis.UMLElementAxisManager.

/*****************************************************************************
 * Copyright (c) 2013 CEA LIST.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.sysml.nattable.block.manager.axis;
 
import org.eclipse.emf.ecore.EClass;
import org.eclipse.gmf.runtime.emf.type.core.IElementMatcher;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.ISpecializationType;
import org.eclipse.papyrus.uml.nattable.manager.axis.UMLElementAxisManager;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.papyrus.sysml.service.types.element.SysMLElementTypes;
import org.eclipse.papyrus.sysml.service.types.matcher.BlockMatcher;
 
public class BlockAxisManager extends UMLElementAxisManager{
 
	private static final IElementMatcher matcher = new BlockMatcher();
 
	/**
	 *
	 * @see org.eclipse.papyrus.infra.emf.nattable.manager.axis.EObjectAxisManager#isAllowedContents(java.lang.Object)
	 *
	 * @param object
	 * @return
	 */
	@Override
	public boolean isAllowedContents(Object object) {
 
		boolean res = super.isAllowedContents(object);//check if object is UML Element
		if(res){
			res = matcher.matches((Element)object);
		}
		return res;
	}
 
	/**
	 *
	 * @see org.eclipse.papyrus.infra.nattable.manager.axis.AbstractAxisManager#canCreateAxisElement(java.lang.String)
	 *
	 * @param elementId
	 * @return
	 */
	@Override
	public boolean canCreateAxisElement(String elementId) {
		return ((ISpecializationType) SysMLElementTypes.BLOCK).getId().equals(elementId);//only blocks can be created in this table
	}
 
}

Then you should register this class in your plugin.xml file using the extension point : org.eclipse.papyrus.infra.nattable.axismanager.

RegisterAxisManager.PNG

  • id : org.eclipse.papyrus.sysml.nattable.BlockAxisManager . It must be the same id than in the Axis Manager Representation
  • manager : The qualified Name of your created class

UMLSynchronizedBlockTable

    1. Axis Manager Id: org.eclipse.papyrus.sysml.nattable.BlockSynchronizedAxisManager
    2. Header Label Configuration: Your must reference your Object Label Configuration
    3. Label Provider context: org.eclipse.papyrus.infra.nattable.header.labelprovider
    4. Specific Axis Configuration: You must reference your EStructural Feature value Configuration and your Paste EObject Configuration.

Now, you need to create a java class called for example BlockSynchronizedAxisManager. This axis manager must implement org.eclipse.papyrus.infra.nattable.manager.axis.IAxisManager, but we advice you to extends org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager<T_StereotypeApplication>.

/*****************************************************************************
 * Copyright (c) 2013 CEA LIST.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.sysml.nattable.block.manager.axis;
 
import org.eclipse.gmf.runtime.emf.type.core.ISpecializationType;
import org.eclipse.papyrus.sysml.blocks.Block;
import org.eclipse.papyrus.sysml.blocks.BlocksPackage;
import org.eclipse.papyrus.sysml.service.types.element.SysMLElementTypes;
import org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.util.UMLUtil;
 
public class BlockSynchronizedAxisManager extends
		AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager<Block> {
 
	/**
	 *
	 * @see org.eclipse.papyrus.infra.nattable.manager.axis.AbstractAxisManager#canCreateAxisElement(java.lang.String)
	 *
	 * @param elementId
	 * @return
	 */
	@Override
	public boolean canCreateAxisElement(String elementId) {
		return ((ISpecializationType) SysMLElementTypes.BLOCK).getId().equals(elementId);
	}
 
 
	/**
	 *
	 * @see org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager#getStereotypeApplication(org.eclipse.uml2.uml.Element)
	 *
	 * @param el
	 * @return
	 */
	@Override
	protected Block getStereotypeApplication(Element el) {
		return UMLUtil.getStereotypeApplication(el, Block.class);
	}
 
	/**
	 *
	 * @see org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager#isInstanceOfRequiredStereotypeApplication(java.lang.Object)
	 *
	 * @param object
	 * @return
	 */
	@Override
	protected boolean isInstanceOfRequiredStereotypeApplication(final Object object) {
		return object instanceof Block;
	}
 
	/**
	 *
	 * @see org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager#getStereotypeBaseElement(org.eclipse.emf.ecore.EObject)
	 *
	 * @param stereotypeApplication
	 * @return
	 */
	@Override
	protected Element getStereotypeBaseElement(final Block stereotypeApplication) {
		return stereotypeApplication.getBase_Class();
	}
 
	/**
	 *
	 * @see org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager#isAllowedAsBaseElement(org.eclipse.uml2.uml.Element)
	 *
	 * @param element
	 * @return
	 */
	@Override
	protected boolean isAllowedAsBaseElement(final Element element) {
		return element instanceof  org.eclipse.uml2.uml.Class;
	}
 
 
	/**
	 *
	 * @see org.eclipse.papyrus.uml.nattable.manager.axis.AbstractStereotypedElementUMLSynchronizedOnFeatureAxisManager#getStereotypeApplicationBasePropertyName()
	 *
	 * @return
	 */
	@Override
	protected String getStereotypeApplicationBasePropertyName() {
		return BlocksPackage.eINSTANCE.getBlock_Base_Class().getName();
	}
}

Then you should register this class in your plugin.xml file using the extension point : org.eclipse.papyrus.infra.nattable.axismanager. See the description done for UMLDnDBlockTable

  • id : org.eclipse.papyrus.sysml.nattable.BlockSynchronizedAxisManager . It must be the same id than in the Axis Manager Representation
  • manager : The qualified Name of your created class

UMLSynchronizedBlockTreeTable

    1. Axis Manager Id: org.eclipse.papyrus.sysml.nattable.BlockTreeAxisManager
    2. Header Label Configuration: Your must reference your Object Label Configuration
    3. Label Provider context: org.eclipse.papyrus.infra.nattable.header.labelprovider
    4. Specific Axis Configuration : you must reference your Paste EObject Configuration allowing to paste Block and all your Tree Filling Configuration.

Now, you need to create a java class called for example BlockSynchronizedTreeAxisManager. This axis manager must implement oorg.eclipse.papyrus.infra.nattable.manager.axis.ITreeItemAxisManagerForEventList but we advice you to extend org.eclipse.papyrus.uml.nattable.manager.axis.UMLElementTreeAxisManagerForEventList

/*****************************************************************************
 * Copyright (c) 2013 CEA LIST.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.sysml.nattable.block.manager.axis;
 
import org.eclipse.gmf.runtime.emf.type.core.IElementMatcher;
import org.eclipse.papyrus.sysml.service.types.matcher.BlockMatcher;
import org.eclipse.papyrus.uml.nattable.manager.axis.UMLElementTreeAxisManagerForEventList;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattableaxisconfiguration.TreeFillingConfiguration;
 
public class BlockTreeAxisManagerForEventList extends
		UMLElementTreeAxisManagerForEventList {
 
	private static final IElementMatcher matcher = new BlockMatcher();
 
	/**
	 * 
	 * @see org.eclipse.papyrus.uml.nattable.manager.axis.UMLElementTreeAxisManagerForEventList#isAllowedContents(java.lang.Object,
	 *      java.lang.Object,
	 *      org.eclipse.papyrus.infra.nattable.model.nattable.nattableaxisconfiguration.TreeFillingConfiguration,
	 *      int)
	 *
	 * @param objectToTest
	 * @param semanticParent
	 * @param conf
	 * @param depth
	 * @return
	 */
	@Override
	public boolean isAllowedContents(Object objectToTest,
			Object semanticParent, TreeFillingConfiguration conf, int depth) {
		if (objectToTest instanceof Element) {
			return false;
		}
 
		if (depth == 0) {
			return matcher.matches((Element) objectToTest);
		}
		if (depth == 1) {
			return objectToTest instanceof Operation;
		}
		if (depth == 2) {
			return objectToTest instanceof Parameter;
		}
 
		return false;
	}
 
	@Override
	public boolean canCreateAxisElement(String elementId) {
		// currently, we can't filter create by level in the table -> it is a
		// bug...
		return super.canCreateAxisElement(elementId);
	}
 
}

Then you should register this class in your plugin.xml file using the extension point : org.eclipse.papyrus.infra.nattable.axismanager. See the description done for UMLDnDBlockTable

  • id : org.eclipse.papyrus.sysml.nattable.BlockTreeAxisManager . It must be the same id than in the Axis Manager Representation.
  • manager : The qualified Name of your created class

Define Column Header Axis Configuration

Create Column Header Axis Configuration

This step is the same to build the 3 tables : UMLDnDBlockTable, UMLSynchronizedBlockTable and UMLSynchronizedBlockTreeTable.

This object allows to:

  • define the row header appearance
  • store the configuration to use to manage rows

Click with the right mouse button on the Table Configuration item and create a New Child of type Column Header Axis Configuration Table Header Axis Configuration. Once added, define its properties:

ColumnTableHeaderAxisConfiguration.png

  • Display Filter : if true a row filter will be displayed at the bottom of the Column Header Area
  • Display Index : the Column Index Header, will be hidden if true.
  • Display Label : the Column Label Header will be hidden if true.
  • Index Style : Alphabetic or Numeric.

We often use Alphabetic for Columns (and Numeric for Rows)

Define Label Provider Configuration for Column Label

This step is the same to build the 3 tables : UMLDnDBlockTable, UMLSynchronizedBlockTable and UMLSynchronizedBlockTreeTable.


This object provides the configuration used to display Column Label in the Column Header Label Area. As the rows represent edited object, we will use a Feature Label Provider Configuration. Click with the right mouse button on your new created Table Header Axis Configuration item and create a New Child of type Feature Label Provider Configuration. Once added, define its properties:

FeatureLabelProviderConfigurationForColumn.png

  • Display Icon : if true, an icon will be displayed at the left of the label for the category
  • Display Is Derived : if truea / will be displayed in the label if the represented feature is derived
  • Display Label : if true, the label will be displayed
  • Display Multiplicity, if true, the multiplicity will be displayed for the category in the label
  • Display Name : if true, the name of the category will be displayed in the label
  • Display Type : if true, the type of the category will be display in the label

Define the Column Manager

This step is the same to build the 3 tables : UMLDnDBlockTable, UMLSynchronizedBlockTable and UMLSynchronizedBlockTreeTable.

This element allows to declare the java class to used to manage rows in the table. Moreover, it references the configuration to use for the columns and declare the context of the label provider. Here, you need to create two Axis Manager Representation to manage columns. The first one will be used to managed UML features and the second one will be used to manage properties of stereotypes applied on the row elements. Click with the right mouse button on your Table Header Axis Configuration item and create a New Child of type Axis Manager Representation. Once added, define its properties:


AxisManagerRepresentation01.PNG

  • Axis Manager Id: org.eclipse.papyrus.uml.nattable.feature.axis.manager. It is an axis manager already declared in Papyrus.
  • Header Label Configuration: Your must reference your Feature Label Configuration
    • Warning in UMLSynchronizedBlockTreeTable, there are now two Feature Label Configuration, Select the right one.
  • Label Provider context: org.eclipse.papyrus.infra.nattable.header.labelprovider
  • Specific Axis Configuration: there is nothing to reference for columns

And again, create a second New Child of type Axis Manager Representation. Once added, define its properties:

AxisManagerRepresentation02.PNG

  • Axis Manager Id: org.eclipse.papyrus.uml.nattable.stereotype.property.axis.manager. It is an axis manager already declared in Papyrus.
  • Header Label Configuration: Your must reference your Feature Label Configuration
    • Warning in UMLSynchronizedBlockTreeTable, there are now two Feature Label Configuration, Select the right one.
  • Label Provider context: org.eclipse.papyrus.infra.nattable.header.labelprovider
  • Specific Axis Configuration: there is nothing to reference for columns

Define Row Provider

This step is the same to build the 3 tables : UMLDnDBlockTable, UMLSynchronizedBlockTable and UMLSynchronizedBlockTreeTable.


The row provider is the object used to store the axis (here the rows) visible in the table. In case of synchronized table. It always be empty. It exists 2 kinds of provider : Master and Slave. For rows, we must take a Master. Click with the right mouse button on the Table Configuration item and create a New Child of type Row Axis Providers Master Object Axis Provider. Once added, define its properties:

MasterObjectAxisProvider.png

  • Description : field used in user dialog. Allows to describe the behavior of the axis manager
  • Disconnect Slave : we advice to set it to true .
    • false for each added row, the system will add columns axis for each features of the rows not yet represented by a column axis.
    • true the behavior described previously will be stopped

Define Column Provider

This step is the same to build the 3 tables : UMLDnDBlockTable, UMLSynchronizedBlockTable and UMLSynchronizedBlockTreeTable.

The column provider is the object used to store the column axis visible in the table. Here, we will take a Slave Axis Provider. Click with the right mouse button on the Table Configuration item and create a New Child of type Column Axis Providers Slave Object Axis Provider. Once added, define its properties:

SlaveObjectAxisProvider.png

Define the default columns for your table

Now you should declare here the default columns for your table.

Define the Name Column

Click with the right mouse button on your new created Slave Object Axis Provider item and create a New Child of type EStructural Feature Axis to define. Once added, define its properties:

EStructuralFeatureAxis.png

  • Alias: this field allow to give an alias to the represented feature
  • Element : set the value to http://www.eclipse.org/uml2/5.0.0/UML#//NamedElement/name
  • Manager: set the reference to the column axis manager created previously. Warning, you have now 3 Axis Manager Representation in the model. You should reference the Axis Manager which have this id : org.eclipse.papyrus.uml.nattable.feature.axis.manager

Define the IsEncapsulated Column

IsEncapsulated is a property of stereotype, so you can not reference directly it. The table framework allows you to reference it using its qualified name. Click with the right mouse button on your new created Slave Object Axis Provider item and create a New Child of type Feature Id Axis. Once added, define its properties:

FeatureIdAxis.png

  • Alias : this field allows to define an alias for the represented feature.
  • Element: : the referenced element
    • Set it to property_of_stereotype:/SysML::Blocks::Block::isEncapsulated
  • Manager: set the reference to the column axis manager created previously. Warning, you have now 3 Axis Manager Representation in the model. You should reference the Axis Manager which have this id : org.eclipse.papyrus.uml.nattable.stereotype.property.axis.manager

Configure Table Configuration

This is the last step concerning the edition of the nattableconfiguration. Select the root of the model, that is to say the object Table Configuration, to set its properties:

File:EditingTableConfiguration.png

  • Cell Editor Declaration : can be :
    • COLUMN : all cells of the columns will use the same kind of editors (used here, because we have the feature in column, so the system needs to declare a Cell Editor by column.
    • ROW : all cells of the rows will use the same kind of editors
    • CELL : each cell uses a specific editor (provided, but not used)
  • Default Column Axis Provider:
    • it is the default, because you could declare several axis provider for columns!
    • Here, you must reference the Column Axis Provider to use by default, ie just after the table creation
  • Default Row Axis Provider
    • it is the default, because you could declare several axis provider for rows!
    • Here, you must reference the Row Axis Provider to use by default, ie just after the table creation
  • Description : a small description of your table. This text will be displayed in user dialog
  • Icon path : an icon (16x16) used to represent this kind of table. This icon will be used in the Table Creation Menu, in the Model Explorer View, in the ShortCut displayed on the Diagram, ...
  • Name : the initial name proposed to the user when it will create a table from your table configuration
  • Type : the type of your table. It must be unique in Papyrus. It is used by the Papyrus Framework to identify each kind of editor for (save, open, create, ... actions)

UMLDnDBlockTable

  • Cell Editor Declaration : COLUMN
  • Default Column Axis Provider: you must reference your Slave Object Axis Provider
  • Default Row Axis Provider : you must reference your Master Object Axis Provider
  • Description : This table allows to edit Block added by the user by DnD
  • Icon path : an icon (16x16) used to represent this kind of table.
  • Name : UMLDnDBlockTable
  • Type : PapyrusExampleUMLDnDBlockTable

UMLSynchronizedBlockTable

  • Cell Editor Declaration : COLUMN
  • Default Column Axis Provider: you must reference your Slave Object Axis Provider
  • Default Row Axis Provider : you must reference your Master Object Axis Provider
  • Description : This table allows to show and edit all Block owned by a Package. It is a synchronized Table
  • Icon path : an icon (16x16) used to represent this kind of table.
  • Name : UMLSynchronizedBlockTable
  • Type : PapyrusExampleUMLSynchronizedBlockTable

UMLSynchronizedBlockTreeTable

  • Cell Editor Declaration : COLUMN
  • Default Column Axis Provider: you must reference your Slave Object Axis Provider
  • Default Row Axis Provider : you must reference your Master Object Axis Provider
  • Description : This table allows to show and edit all Block owned by a Package. It is a tree synchronized Table. It allows to show the Block, with their Operations and them Parameters.
  • Icon path : an icon (16x16) used to represent this kind of table.
  • Name : UMLSynchronizedBlockTreeTable
  • Type : PapyrusExampleUMLSynchronizedBlockTable

Register your new table configuration into Papyrus framework

Register your nattable configuration file

Each Table Configuration File created previously must contribute to the Papyrus Extension Point org.eclipse.papyrus.infra.nattable.configuration. This extension point allows to contribute to the Table Configuration Catalogue provided by the Papyrus Framework.

File:ContributeToNattableConfigurationExtensionPoint.PNG

  • file: the path for the file configuration
  • type: the type of the table, it must be the same string than the type field in the nattableconfiguration file.


UMLDnDBlockTable

  • file: the path for the file configuration
  • type: PapyrusExampleUMLDnDBlockTable

UMLSynchronizedBlockTable

  • file: the path for the file configuration
  • type: PapyrusExampleUMLSynchronizedBlockTable

UMLSynchronizedBlockTreeTable

  • file: the path for the file configuration
  • type: papyrusExampleUMLSynchronizedBlockTreeTable

Contribute to Viewpoint Framework

//TODO : review this part For this step, first of all, you have to create a new Viewpoints configuration, for example, called blocktableViewpoint.configuration.

Once a blocktableViewpoint.configuration has been created, you have to open it for setting the viewpoint. The basic sets for viewpoints are:

1. Add a Stakeholder and name;

2. Set the Papyrus Configuration with the Stakeholder in the Default Stakeholder field and http://www.eclipse.org/uml2/5.0.0/UML in the Metamodel field;

3. Load the resource: platform:plugin/org.eclipse.papyrus.infra.viewpoints.policy/builtin/default.configuration

4. Add a Papyrus Viewpoint, name BlockTable Viewpoint and set the Parent field with the value Default Papyrus Viewpoint;

5. Add a View Category and name blockTable;

6. Set the Stakeholder with the BlockTable Viewpoint in the Viewpoints field.

BlockTableViewpointConfiguration01.png

Now, you must to add one Papyrus Table in BlockTable Viewpoint and set its properties.

BlockTableViewpointConfiguration02.png

Very impotant: the name must be the same to defined in block.nattableconfiguration (see in the subsection Configure Table Configuration )

Under the BlockTable Viewpoint, add one Model Rule:

BlockTableViewpointConfiguration03.png

And add one Owning Rule:

BlockTableViewpointConfiguration04.png

Contribute to the extenspoint point 2. org.eclipse.papyrus.infra.viewpoints.policy.custom

In this extension you need to add a new configuration and set with the viewpoint file (blockTableBiewpoint.configuration).

ViewpointsPolicyCustomExtension.png

Result

Creating a new Block Table

CreateBlockTable03.png

You can create the Block Table directly under a package:

ExampleBlockTable.png

Synchronization between table and model

The illustration of the table sychronization when a block is added or removed:

SynchroTable01.png After added Block2

SynchroTable02.png Affter removed Block1

Contribute to menu with a new Papyrus Table (must be completed)

The contribution must be done using viewpoint with a file .configuration. The viewpoint metamodel provides 2 ways to register new tables. the first one used the Element Papyrus Sync Table. You must fill the field :

  • categories
  • Icon
  • ImplementationID : the type of the table, with the same value than in the field type declared in the file .nattableConfiguration of your table
  • Name : the name of the table

Then you can declare Rules to allow/forbid the creation of the table according to the selected element

The second way with viewpoint using Papyrus Table element In this case you must fill the field configuration with the path of your table configuration file.

Label Providers

For the label provider, we divided the tables into 2 areas :

  1. Headers
  2. Body


for each areas we declared a label provider context (using the Papyrus Label Provider extension point):

  1. org.eclipse.papyrus.infra.nattable.full.labelprovider : the context to use to find the label provider used in the table. This one calls the others label providers according to the region for which we are looking for the text/image to display.
  2. org.eclipse.papyrus.infra.nattable.body.labelprovider : the context to use to find the label provider used to get the text in the body of the table
  3. org.eclipse.papyrus.infra.nattable.header.labelprovider : the context to use to find the label provider used to get the text in the header of the table (display icon, display label)
  4. org.eclipse.papyrus.infra.nattable.header.feature.labelprovider : the context to use to find the label provider used to display feature in a header. This label provider allows to define the text to display according to the configuration of the axis ( display icon, display label, display multiciplicity, display type, display a "/" for the derived feature)

These contexts are declared into the plugin org.eclipse.papyrus.infra.nattable.

All label provider declared on these context must accept elements implementing ILabelProviderCellContextElement. This object encapsulate the cell for which we want display a text AND useful elements which can be used to define the text/image/color/... to display in the cell.


Papyrus Extension Point For Table :

  1. Axis Manager
  2. Cell Manager
  3. Cell Editor Configuration




Selection synchronization

Some changes have been applied to the code in order to allow a selection synchronization between the tables and the model explorer. The affected classes are the following :
In these changes, IRevealSemanticElement and its associated method revealSemanticElement is at the core of the modifications.

  1. org/eclipse/papyrus/infra/nattable/common/editor/AbstractEMFNattableEditor.java
    • where we implement the interface and add the call for the newly created revealSemanticElement method in AbstractNattableWidgetManager and giving it the list of selected objects.
  2. org/eclipse/papyrus/infra/nattable/manager/table/AbstractNattableWidgetManager.java
    • the method goes through the lines and columns of the tables to find all the elements contained in the list and then proceed to select them graphically in the table.
    • the method used for this is org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.doCommand(ILayerCommand command)
  3. org/eclipse/papyrus/infra/gmfdiag/common/SynchronizableGmfDiagramEditor.java
    • in the original method the selection only allowed the first selected element found to show. we now collect all found IGraphicalEditParts and proceed to set the selection in the graphicalViewer.
  4. org/eclipse/papyrus/views/modelexplorer/ModelExplorerView.java
    • in the handleSelectionChangedFromDiagramEditor method the principal problem was that, if revealSemanticElement was called, it shunned the model explorer as it was not an IEditPart and the redundacy of the IAdaptable verification crashed the process.
    • when we are in an IIeditorPart, as we were interested in the Eobject for the contruction of the list of selected elements and that org.eclipse.papyrus.infra.emf.utils.EMFHelper.getEObject(Object source) already checked for the IAdaptability, the direct replacement of the verification was done.
    • as for the selection in the model explorer, we went through org.eclipse.ui.navigator.LinkHelperService.getLinkHelpersFor(Object anObject) to get the list of LinkHelpers and used the LinkHelper in org.eclipse.papyrus.views.modelexplorer.LinkHelper.activateEditor(IWorkbenchPage aPage, IStructuredSelection aSelection) to handle the selection with revealSemantiElement.


Modifying the graphical representation of the tables

Resizing Axis and Headers

Three listeners have been added to the AbstractNattableWidgetManager class in order to capture the graphical changes and edit the model.

  1. addAxisResizeListener(final BodyLayerStack bodyLayerStack) that listens to the BodyLayerStack
  2. addColumnHeaderResizeListener(final ColumnHeaderLayerStack columnHeaderLayerStack) that listens to the ColumnHeaderLayerStack
  3. addRowHeaderResizeListener(final RowHeaderLayerStack rowHeaderLayerStack) that listens to the RowHeaderLayerStack

Those methods verify the model in order to know if the graphical values are already present and if not create them and write them using a CompositeCommand and executing it.

The major change introduced in order to accomplish this is the modification of the table's model by adding new Styles, more particularly intValueStyles that are used to carry this specific information. These styles are introduced in the Axis elements, if the changes of size modify axis, or to a newly, or existing, LocalHeaderAxisConfiguration if the changes modify headers. Of course, both of them are now StyledElements and therefore can call on the method getNamedStyle().

  • NamedStyle org.eclipse.papyrus.infra.nattable.model.nattable.nattablestyle.StyledElement.getNamedStyle(EClass eClass, String name)

Two init methods will also be called when refreshing or opening the table in order to show the modified table.

  1. initTableAxis() that gets the list of the displayed axis and check their styles
  2. initTableHeaders() that gets the AbstractHeaderAxisConfigurationUsedInTable, allowing the method to get around the invertAxis configuration, and check their styles

Both have a default behavior, in case no styles were attached to an element, to apply its default size to its graphical representation.


Merging cells inside their Axis

The merge is based on nebula's AutomaticSpanningDataProvider (org.eclipse.nebula.widgets.nattable.data.AutomaticSpanningDataProvider) allowing us to apply a spanProvider to the table's BodyLayerStack and, with minor modification of the method valuesNotEqual(), triggering the desired span (i.e. merge) throughout the layer. It is to be noted that the cells are merged using their values and not their types.
To accomplish this four new merge handlers were created, piloted by a menu constituted of exclusive choices.

  1. org.eclipse.papyrus.infra.nattable.handler.MergeColumnsHandler
  2. org.eclipse.papyrus.infra.nattable.handler.MergeRowsHandler
  3. org.eclipse.papyrus.infra.nattable.handler.MergeSelectedRowsHandler
  4. org.eclipse.papyrus.infra.nattable.handler.MergeSelectedColumnsHandler

The handlers' model modification behavior are similar to the resize listeners' one, where the style's value is researched in the model and created, or modified, depending on the case, set inside a CompositeCommand and executed inside the core AbstractMergeHandler. The merge boolean will then be carried by the LocalHeaderAxisConfiguration if the user chose to merge all the columns/rows inside the table or by the Axis if the user chose to merge only selected Axis.

To access these handlers, new extensions have been added in the form of a new menu to call on the handlers and a property tester to check the availability of the menu's options or not depending on the merge state of the table; and to update the toggles of each menu, new updateToggleCommandState() have also been added to NattableModelManager, calling on the getToggleState[...]() methods in AbstractNattableManager.
One init method and two Listeners, one for the resourceSet and another for the Layer, are used to update the graphical view throughout the changes.

  1. initTableMerge() in wich the list of the displayed axis is obtained and their styles checked
  2. resourceSetListener inside the updateCellMap() method in which the notifications due to the modification of the resourceSet are filtered to keep the styles we want to apply or unapply, depending on the user's choices, and enable the use of the undo/redo behavior


Editing merged cells

A PapyrusSpanningDataLayer has been added and @Overrides the setDataValue of nebula's org.eclipse.nebula.widgets.nattable.layer.SpanningDataLayer#setDataValue(int, int, java.lang.Object) to allow the edition of multiple cells inside a merged selection.
The edition will then verify if the value to be inputted can indeed be, see org.eclipse.papyrus.infra.nattable.manager.cell.AbstractCellManager.setValue(), and edit the cell accordingly. Of course, the merge selection is updated after this so that, inside a previuosly merged selection, only the cells of equal values are still merged.

What is the difference between Slave and Master for AxisManager/AxisProvider

We decided to introduce a difference between the Axis Manager used in the Table. There is Slave and Master.

Master/Slave configuration

For the tables which present an axis with Object (Rows) and an Axis with Features (Columns), like in UML Generic Table, the row axis provider is a Master and the Column axis provider is the slave. Its means that the the column axis manager provides elements which depends on the elements provided by the rows axis. For UML Table, the columns axis provides the features for the object displayed as rows in the table.

As you can declare several AxisManagers for rows and several AxisManager for columns, we store them in CompositeAxisManager. So you must have a CompositeAxisManager Master for rows and a CompositeAxisManager slave for columns. You can mix Master and Slave Axis Manager in a CompositeAxisManager.

Master/Master configuration

In case of Matrix, the Rows and the Columns Axis Manager must be master, because the twice provide Object.

Filters

The filters are located at the bottom of the Column Header.

In Papyrus, you can get cells displaying N/A, it means than the columns is not available for the row element owning this cell. That's why declaring your own filter, you must think to manage this specific value. Moreover, if you declare a specific comparator in your filter configuration, it should returns the value org.eclipse.papyrus.infra.nattable.filter.FilterPreferences.INCONSISTENT_VALUE when the value in the cell is inconsistent vs the filter value. This specific returns will be used, according to the preference obtained with org.eclipse.papyrus.infra.nattable.filter.FilterPreferences.displayInconsistentValueWithFilter(Table) to show/hide the rows with inconsistent values.

How to activate filters

To activate filter by default for your table, in your nattableconfiguration file, you must select your Table Header Axis Configuration used for columns and set the value Display Filter to true.
ActivateFilterInTableConfiguration.png

How to declare your own filter

In your plugin.xml file, you must contribute to the Papyrus Extension Point org.eclipse.papyrus.infra.nattable.filter.configuration. You must fill the 2 fields of the extension:

  • class : your java class implementing org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration
  • order : the order to use for your configuration (smaller are more priority). Papyrus default configuration uses value near to 8000.

The interface IFilterConfiguration provides 4 methods:

  • public boolean handles(IConfigRegistry registry, final Object axis): Your must return true<:code> if your configuration must be applied to the axis.
  • <code>public void configureFilter(IConfigRegistry configRegistry, final Object axis, final String configLabel) here, you will configure nattable to use your filter configuration.
  • public String getConfigurationId(): You must return a unique id.
  • public String getConfigurationDescription(): You should return a description of your configuration. It will be use in user dialog to allow to the user to apply a specific configuration for a column.

In the method onfigureFilter you can configure several elements, using these keys:

  • org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes.CELL_EDITOR
    • to declare the cell editor to use in the the filter cell for the column
  • org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes.DISPLAY_CONVERTER
    • to declare the display converter to use (conversion between real element in the cell and the label to use for this element)
  • org.eclipse.papyrus.infra.nattable.utils.NattableConfigAttributes.MATCHER_EDITOR_FACTORY
    • to declare the matcher factory which will create the matcher to use to compare the filter value and the cell value
  • org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration.FILTER_VALUE_TO_MATCH_MANAGER
    • to declare the class which will save the filter state in the model and which will read/convert the saved value to the value to use.

Example to filter EEnumLiteral

This example will allow you to filter EEnumLiteral (http://www.eclipse.org/uml2/5.0.0/UML#//NamedElement/visibility for example)

/*****************************************************************************
 * Copyright (c) 2015 CEA LIST and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   CEA LIST - Initial API and implementation
 *   
 *****************************************************************************/
 
package org.eclipse.papyrus.infra.emf.nattable.filter.configuration;
 
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
 
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.papyrus.infra.nattable.dataprovider.ListComboBoxDataProvider;
import org.eclipse.papyrus.infra.nattable.filter.IFilterValueToMatchManager;
import org.eclipse.papyrus.infra.nattable.filter.IPapyrusMatcherEditorFactory;
import org.eclipse.papyrus.infra.nattable.filter.configuration.AbstractFilterValueToMatchManager;
import org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration;
import org.eclipse.papyrus.infra.nattable.manager.cell.ICellManager;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattableaxis.IAxis;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattablestyle.NamedStyle;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattablestyle.NattablestyleFactory;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattablestyle.NattablestylePackage;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattablestyle.StringListValueStyle;
import org.eclipse.papyrus.infra.nattable.model.nattable.nattablestyle.StringValueStyle;
import org.eclipse.papyrus.infra.nattable.utils.AxisUtils;
import org.eclipse.papyrus.infra.nattable.utils.NattableConfigAttributes;
 
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.matchers.MatcherEditor;
 
/**
 * Filter configuration for eenum filter
 *
 */
public class EEnumFilterCellEditorFilterConfiguration implements IFilterConfiguration {
 
	/**
	 * the id of this editor
	 */
	private static final String ID = "org.eclipse.papyrus.infra.emf.nattable.eenum.checkboxcombo.with.NA";//$NON-NLS-1$
 
	/**
	 * @see org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration#handles(org.eclipse.nebula.widgets.nattable.config.IConfigRegistry, java.lang.Object)
	 *
	 * @param registry
	 * @param columnElement
	 * @return
	 */
	@Override
	public boolean handles(IConfigRegistry registry, Object columnElement) {
		Object representedElement = AxisUtils.getRepresentedElement(columnElement);
		if (representedElement instanceof EStructuralFeature) {
			EClassifier eType = ((EStructuralFeature) representedElement).getEType();
			return eType instanceof EEnum;
		}
		return false;
	}
 
	/**
	 * 
	 * @param axis
	 *            an axis
	 * @return
	 *         a list containing the possible Enumerator for the axis
	 */
	protected List<Enumerator> getLiteral(IConfigRegistry configRegistry, Object axis) {
		Object representedElement = AxisUtils.getRepresentedElement(axis);
		final List<Enumerator> literals = new ArrayList<Enumerator>();
		if (representedElement instanceof EStructuralFeature) {
			EClassifier eType = ((EStructuralFeature) representedElement).getEType();
			Assert.isTrue(eType instanceof EEnum);
			EEnum eenum = (EEnum) eType;
			for (EEnumLiteral current : eenum.getELiterals()) {
				literals.add(current.getInstance());
			}
		}
		return literals;
	}
 
 
	/**
	 * @see org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration#configureRegistry(org.eclipse.nebula.widgets.nattable.config.IConfigRegistry, java.lang.Object, java.lang.String)
	 *
	 * @param configRegistry
	 * @param columnElement
	 * @param configLabel
	 */
	@Override
	public void configureFilter(IConfigRegistry configRegistry, final Object columnElement, String configLabel) {
		List<Enumerator> literals = getLiteral(configRegistry, columnElement);
		final List<Object> valuesToProposed = new ArrayList<Object>(literals);
 
		valuesToProposed.add(0, ICellManager.NOT_AVALAIBLE);
 
		ICellEditor editor = new FilterRowComboBoxCellEditor(new ListComboBoxDataProvider(valuesToProposed));
		IPapyrusMatcherEditorFactory<Object> factory = new IPapyrusMatcherEditorFactory<Object>() {
 
			@Override
			public EventList<MatcherEditor<Object>> instantiateMatcherEditors(IColumnAccessor<Object> columnAccessor, Integer columnIndex, Object wantedValue, IConfigRegistry configRegistry) {
				EventList<MatcherEditor<Object>> list = new BasicEventList<MatcherEditor<Object>>();
				list.add(new EnumeratorMatcherEditor(columnAccessor, columnIndex, wantedValue, configRegistry));
				return list;
			}
		};
 
		configRegistry.registerConfigAttribute(NattableConfigAttributes.MATCHER_EDITOR_FACTORY, factory, DisplayMode.NORMAL, configLabel);
		configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, editor, DisplayMode.NORMAL, configLabel);
		configRegistry.registerConfigAttribute(NattableConfigAttributes.FILTER_VALUE_TO_MATCH_MANAGER, createFilterValueToMatchManager(getConfigurationId(), literals), DisplayMode.NORMAL, configLabel);
 
	}
 
	/**
	 * 
	 * @param filterConfiguration
	 *            the id of the filter configuration used for the managed axis
	 * @param literals
	 *            the available literals
	 * @return
	 *         the filter value manager for the managed axis
	 */
	protected IFilterValueToMatchManager createFilterValueToMatchManager(String filterConfiguration, List<Enumerator> literals) {
		return new EnumeratorFilterValueToMatchManager(filterConfiguration, literals);
	}
 
	/**
	 * 
	 * 
	 * @see org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration#getConfigurationId()
	 *
	 * @return
	 */
	@Override
	public String getConfigurationId() {
		return ID;
	}
 
	/**
	 * @see org.eclipse.papyrus.infra.nattable.filter.configuration.IFilterConfiguration#getConfigurationDescription()
	 *
	 * @return
	 */
	@Override
	public String getConfigurationDescription() {
		return "This configuration provides a combo with checkbox to filter EEnum, with N/A value"; //$NON-NLS-1$
	}
 
	/**
	 * 
	 * This class allows to save the state of the filter for column typed with enumeration
	 *
	 */
	public static class EnumeratorFilterValueToMatchManager extends AbstractFilterValueToMatchManager {
 
		/**
		 * a list with the available literal
		 */
		protected final List<Enumerator> literals;
 
		/**
		 * Constructor.
		 *
		 * @param filterConfigurationId
		 *            the id of the filter configuration used by the axis
		 * @param literals
		 *            the available literals
		 */
		public EnumeratorFilterValueToMatchManager(String filterConfigurationId, List<Enumerator> literals) {
			super(filterConfigurationId);
			this.literals = literals;
		}
 
 
		/**
		 * @see org.eclipse.papyrus.infra.nattable.filter.IFilterValueToMatchManager#getValueToMatch(org.eclipse.nebula.widgets.nattable.config.IConfigRegistry, java.lang.Object)
		 *
		 * @param configRegistry
		 * @param axis
		 * @return
		 */
		@Override
		public Object getValueToMatch(IConfigRegistry configRegistry, Object axis) {
			if (!(axis instanceof IAxis)) {
				return null;
			}
			IAxis iaxis = (IAxis) axis;
			NamedStyle style = getValueToMatchStyle(iaxis);
			if (style != null) {
				if (style instanceof StringListValueStyle) {
					List<Object> returnedValues = new ArrayList<Object>();
					Collection<String> coll = ((StringListValueStyle) style).getStringListValue();
					for (String string : coll) {
						if (string.equals(ICellManager.NOT_AVALAIBLE)) {
							returnedValues.add(ICellManager.NOT_AVALAIBLE);
						} else {
							for (Enumerator tmp : literals) {
								if (tmp.getName().equals(string)) {
									returnedValues.add(tmp);
									continue;
								}
							}
						}
					}
					return returnedValues;
				}
				if (style instanceof StringValueStyle) {
					String val = ((StringValueStyle) style).getStringValue();
					if (val.equals(ICellManager.NOT_AVALAIBLE)) {
						return ICellManager.NOT_AVALAIBLE;
					}
					for (Enumerator tmp : literals) {
						if (tmp.getName().equals(val)) {
							return tmp;
						}
					}
				}
			}
			return null;
		}
 
		/**
		 * @see org.eclipse.papyrus.infra.nattable.filter.configuration.AbstractFilterValueToMatchManager#getSaveValueToMatchCommand(org.eclipse.emf.transaction.TransactionalEditingDomain, org.eclipse.nebula.widgets.nattable.config.IConfigRegistry,
		 *      java.lang.Object, java.lang.Object)
		 *
		 * @param domain
		 * @param configRegistry
		 * @param axis
		 * @param newValue
		 * @return
		 */
		@Override
		protected Command getSaveValueToMatchCommand(TransactionalEditingDomain domain, IConfigRegistry configRegistry, Object axis, Object newValue) {
			if (!(axis instanceof IAxis)) {
				return null;
			}
			IAxis iaxis = (IAxis) axis;
			CompoundCommand cc = new CompoundCommand("Save Value To Match Command"); //$NON-NLS-1$
			NamedStyle keyStyle = getValueToMatchStyle(iaxis);
			if (newValue instanceof Collection<?>) {
				Collection<?> coll = (Collection<?>) newValue;
				// we need to update the keystyle
				if (keyStyle != null && !(keyStyle instanceof StringListValueStyle)) {
					// we need to destroy the previous keystyle
					Command cmd = getDestroyFilterValueToMatchCommand(domain, configRegistry, axis);
					if (cmd != null && cmd.canExecute()) {
						cc.append(cmd);
					}
				}
				if (keyStyle == null) {
					keyStyle = NattablestyleFactory.eINSTANCE.createStringListValueStyle();
					keyStyle.setName(FILTER_VALUE_TO_MATCH);
					cc.append(AddCommand.create(domain, iaxis, NattablestylePackage.eINSTANCE.getNamedStyle(), keyStyle));
				}
 
				List<String> values = new ArrayList<String>();
				for (Object tmp : coll) {
					Assert.isTrue(tmp instanceof Enumerator || ICellManager.NOT_AVALAIBLE.equals(tmp));
					if (tmp instanceof Enumerator) {
						values.add(((Enumerator) tmp).getName());
					} else {
						values.add(ICellManager.NOT_AVALAIBLE);
					}
				}
				cc.append(SetCommand.create(domain, keyStyle, NattablestylePackage.eINSTANCE.getStringListValueStyle_StringListValue(), values));
			} else {
				if (keyStyle != null && !(keyStyle instanceof StringValueStyle)) {
					// we need to destroy the previous keystyle
					Command cmd = getDestroyFilterValueToMatchCommand(domain, configRegistry, axis);
					if (cmd != null && cmd.canExecute()) {
						cc.append(cmd);
					}
				}
				if (keyStyle == null) {
					keyStyle = NattablestyleFactory.eINSTANCE.createEObjectValueStyle();
					keyStyle.setName(FILTER_VALUE_TO_MATCH);
					cc.append(AddCommand.create(domain, iaxis, NattablestylePackage.eINSTANCE.getNamedStyle(), keyStyle));
				}
				Assert.isTrue(newValue instanceof Enumerator || ICellManager.NOT_AVALAIBLE.equals(newValue));
				// we store the name of the literal and a reference to the literal, for 2 reasons :
				// - for static profile we get enumerator and not EEnumLiteral
				// - in case of redefinition of the profile, the reference would be not correct
				String name;
				if (newValue instanceof Enumerator) {
					name = ((Enumerator) newValue).getName();
				} else {
					name = ICellManager.NOT_AVALAIBLE;
				}
				cc.append(SetCommand.create(domain, keyStyle, NattablestylePackage.eINSTANCE.getStringValueStyle_StringValue(), name));
			}
			return cc;
		}
 
	}
}

In the previous class, we declare:

  • we declare a ComboBox with CheckBox as CellEditor for the filter
  • how to save and read the eenumliteral chosen by the user in the filter cell editor
    • You can see that we store their name in the model and a direct reference to its in the model.
  • how to display them in the filter area
  • how to compare the eenum literal. The class doing the match follow
/*****************************************************************************
 * Copyright (c) 2015 CEA LIST and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   CEA LIST - Initial API and implementation
 *   
 *****************************************************************************/
 
package org.eclipse.papyrus.infra.emf.nattable.filter.configuration;
 
import java.util.Collection;
 
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
import org.eclipse.papyrus.infra.nattable.filter.AbstractPapyrusMatcherEditor;
import org.eclipse.papyrus.infra.nattable.filter.AbstractSinglePapyrusMatcher;
 
import ca.odell.glazedlists.matchers.Matcher;
 
/**
 * Matcher Editor for UML Enumeration
 *
 */
public class EnumeratorMatcherEditor extends AbstractPapyrusMatcherEditor {
 
	/**
	 * 
	 * Constructor.
	 *
	 * @param columnAccesor
	 * @param columnIndex
	 * @param matchOn
	 * @param configRegistry
	 */
	public EnumeratorMatcherEditor(IColumnAccessor<Object> columnAccesor, int columnIndex, Object matchOn, IConfigRegistry configRegistry) {
		super(columnAccesor, columnIndex, matchOn, configRegistry);
	}
 
	/**
	 * @see org.eclipse.papyrus.infra.nattable.filter.AbstractPapyrusMatcherEditor#createMatcher(org.eclipse.nebula.widgets.nattable.data.IColumnAccessor, int, java.lang.Object, org.eclipse.nebula.widgets.nattable.config.IConfigRegistry)
	 *
	 * @param columnAccesor
	 * @param columnIndex
	 * @param matchOn
	 * @param configRegistry
	 * @return
	 */
	@Override
	protected Matcher<Object> createMatcher(IColumnAccessor<Object> columnAccesor, int columnIndex, Object matchOn, IConfigRegistry configRegistry) {
		return new EnumeratorMatcher(columnAccesor, matchOn, columnIndex, configRegistry);
	}
 
	/**
	 * This Matcher allows to know is an object is displayed in a cell
	 *
	 */
	public static class EnumeratorMatcher extends AbstractSinglePapyrusMatcher<Object> {
 
		/**
		 * Constructor.
		 *
		 * @param accessor
		 * @param wantedObject
		 * @param columnIndex
		 * @param configRegistry
		 */
		public EnumeratorMatcher(IColumnAccessor<Object> accessor, Object wantedObject, int columnIndex, IConfigRegistry configRegistry) {
			super(accessor, columnIndex, wantedObject, configRegistry);
		}
 
		/**
		 * Constructor.
		 *
		 * @param accessor
		 * @param wantedObject
		 * @param columnIndex
		 */
		public EnumeratorMatcher(IColumnAccessor<Object> accessor, Object wantedObject, int columnIndex) {
			super(accessor, columnIndex, wantedObject);
		}
 
		/**
		 * @see ca.odell.glazedlists.matchers.Matcher#matches(java.lang.Object)
		 *
		 * @param item
		 * @return
		 */
		@Override
		public boolean matches(Object item) {
			Object res = getColumnAccessor().getDataValue(item, getColumnIndex());
			if (res != null) {
				Object wantedObject = getObjectToMatch();
				String stringToMatch = null;
				if (wantedObject instanceof String) {
					stringToMatch = (String) wantedObject;
				} else {
					stringToMatch = ((Enumerator) wantedObject).getName();
				}
				if (res instanceof Collection<?>) {
					for (Object tmp : (Collection<?>) res) {
						if (tmp instanceof Enumerator) {
							Enumerator lit = (Enumerator) tmp;
							if (stringToMatch.equals(lit.getName())) {
								return true;
							}
						} else {
							if (stringToMatch.equals(tmp)) {
								return true;
							}
						}
					}
				}
				if (res instanceof Enumerator) {
					return stringToMatch.equals(((Enumerator) res).getName());
				}
				return stringToMatch.equals(res);
			}
			return false;
		}
	}
 
}

Available Filter cell Editors

You can declare several kinds of editors for filters:

  • a combo with checkbox for multi selection (class org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor), used in org.eclipse.papyrus.infra.emf.nattable.filter.configuration.EBooleanComboBoxCellEditorFilterConfiguration

ComboWithCheckBox.png

  • a combo without checkbox (class org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor), used in org.eclipse.papyrus.infra.emf.nattable.filter.configuration.EBooleanFilterRowComboBoxCellEditorFilterConfiguration

ComboWithoutCheckBox.png

  • a string editor with several mode used in org.eclipse.papyrus.infra.nattable.filter.PapyrusTextMatchingMode:
    • contains: (case by default)
    • regex found: a substring of the text match the regex
    • regex match: the full text match the regex
    • start with: the text start with the wanted string
    • numeric mode: This mode allows you to use comparison operators : <, >, <=, >=, = and <> for not equals.

So, to configure filter using a text cell editor, you should extends the class org.eclipse.papyrus.infra.nattable.filter.configuration.TextEditorFilterConfiguration. To configure integer comparison, with a text editor, you should look the class org.eclipse.papyrus.infra.nattable.filter.configuration.AbstractIntegerEditorFilterConfiguration. In this class and its subclasses, in the method org.eclipse.papyrus.infra.nattable.filter.configuration.TextEditorFilterConfiguration.configureFilter(IConfigRegistry, Object, String):

  • we declare a specific data validator: the user can only typed numerical values
  • we declare a specific Comparator to compare numerical values
  • we declare the matching mode to org.eclipse.papyrus.infra.nattable.filter.PapyrusTextMatchingMode.NUM: to get numeric comparison and use the comparator declared previously

Comparator used by filter

If you need to declare your own comparator and you extend org.eclipse.papyrus.infra.nattable.filter.configuration.TextEditorFilterConfiguration.configureFilter(IConfigRegistry, Object, String), you should know these points.

  • Your comparator must always manage String comparison to manage cells with N/A value.
  • In the case where the value to compare is inconsistent (String value to match a numerical value for example), Your comparator must returns the value org.eclipse.papyrus.infra.nattable.filter.FilterPreferences.INCONSISTENT_VALUE. this value will be interpreted as an impossible comparison.
  • The method org.eclipse.papyrus.infra.nattable.filter.FilterPreferences.displayInconsistentValueWithFilter(Table) allow to know it the inconsistent row (impossible comparison) must be displayed or hidden. By default, Papyrus hides these rows.

Filter Strategy

  • By default, the row with a cell contains is inconsistent with the column are hidden. There is 2 main cases in Papyrus:
    • the cell display N/A for a value which is not a string
    • the user uses num: (see below) in the string editor, all values which are not numerical values will be hidden
  • In case of filtering on several column in the same time, we will display only the rows matching all applied filters
  • In case of collection in a cell, the row matches if the collection contains the wanted element

How to create your own label provider

//TODO

How to create your own cell manager

//TODO

How to create your own axis manager

//TODO

How to create your own post action for paste

//TODO

How to declare several axis configuration in the same table

//TODO

Matrix

The Papyrus tabular editor should allow to the developper to create matrix, we doesn't really try to do that. Here, we give some points which should allow you to create matrix :

  1. the column axis must manage object instead of manage features
  2. you must declare a cell manager specific to your table to get and edit the values in the cell
  3. you probably need to declare your own label provider
  4. others things to do?
  5. to conclude, don't forget to implements required methods to allow (or forbid) all features already supported by the table framework. The list of the supported feature is available here.

=Which is the difference between TableHeader... and LocalHeader... //TODO