Relationship navigation

Traversal from the local instance to one on the other side of an association can be accomplished with the hop / operator.

An instance of Flot, for example, can quickly access its Flot Specification across using the / hop operator like this:

my Spec .= /is specified by/Flot Specification

The relationship phrase or name or both can be used to unambiguously specify the access.

my Spec .= /R4/Flot Specification

my Spec .= /R4/is specified by/Flot Specification

In xUML, relationship names must be unique within a domain, but there is no such constraint, other than common sense, on association phrases. So, if there is any ambiguity, you must include the relationship name.

Multiple associations may be traversed by chaining the hops.

my Bank .= /R4/Cabin/R2/Shaft/R1/Bank

Class names are not necessary, but it is best to be as clear as possible.

A smart editor with access to the class model can provide autofill assistance so that a relationship path can be entered with very few keystrokes.

Reflexive navigation

Reflexive associations require some special handling. For example, the destination class is reached through two access directions on the same association, so the phrase in the access direction must always be specified.

Assume, for example, that a number of aircraft are following each other in a holding pattern. Starting from some instance of Aircraft, to following action assigns the next aircraft in sequence:

follow ac .= /R3/is following/Aircraft

Note that the class name at the end isn’t really necessary to resolve the path. (Having two reflexive associations on the same class with the same phrase names is not currently illegal in xUML, but it probably should be).

Also note that the .= assignment operator is used since at most one instance can be selected on R3 which is a 0..1:0..1 reflexive association.

Now let’s say that you want to find the nearest Aircraft in sequence at a higher altitude.

higher follow ac .= /R3/is following/~/( its.Altitude > )

The repeated hop symbol /~/indicates continued traversal until either the criteria that follows is met or there are no more links to hop or a link back to the local object is encountered. This last case prevents a cycle of references from being traversed endlessly.

The selection predicate will often compare an attribute of the referenced class with the same attribute of the local instance. To avoid confusion, the attribute of the referenced instance is prefaced with the keyword its, with that of the local object optionally left implicit. In the above example, the criteria reads: “with a greater altitude”.

Now let’s say that we want the next aircraft whose altitude is near the local object’s altitude within some higher/lower delta.

same level follow ac .= /R3/is following/~/( (its.Altitude - Altitude).abs <= delta )

Note the use of the abs operation to get the absolute value of the difference.

Rather than closest, to find the leading aircraft that is the furthest number of hops away on R3, use the repeated hop to end /~| symbol.

leading higher follow ac .= /R3/is following/~|( its.Altitude > )

Alternatively, you can compare to the local attribute eliminating the need for the its keyword:

leading higher follow ac .= /R3/is following/~|( Altitude <= )

Chained navigation and selection

You can hop around narrowing selection quite a bit in a single line. Consider the following action language that selects one or more elevator Shafts best suited to service a floor call.

in service shafts ..= /R1/Shaft( In service )
best cabin .=. in service shafts/R2/Cabin(
^-Estimated travel delay( To floor : in.calling floor, Calling dir: in.Dir )
)
return ( best cabin/R2/Shaft )

In the example above, we hop to all Shafts whose In service attribute is true and save them in the in service shafts instance set variable. Then we hop to all related Cabins sorting for the least returned value from the Estimated travel delay method invoked on each Cabin instance and assign one of them. The associated Shaft instance is then returned.


The traditional xUML approach of using a reflexive association to model sequence is being re-evaluated in light of more powerful relational operations that may be available to rank and order instances. In particular, 0..1:0..1 reflexive associations are often problematic when it comes to capturing real world constraints. We’re hoping to formulate and recommend a better approach that is consistent with current relational theory. Until then, we’ll keep using reflexive associations and the attendant action language to manage constraints.