Saturday, January 12, 2008

Modeling Associations With Ecore

It's often been said that Ecore doesn't support associations. Although, like EMOF, it supports bidirectional references, it doesn't support the full generality of CMOFs notion of an association. An association in the general case consists of two or more features and those features can either be owned by the association or by the classes being associated. So while a simple association, where the ends are not owned by the association, is quite effectively modeled as a pair of references that are related as opposites, the full generality of an association is not directly supported by Ecore. But that doesn't mean they can't be modeled!

Let's start with a simple example to show how we can model associations, i.e., how we can map the CMOF concept of an association onto an EMOF realization using only classes. In all these examples we will be be associating an "A" and a "B" where each "A" is related via feature "bs" to at least 2 "Bs" and at most 4 "Bs" and each "B" is related via feature "as" to at least 3 "As" and at most 5 "As". I use the multiplicities so I can demonstrate how these multiplicity constraints propagate to the different parts of the realization in the screen captured tree views below.

In the simplest case these would just be a bidirectional references as are directly supported already, but suppose we want to carry some data on the link that associates the instances. We could model that like this:

So the idea is we model the association instance as "ABLink" and include with it the "data" which in this case is just a string. We arbitrarily pick "A" as the container for the link, and hence the "a" feature of "ABLink" is just the bidirectional opposite of "As" "aBLinks" feature. Then "B" also has a reference to the link via its own "aBLinks" feature and in this case too, the "b" feature of "ABLink" is the bidirectional opposite. Both "As" "bs" feature and "Bs" "as" feature are derived from their "aBLinks" feature by viewing the corresponding "b" or the "a" feature of each link. Populating a link involves adding the link to the link-holding features at each end. Note that I'll need to investigate how to describe the derived features' derivation declaratively so that it can be emulated in a dynamic model and generated in a static model, but you can imagine that declaring "bs" is derived via "aBLinks/b" would be pretty clear and simple.

Suppose we wanted to deal with the case that one of the two ends of the association was owned by the association instead; this is the approach you'd take if you could not modify "B" to add an "as" feature. We could do something like this:

In other words, we simply don't have the features in "B" and instead the "b" feature of "ABLink" will be the only way that A and B instances are associated. Note however that the multiplicity constraints for "B" have disappeared! This approach works well only if the "bs" feature that we've omitted has lower bound 0 and upper bounded unbounded, and even then, it's hard to find all the links involving any particular "B" starting with a "B" instance. Populating a link for this design involves adding it to the link-holding feature of "A and setting the "b" feature explicitly.

Here's a different approach that doesn't lose the multiplicity:

Here we've introduced "ABAssociation" as a container for managing all the link instances. It has a map that uses "BAMapEntry" to map the "key" which is of type "B" to the "ABLink" instances that involve B. Note that the "value" feature of the map entry captures the multiplicity that each "B" must have at least 3 "As" and at most 5 "As" though expressed in terms of the links. Note too that the "ABLink" has an "bAMapEntry" feature which is the bidirectional opposite of the "value" feature of "BAMapEntry" and now the "b" feature of the "ABLink" is derived again. So this approach enforces all the constraints. Also, we would provide convenience operations to get the "as" for a given "B", to get the link if it exists for a given "A"and "B", to create or update the link between a given "A" and "B", and finally to delete the link between a given "A" and "B". Populating a link directly involves adding the link to the link-holding feature of "A" as well as to the link holding feature of the "BAMapEntry".

Now for the most complex case, the holy grail of associations. In this case, neither "A" nor "B" can be modified as part of describing their association. Here's the approach:

Now the "ABAssociation" has two maps one from "A" to "B" and the other from "B" to "A" though expressed in terms of the links. We choose one arbitrarily to contain the links so the "value" feature of "ABMapEntry" is a containment and the "abMapEntry" feature of the link is its bidirectional opposite. The "bAMap" is just like in the previous case. Now both the "a" and the "b" feature of the link are derived from corresponding feature in their containing/referencing map entry. We provide the same convenience methods as the previous case, including an additional one to get the "bs" for a given "A". Populating a link directly involves adding the link to both maps. Note that the multiplicities are preserved in the two "value" features.

Voila! Who says Ecore doesn't support associations?

If you're interested, you can track the progress of 105920. It contains a zip of the complete model as well as the generated code for it. Note that the use of colors and fonts in the tree views reflects progress on 95934, that the multiplicities on operations and parameters is a work in progress, i.e., I drew my own icons pending good ones from the graphic designers, and that the ability to show "2..4" and "3..5" is a hack to make the screen captures more illustrative of the solution. Comments and feedback would be most welcome!

One a personal note, my vacation plans have been postponed for a week because the ship is in dry dock for longer than expected, so I'll have to wait for the first two weeks of February for my current mania to come to an end. And finally, what blog of mine would be complete without a gratuitous photo of something cool? I hate to disappointed people! I took this cool picture on a previous trip to the Caribbean where even the fungus are fabulously fascinating:

3 comments:

Darin Swanson said...

Ed...you are such a "fun guy" :-)

Conrad said...

Nice! It looks like you'll need constraints that the links have unique end objects (values of a and b features), ie, no two links can have the same end objects. Otherwise the multiplicities don't work (eg, on the abLinks feaures or on map values) because the link list could have duplicates.

Ed Merks said...

EMaps are already constrained such that no two map entries can have the same key, so given there is a map for each end type, that should be sufficient.