Validation and verification are important activities when working with models. Validation means checking external consistency, i.e. that a model corresponds to what it models (usually the real world), whereas verification means checking internal consistency, i.e. that the model isn't contradicting itself. Validation in Ecore relies on capturing knowledge about a domain in the form of constraints, so that you can check that an actual object structure that conforms to a model, also satisfies additional constraints in the domain.

Introduction

Suppose you are modelling an organisation with organisational units (OrgUnit), employees (Person) and roles (Role), and you want to capture the fact that people names can only have letters, spaces and dashes (-), and all OrgUnit should have a manager, i.e. an employee with a role labeled 'manager'. The first rule can be checked by hand-editing the setter-method for the name, but it may be better to somehow indicate that a rules is violated, rather than throwing an exception. The second rule can be easily coded, but it is more difficult to ensure the code is always run when relevant object properties are changed, since several objects and properties are involved. A related problem is how to handle step-wise modelling, since the second rule necessarily will be violated while the model is built, it doesn't make sense to consider it an error that should be prevented. Instead, you can check it at a point when you consider yourself done.

As mentioned in the Ecore page, an Ecore model can be thought of as a way of constraining what objects structures are legal. By using types and multiplicity you can constrain what objects can be linked and how many, but there are still many constraints that cannot be captured with the concepts available in Ecore. To support such constraints, like the ones above, Ecore provides a mechanism for validation that supports both hand-written Java code and interpreted queries using languages like OCL. Ecore's mechanism relies on separating constraints from the checks that are part of the core model and enforced in the generated code. A validator object (EValidator instance) is created and is asked to validate all constraints for a particular object tree, and so-called diagnostic objects report any violations. The standard editors use this mechanism, and mark all invalid objects with a red (error) or yellow (warning) marker, depending on the severity.

Implementing constraints

Ecore supports three ways of providing/implementing constraints

  • rules provided in ExtendedMetaData annotations
  • logic provided in Java code
  • interpreted expressions written in one of many supported languages.

Constraints provided in ExtendedMetaData annotations

A limited set of constraints on data types (EDataType) kan be provided by using so-called ExtendedMetaData annotations, e.g. a pattern for a String-based data type. See https://eclipsesource.com/blogs/2014/08/26/emf-validation-for-datatype-constraints/

Constraints implemented in Java code

The Java code variant relies on genmodel's ability to generate a validator subclass with methods for each constraint. Adding and using such a constraint is done in four steps:

Step 1: Add an EAnnotation to the appropriate EClass.

Right-click on the EClass in the editor and add an EAnnotation. Set the source attribute to http://www.eclipse.org/emf/2002/Ecore. Then add a key/value pair, with constraints as the key and the logical name of the constraint (a valid Java name) as the value. If you need to add several constraints, separate their names by a space.

Step 2: Generate code and locate the validation method stub(s)

Genmodel will generate an EValidator subclass in the util package with one method for each constraints (if there are any) named validator_<constraint name>. The method body contains template code for adding a diagnostic for a violated constraint with ERROR severity. The condition is initially false and needs to be replaced by the real constraint logic. An example of the generated stub is shown right.

 

Step 3: Implement the constraint logic in Java

The false condition has been replaced by a call to the hasOnlyNameCharacters helper method. You may also lower the severity if the constraint is more of a suggestion. Notice how NOT is added after @generated, to indicate that we've change generated code. This will prevent the code from being overwritten if the class is regenerated.

	/**
	 * Validates the nameCharacters constraint of '<em>Person</em>'.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated NOT
	 */

	public boolean validatePerson_nameCharacters(Person person, ...) {
		if (! hasOnlyNameCharacters(person.getName())) {
			if (diagnostics != null) {
				...
			}
			return false;
		}
		return true;
	}

	private boolean hasOnlyNameCharacters(String name) {
		for (int i = 0; i < name.length(); i++) {
			char c = name.charAt(i);
			if (! (Character.isLetter(c) || c == ' ' || c == '-')) {
				return false;
			}
		}
		return true;
	}

Step 4: Make the implementation available for the editor

Since the constraint logic is implemented in Java code that must be compiled, you need to either launch another Eclipse or install the EMF project in the same Eclipse, to make the constraint available in an editor.

 

Interpreted constraints

As an alternative to Java code that must be compiled, you can use an interpreted expression language like OCL for coding constraints. The advantage is a shorter development cycle, the downside is slightly more complex configuration. And of course, there are both advantages and disadvantages of expression languages. Note that the available languages depend on what plugins are installed in Eclipse. OCL support is not part of the standard modelling package, so must be installed from the standard Eclipse installation site. There are several OCL interpreters available, two come with the standard OCL project, and one in the Acceleo Query SDK (easiest to use standalone).

Adding and using an interpreted constraint is done in three steps (note that the OCLinEcore Editor provides a custom textual syntax for Ecore including constraints, see Editing Ecore models):

Step 1: Add an EAnnotation for using OCL for validation to the EPackage.

Support for using OCL for validation must be explicitly indicated with an EAnnotation on the EPackage. Right-click on the EClass in the editor and add an EAnnotation. Set the source attribute to http://www.eclipse.org/emf/2002/Ecore, and add a key/value pair, with validationDelegates as the key and a language-specific identifier as the value, e.g. http://www.eclipse.org/emf/2002/Ecore/OCL for the standard OCL support. If you have installed Acceleo (part of Sirius) and prefer the Acceleo Query Language (AQL) you can use http://www.eclipse.org/acceleo/query/1.0.


Step 2: Add an EAnnotation to the appropriate EClass.

This step is the same as step 1 in the previous section: Add an EAnnotation with the source attribute set to http://www.eclipse.org/emf/2002/Ecore and a key/value pair, with constraints as the key and the logical names of the constraints separated by a space.

 

Step 3: Add an EAnnotation for the constraint expression.

This step links the validationDelegate from step 1 to the constraint in step 2: Add another EAnnotation to the EClass, where the source attribute is the same language identifier as above, i.e. the value for the validationDelegate key of step 1. Then add key/value pairs for each constraint, with the constraint name as the key and the OCL expression for the constraint as the value.

An example is shown in the figure to the right. The OCL expression is executed with an OrgUnit instance as self and computes the set of workers with the role of a manager and checks if its size is 1. It can be a bit tricky to formulate the correct expression, and many language installations provide a custom interpreter view for experimenting. E.g. OCL has a special OCL Console and Sirius has an Interpreter view that support evaluating both OCL and AQL expressions on the current selection in the active editor.

  • No labels