OCLConstraints

Timothy Lethbridge edited this page Jun 14, 2016 · 2 revisions
Clone this wiki locally

Constraints in Umple

Constraints have been added. This page is for the historical reference only..

For the first phase, a small subset of OCL will be chosen. We will also consider SQL constraints as we are doing this work.

This is only a draft.

Draft Syntax and Semantics of Umple constraints

In general, a constraint is contained in square brackets: .md

Context

The context of a constraint is the class, state or method in which it is embedded.

Naming

A name, followed by a colon may appear at the start of a constraint.

Invariants

Invariants appear in square brackets. Initially we will define them to have a left-hand-side (lhs), a right-hand side (rhs) and a Boolean operator. Certain functions will be available. Integers, Floats, Strings and Booleans will be the initially supported types. The lhs and rhs can initially be either constants, attributes, the functions match and length, references to an association with a 1 or 0..1 multiplicity, or references to attributes attached to such associations (recursively, separated by dots). Later on this will be extended.

Allowed operators: Binary: > < == <= >= != &&

Simple unary not: !

Examples:

class X {
  Integer a;
  String s;

  // for range checking
  [a > 0] [a < 3000]
  // [a > 0 && a < 3000] would be eqiivalent

  // for regular expression matching, introduce a builtin
  [s.match("^\$?\d+(\.(\d{2}))?$"")]

  // for string length
  [s.length() <= 12]
}
class Student {
    Integer age;
    immutable Integer minimumAge = 16;
    immutable Integer maximumAge = 120;
    Integer studyYear;
    Boolean isMature;
    [minAge: age >= minimumAge]
    [maxAge: age <= maximumAge]
}
// Constraint on an attribute in a state
// In this case, c must be always be positive, but in s2 must
// remain greater than 100. To enter int s2, c must be greater than 1000
class Y {
  Integer c;
  [c >= 0]
  sm {
    s1 {
      e1 [c > 1000] -> s2;
    }
    s2 {
      e1 -> s1;
      [c > 100];
    }
  }
}

There is an important difference between states S1 and S2 above. In the first case e1 [C>1000] -> S2, the condition c>1000 is checked only once upon the transition, and never again. Meaning that the value of c could go down below 1000 while at state S2.

Preconditions and postconditions

Integer method m(Integer arg) {
 [pre: arg > 5]
 // rest of stuff that we don't interpret
 [post: result < 10]
}

For named pre/postconditions:

Integer method m(Integer arg) {
 [overFive pre: arg > 5]
 // rest of stuff that we don't interpret
 [underTen post: result < 10]
}

Mixins

Constraints would work with Umple's mixin capability. This means that a constraint could be in a separate class declaration which might be in a separate file. So the earlier example could be.

class X {
  Integer a;
  String s;
}
class X {
  // for range checking
  [a > 0] [a < 3000]
  // [a > 0 && a < 3000] would be eqiivalent

  // for regular expression matching, introduce a builtin
  [s.match("^\$?\d+(\.(\d{2}))?$"")]

  // for string length
  [s.length() <= 12]
}

Or even split into several separate parts, not just two.

API

By default, all constraints are checked, and the set methods return false and refuse to make the change if the constraint would be violated. Furthermore they would set an error construct that could be queried with.

getConstraintViolations()

This would return a collection, possibly empty of current constraint violations. One could call while constraints are disabled (see below), or after enableConstraints has thrown an exception (see below). Any other time it is guaranteed to return an empty collection. This method would only exist in classes with constraints defined.

Any class with a constraint will have a method

disableConstraints()

that causes the constraints to be ignored from this point on (although not state machine guards). Normally the intent is that this would be temporary, but it could be a normal in some designs, and even injected in the constructor using 'before constructor'. In that case, the programmer woud use getConstraintViolations on each object whenever they want to check the sanity of the system.

Each class with constraints would have:

enableConstraints()

that:

  • Checks if there are any violations and throws an exception if there are
  • Causes the default constraint-checking to be re-established.

Subsequent additions

The following will be added in a later phase

Implication

a => b

[age > 25 => isMature]
[age > 25 => studyYear > 1]

Implementation and code generation

We would generate umple 'beforeSetX' statements in metamodel so we do not have to actually change the code generation to make this work.

  1. Constraints are written using Umple syntax (language independent).

  2. Constraints are the translated into the target language (ie. Java, PHP, Ruby).

  3. Result from step 2 is then injected into generated code (using after and before injections).

References about OCL and constraints

For background information on OCL, refer to the following

UML2.0OCL Specification

  • DresdenOCL provides a set of tools to parse and evaluate OCL constraints on various models like UML, EMF and Java. http://www.dresden-ocl.org/index.php/DresdenOCL

  • JML. It is a specification language that allows you to write constraints in Java Code. These constraints are then validated during the execution of the program.

  • An interesting paper: Carmen Avila, Guillermo Flores, Jr., and Yoonsik Cheon. A Library-Based Approach to Translating OCL Constraints to JML Assertions for Runtime Checking, International Conference on Software Engineering Research and Practice, July 14-17, 2008, Las Vegas, Nevada, U.S.A., pages 403-408, July 2008

  • Another paper OCL to Java: Jos Warmer and Anneke Kleppe. 2003. The Object Constraint Language: Getting Your Models Ready for MDA (2 ed.). Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA.