Contents
The EMF Validation Framework provides a generic and extensible framework
for defining constraints on EMF metamodels and for checking models against those
constraints. It differs from the
EValidator
API in EMF in several important respects:
-
support for automatic validation on transaction boundaries: constraints
can indicate that they are evaluated in "live" mode, as changes are made
in a model, rather than by user demand.
- dynamic extensibility: the framework is not based on code generation.
- pluggable support for constraint languages such as OCL.
This tutorial will illustrate how a client application performs model validation
using the Validation Framework. In particular, it will show you how to create an
EValidator
implementation that delegates to the validation framework, to provide
user-demand "batch mode" validation from an EMF editor.
[
back to top]
This tutorial assumes that the reader is familiar with the Eclipse extension point
architecture. There is an abundance of on-line help in Eclipse for those
unfamiliar with extension points.
To see the complete source code for the examples shown in this tutorial, install
the
Validation Adapter Example
plug-in into your workspace.
Other references:
[
back to top]
Our
EValidator
implementation will delegate to the EMF Validation Framework to evaluate all
active constraints on a sub-tree of a model. The metamodel that will target is
the Library Metamodel example.
The
EValidator
API requires us to implement three methods, as shown:
boolean validate(EObject eObject, DiagnosticChain diagnostics, Map context);
boolean validate(EClass eClass, EObject eObject, DiagnosticChain diagnostics, Map context);
boolean validate(EDataType eDataType, Object value, DiagnosticChain diagnostics, Map context);
We will extend the EObjectValidator
class from the EMF API to
inherit the basic EObject
constraints. Then, we will
concentrate our efforts on implementing the second of the
EValidator
methods, above, implementing the first by delegating to the second.
The EMF Validation Framework does not validate
EDataType
values directly; it relies on constraints on
EClass
es to validate the EAttribute
s of their
instances.
public class EValidatorAdapter
extends EObjectValidator {
private final IBatchValidator batchValidator;
public EValidatorAdapter() {
super();
batchValidator =
(IBatchValidator) ModelValidationService.getInstance().newValidator(
EvaluationMode.BATCH);
batchValidator.setIncludeLiveConstraints(true);
batchValidator.setReportSuccesses(false);
}
public boolean validate(EObject eObject, DiagnosticChain diagnostics,
Map context) {
return validate(eObject.eClass(), eObject, diagnostics, context);
}
The snippet above shows how we use the
ModelValidationService
to create a validator object to perform batch (user-triggered) validation. We
want to include any constraints that also run in live mode, and are not
interested in receiving informational statuses indicating which constraints
pass.
First, we perform the superclass's validation (whatever that might be), then
delegate to our
IBatchValidator
instance to evaluate constraints contributed to the EMF Validation Framework
and convert the results to EMF
Diagnostic
s:
public boolean validate(EClass eClass, EObject eObject,
DiagnosticChain diagnostics, Map context) {
super.validate(eClass, eObject, diagnostics, context);
IStatus status = Status.OK_STATUS;
if (diagnostics != null) {
if (!hasProcessed(eObject, context)) {
status = batchValidator.validate(
eObject,
new NullProgressMonitor());
processed(eObject, context, status);
appendDiagnostics(status, diagnostics);
}
}
return status.isOK();
}
When EMF's
Diagnostician
invokes our validator, it will assume that
we are evaluating only a single object (not an entire content tree). The EMF Validation Framework takes the opposite approach: by default, batch
validation is recursive over the object's content tree (though this can be
changed by a client or a metamodel provider). This allows constraints to
detect and avoid redundancy in evaluation on related elements and to implement
other kinds of optimizations. Because of this difference, we need to ensure
that our validator does not re-validate objects already reached from their
containers, using the context
map provided by the diagnostician:
private void processed(EObject eObject, Map context, IStatus status) {
if (context != null) {
context.put(eObject, status);
}
}
private boolean hasProcessed(EObject eObject, Map context) {
boolean result = false;
if (context != null) {
while (eObject != null) {
if (context.containsKey(eObject)) {
result = true;
eObject = null;
} else {
eObject = eObject.eContainer();
}
}
}
return result;
}
Finally, we need to convert the IStatus
objects reported by the
IBatchValidator
to represent constraint violations to
Diagnostic
s:
private void appendDiagnostics(IStatus status, DiagnosticChain diagnostics) {
if (status.isMultiStatus()) {
IStatus[] children = status.getChildren();
for (int i = 0; i < children.length; i++) {
appendDiagnostics(children[i], diagnostics);
}
} else if (status instanceof IConstraintStatus) {
diagnostics.add(new BasicDiagnostic(
status.getSeverity(),
status.getPlugin(),
status.getCode(),
status.getMessage(),
((IConstraintStatus) status).getResultLocus().toArray()));
}
}
[
back to top]
EMF provides extension points on which to register resource factories for
file extensions and EPackage
s for namespace URIs. However, there
is no extension point on which we can register our
EValidator
implementation for the Library Metamodel. Instead, we will create an
org.eclipse.ui.startup
extension to register our validator when
the Eclipse platform launches. A real application would probably have some
better trigger point than this. In our plugin.xml
we define:
<extension point="org.eclipse.ui.startup">
<startup class="org.eclipse.emf.validation.examples.adapter.Startup"/>
</extension>
Our start-up class then just needs to instantiate our validator and add
it to the EValidator.Registry
:
public class Startup
implements IStartup {
public void earlyStartup() {
EValidator.Registry.INSTANCE.put(
LibraryPackage.eINSTANCE,
new EValidatorAdapter());
}
}
Now, any invocation of EMF's "Validate" menu in the Library Editor will
invoke the EMF Validation Framework to perform the validation.
[
back to top]
To illustrate how to implement an EMF Validation Framework client, we
-
Created an EMF validator that performs validation using the model
validation service.
-
Registered the validator with an EMF metamodel to install the validator
in the editor.
[
back to top]
Copyright (c) 2000,2005 IBM Corporation and others. All Rights Reserved.