Constraints

Overview

XMF classifiers may contain a collection of constraints. A constraint is used to determine whether a candidate instance is viewed as a well formed instance of the classifier. In order to be correctly classified, a candiate instance must satisfy all of the constraints that are defined locally or inherited by the classifier. This document describes how to use constraints.

Class Constraints

A constraint can be created directly as an instance of XCore::Constraint and then added to a classifier using Classifier::add. However, it is usual to define constraint either as part of a class definition or via a context-definition. Consider the following definition of a class Date:
import Java;

context Root

@Class Date metaclass JavaClass
JavaDescriptor("java.util.Date","")
@Operation less(other)
if other.isKindOf(Date)
then self.compareTo(other) < 0
else false
end
end
end
which is used in the following definition of a class representing personnel records:
context Root
@Class PersonnelRecord

@Attribute name : String end
@Attribute start : Date end
@Attribute termination : Date end

@Constructor(name) !
self.start := Date()
end

@Constraint NonEmptyName
name <> ""
fail "The name must not be empty."
end

@Operation terminate()
self.termination := Date()
end

end
The idea is that the constraint can be used to check that the personnel record is correctly formed. The constraint contains two expressions: a boolean valued expression that determines whether the constraint is satisfied. Within this expression the value of 'self' is the candidate instance. The (optional) expression after 'fail' is a string-valued expression and is used to construct a report string in the case when the constraint fails. Given a valid record:
p := PersonnelRecord("Fred Brown");
we can check the constraints for p either by:
p.checkConstraints();
or by:
PersonnelRecord.classify(p);
since these are equivalent. The result is a constraint report:
[1] XMF> PersonnelRecord.classify(p).writeReport(stdout);

The constraint NonEmptyName succeeded PersonnelRecord(Fred Brown) (no reason given)

The constraint TypeCheck succeeded PersonnelRecord(Fred Brown) (no reason given)

The constraint AllSlotsTypeCorrect succeeded PersonnelRecord(Fred Brown) (no reason given)
You can see from the report that TypeCheck and AllSlotsTypeCorrect are system supplied constraints (from Object). Suppose we create an illegal record:
[1] XMF> PersonnelRecord.classify(PersonnelRecord()).writeReport(stdout);

The constraint NonEmptyName failed PersonnelRecord() The name must not be empty.

The constraint TypeCheck succeeded PersonnelRecord() (no reason given)

The constraint AllSlotsTypeCorrect succeeded PersonnelRecord() (no reason given)
Now suppose that we add a new constraint via a context-definition:
context PersonnelRecord
@Constraint ValidTermination
termination = null orelse start < termination
fail
"Termination date " + termination.pprint() + " must be after the start date " + start.pprint()
end
Terminating employment should be done via the terminate() operation. Under these circumstances the constraint will be satisfied. However, if the termination date is tampered with the record may become illegal:
[1] XMF> p.checkConstraints().writeReport(stdout);

The constraint NonEmptyName succeeded PersonnelRecord(Fred Brown) (no reason given)

The constraint ValidTermination failed PersonnelRecord(Fred Brown) Termination date Tue Oct 11 12:00:09 GMT 2007 must be after the start date Tue Oct 11 12:00:13 GMT
2007

The constraint TypeCheck succeeded PersonnelRecord(Fred Brown) (no reason given)

The constraint AllSlotsTypeCorrect succeeded PersonnelRecord(Fred Brown) (no reason given)
Instead of writing the constraint report as text, it can be written to a file as HTML:
p.checkConstraints().writeHTML(file);
The resulting HTML file uses colour coding to show the satisfied constraints (green) and failures (red). The following shows all the constraints satisfied:

Constraint Report (Tue Oct 11 12:04:44 GMT 2007)


Checks for PersonnelRecord(Fred Brown)
Dependent checks
Constraint NonEmptyName successful
Constraint ValidTermination successful
Constraint TypeCheck successful
Constraint AllSlotsTypeCorrect successful


Constraint NonEmptyName
Candidate PersonnelRecord(Fred Brown)
termination null
name Fred Brown
start Tue Oct 11 12:04:37 GMT 2007
Invariant
@Operation body(classifier : XCore::Element):XCore::Element
      name <> ""
    end


Constraint ValidTermination
Candidate PersonnelRecord(Fred Brown)
termination null
name Fred Brown
start Tue Oct 11 12:04:37 GMT 2007
Invariant
@Operation body(classifier : XCore::Element):XCore::Element
      termination = null orelse start < termination
    end


Constraint TypeCheck
Candidate PersonnelRecord(Fred Brown)
termination null
name Fred Brown
start Tue Oct 11 12:04:37 GMT 2007
Invariant


Constraint AllSlotsTypeCorrect
Candidate PersonnelRecord(Fred Brown)
termination null
name Fred Brown
start Tue Oct 11 12:04:37 GMT 2007
Invariant




The following shows some failures (the termination date and the name has been changed to the illegal value 10):



Constraint Report (Tue Oct 11 12:08:48 GMT 2007)


Checks for PersonnelRecord(10)
Dependent checks
Constraint NonEmptyName successful
Constraint ValidTermination failed
Constraint TypeCheck successful
Constraint AllSlotsTypeCorrect failed


Constraint NonEmptyName
Candidate PersonnelRecord(10)
termination Tue Oct 11 12:00:09 GMT 2007
name 10
start Tue Oct 11 12:04:37 GMT 2007
Invariant
@Operation body(classifier : XCore::Element):XCore::Element
      name <> ""
    end


Constraint ValidTermination
Candidate PersonnelRecord(10)
termination Tue Oct 11 12:00:09 GMT 2007
name 10
start Tue Oct 11 12:04:37 GMT 2007
Invariant
@Operation body(classifier : XCore::Element):XCore::Element
      termination = null orelse start < termination
    end
Failure Reason
Termination date Tue Oct 11 12:00:09 GMT 2007 must be after the start date Tue Oct 11 12:04:37 GMT 2007


Constraint TypeCheck
Candidate PersonnelRecord(10)
termination Tue Oct 11 12:00:09 GMT 2007
name 10
start Tue Oct 11 12:04:37 GMT 2007
Invariant


Constraint AllSlotsTypeCorrect
Candidate PersonnelRecord(10)
termination Tue Oct 11 12:00:09 GMT 2007
name 10
start Tue Oct 11 12:04:37 GMT 2007
Invariant
Failure Reason
termination = Tue Oct 11 12:00:09 GMT 2007:Root::Date is not of type Root::Date, name = 10:Root::XCore::Integer is not of type Root::XCore::String