# Overview of Constraints

Constraints are implemented in the EMF Validation Framework as instance of the IModelConstraint interface. A constraint has only two features: a validate() operation that evaluates the constraint on some target object, and a descriptor that supplies all of the meta-data about the constraint that the framework needs in order to work with it.

> [as SVG]

The IConstraintDescriptor provides:

- an ID and a localizable name to identify the constraint to the system and to users
- the evaluation mode, whether live or batch
- the EClasses that the constraint targets
- in the case of a live constraint, the event types and features that trigger it
- the severity, error code, and error message pattern, for construction of Eclipse IStatus objects on violation of the constraint
- categorization: categories are hierarchical, and a constraint can be a member of zero or more categories
- enablement state: constraints can be selectively enabled by the user, or can be disabled by the system if they are misconfigured or throw run-time exceptions

Much of this information appears in the preferences dialog, in the Model Validation / Constraints page, for presentation to the user.

## Implementing Constraints

Constraints can be implemented in Java or in other languages, according to the available contributions of language providers. The implementation of a constraint in some language is separate from the contribution of that constraint by a constraint provider.

> [as SVG]

A Java constraint is implementated as a subclass of the AbstractModelConstraint class. It implements the validate() method accepting an IValidationContext and return an IStatus reporting the validation result.

The ValidationContext object provides a variety of information about the current validation operation, including:

- the target of validation, the element to be validated
- the eventType, in the case of live validation
- the feature and its new value, in the case of live validation
- the currentConstraintId, indicating the constraint that is being invoked. This allows a single Java class to implement any number of (probably related) constraints, and switch on the ID to determine what to check

In addition to this contextual information, the validation context provides a variety of other services to the constraint. Using the get/putCurrentConstraintData(), a constraint can cache some information that will help it to optimize the validation of multiple objects by persisting data between invocations on different objects in the same validation operation. This cache can take the form of any object.

Another time-saving measure is the skipCurrentConstraintFor() method, which allows a constraint to indicate that it has already validated some other objects while checking the current target, so that it does not need to be invoked on them later. For example, a constraint that checks for dependency cycles in a graph will, if it does or does not find a cycle, have indirectly validated a number of other objects in doing so.

## Reporting Validation Results

An implementation of a constraint will report validation results when the constraint fails, in the form of an IStatus object. More precisely, validation problems are implemented as IConstraintStatuses. The constraint may construct these results, itself, using the factory methods on the ConstraintStatus class. Indeed, in order to create a multi-status result reporting multiple discrete problems, it is necessary to work with the ConstraintStatus class.

> [as SVG]

For simpler cases, the IValidationContext provides convenient API for creating the validation results. The addResult() and addResults() methods add problem elements to the result. These are elements that are somehow related to the violation of the constraint on the target element, which should be highlighted when the user double-clicks the problem marker reporting the violation.

The createFailureStatus() method creates a result status encapsulating the current result set (as constructed via addResult() calls) and referencing the current constraint and target element. The arguments to the createFailureStatus() method are the positional arguments to slot into the error message pattern declared by the constraint's descriptor. The validation framework provides formatting of EObjects using the label providers from their metamodels' *.edit plug-ins, as well as pretty-printing of collections.

In the case that a constraint finds no problems, it should return the result of createSuccessStatus(). This method will return an appropriate status object if the client invoking validation requested results for successful constraint evaluations as well as failures. Otherwise, this method returns null. So, a well-behaved constraint will always use this to report success.

To illustrate several of these concepts, consider a constraint on the Library metaclass that requires libraries to be uniquely named. Note, in particular, that a violation of this constraint for one library necessarily means that multiple libraries are in violation of the same constraint:

    public class LibraryNameIsUnique extends AbstractModelConstraint {
        public IStatus validate(IValidationContext ctx) {
            // the constraint is declared to target only Library, so we can safely cast
            Library target = (Library) ctx.getTarget(); // object to validate

            // does this library have a unique name?
            Set<Library> libs = findLibrariesWithName(target.getName());
            if (libs.size() > 1) {
                // report this problem against all like-named libraries
                ctx.addResults(libs);

                // don�t need to validate these other libraries
                libs.remove(target);
                ctx.skipCurrentConstraintFor(libs);

                // arguments are slotted into the message pattern
                return ctx.createFailureStatus(new Object[] {
                    target, libs});
            }
        }

        return ctx.createSuccessStatus();
      }
    }

Copyright (c) 2000, 2007 IBM Corporation and others.