Safe navigation tutorial

Safe navigation tutorial

This tutorial demonstrates the new safe navigation facilities of Eclipse Mars; Eclipse 4.5, EMF 2.11, OCL 6.0.

Overview

In this example you will

  • learn about navigation hazards

  • switch on safe navigation validation

  • use safe navigation to eliminate hazards

  • use null free declarations to avoid many safe navigation hazards

References

This tutorial continues the OCLinEcore tutorial.

Evaluation hazards

Evaluation of OCL expressions can give invalid results for internal problems

  • divide by zero

  • index out of bound for an Ordered Collection

  • most navigations of an operation or property from a null source

In this tutorial we will show how to eliminate the hazards of unsafe navigation from null.

Enable Safe Navigation Diagnosis

Safe navigation is too new and experimental to be enabled by default. You must therefore enable it explicitly by selecting the OCL->Unified Pivot Binding settings from the workspace Window->Preferences. You may alternatively set project-specific preferences from Project property pages.

Change the Potential null navigation and Redundant safe navigation to Error (or warning).

Safe Navigation Diagnosis

We will continue the OCLinEcore tutorial, which you may jump to the end of by New ->Example... ->OCL Plugins ->OCLinEcore Tutorial.

Select Tutorial.ecore and open with the OCLinEcore Editor. 8 errors appear on 5 lines.

A bit depressing; 5 out of 7 OCL lines have hazards on a long standing example. The problems arise wherever a null is permitted.

Non-collection values may be null whenever the multiplicity is implicitly or explicitly MyType[?], which permits either an instance of MyType or null. The alternative MyType[1] prohibits a null value. The example metamodel is comparatively good with many properties such as Loan::book defined as as Book[1]. However Loan::date is Date[?] which seems unwise; why should a Loan have an unknown Date? Book::library is correctly Library[?] since there is no reason why Books may not found in Bookshops or Homes.

We will examine the two errors after expanding short forms.

self.library.loans violates the UnsafeSourceCannotBeNull constraint because the source, self.library, can be null as a consequence of the library[?] multiplicity.

Collection values, which are almost the raison d’etre of OCL, are a disaster safe-navigation-wise. Any OCL collection may contain a null value and so any OCL iteration may have a null iterator. Consequently the implicit iterator is typed as Loan[?] and the source of loan.book is also unsafe.

Safe Navigation Operators

Languages such as Groovy have introduced a safe navigation operator to mitigate problems with null navigation. It is proposed that OCL 2.5 will do so too. Eclipse OCL provides a prototype implementation.

OCL provides two navigation operators

  • the object navigation operator "."

  • the collection navigation operator "->".

Safe navigation adds

  • the safe object navigation operator "?."

  • the safe collection navigation operator "?->".

The safe object navigation operator replaces any null navigation by null. Where a is an object value, a?.b is therefore equivalent to

let a' = a in if a' <> null then a'.b else null endif

The safe collection navigation operator eliminates all null terms from collection sources. a?->b is therefore equivalent to

a->excluding(null)->b

The safe implicit collection navigation operator similarly eliminates all null terms from collection. Where a is a collection value, a.b is therefore equivalent to

a->excluding(null)->collect(b)

We may use these operators to make the warnings go away.

The first replacement for library?.loans is reasonable. The library really can be null and so, if it is null, the shortform execution is null->select(book = self). Use of a collection operator on a non-collection object such as null causes oclAsSet() to be invoked which for null gives giving an empty set. Therefore null.oclAsSet()->select(...) selects elements from an empty set ensuring that the loans from a null library are an empty collection.

The second replacement for loans?->select makes the problem go away, but in practice requires almost every collection navigation operator to be prefixed lexically by “?” and operationally by an exclude(null).

Null-free Collections

OCL and UML support four permutations of ordered/not-ordered, unique/not-unique to give useful Collection behaviors.

OCL unfortunately allows any collection to contain null, even though null collection elements are undesirable in almost all applications, and as we have just seen safe, navigation imposes a redundant exclude(null) on many collection accesses.

The need for exclude(null) can be eliminated if OCL collections can be declared to be null-free, potentially giving 8 rather than 4 possible collection behaviors.

UML and Ecore declarations of collections such as MyType[2..*] {ordered} support bounds, whereas Complete OCL supports nested collections such as Set(Sequence(MyType)). UML alignment for OCL 2.5 supports nested bounded collections such as Set(Sequence(MyType[*])[+]); a Set of one or more Sequences of zero or more MyTypes.

We can extend this notation by suffixing an element multiplicity following each collection multiplicity so that each element may be

  • non-null, explicitly [...|1]

  • implicitly or explicitly null/not-null [...|?]

It is not useful to have null loans so we can change the multiplicity of Library::loans to Loan[*|1]; zero or more Loan objects where each loan is not-null.

The problem with the iterator is now replaced by one with the iteration. The SafeSourceCanBeNull constraint is now violated because the source library?.loan cannot provide null elements as a consequence of the [==|1==]* multiplicity. Note that the extended multiplicity is shown in messages and hover text to assist in understanding null-ness.

Revert back to loans->select and the new problem goes away; changing the multiplicity to declare a null-free collection makes the original expression safe without an additional safe navigation operator.

Declaring Null-free Collections in Ecore

We have just seen an extension to the multiplicity syntax so that in OCLinECore a null-free collection may be declared by the [...|1] extended per-element multiplicity.

Ecore does not support null-free collections and so behind the scenes this is represented by an EAnnotation.

    <eStructuralFeatures xsi:type="ecore:EReference" name="loans" ordered="false"
        upperBound="-1" eType="#//Loan" containment="true">
      <eAnnotations source="http://www.eclipse.org/OCL/Collection">
        <details key="nullFree" value="true"/>
      </eAnnotations>
    </eStructuralFeatures>

Declaring Null-free Collections in UML

UML does not support null-free collections and so an OCLforUML profile is introduced to remedy this and other deficiencies.

A Collection stereotype may be applied to a TypedElement such as a Parameter or Property so that the Collection::isNullFree property defines the required null-free-ness.

Applying a stereotype to all collection properties and parameters is a little tedious and may be avoided by instead applying the Collections stereotype to Classes or even Packages. The null-free-ness is determined by looking first for a Collection stereotype, then searching the container hierarchy for the nearest Collections stereotype.

A single Collections stereotype application on a Package is sufficient to declare all its collections null-free This is often appropriate, however if any collections can contain nulls, the package-level Collections stereotype must be overridden for each TypedElement where the collection may contain a null.