# Tutorial: EMF Validation Adapter

## Contents

- Overview
- References
- Creating the EValidator Implementation
- Registering the EValidator Implementation
- Summary

## Overview

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]

## References

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:

- For an environment in which to test the EValidator that you will create in this tutorial, install the Library Metamodel example which provides a generated editor.
- ModelValidationService API
- EValidator API

[back to top]

## Creating the EValidator Implementation

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]

## Registering the EValidator Implementation

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]

## Summary

To illustrate how to implement an EMF Validation Framework client, we

1. Created an EMF validator that performs validation using the model validation service.
2. 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.