In some situations, it is necessary to return a set of elements in a mapping. If this size of the set is predefined, it is possible to use a tuple. If the size is undefined, it is necessary to use a Collection. But how to use them? A first solution would be to write something like:

mapping A::A2B() :  Set(B) {
	result += object B {
		id := self.id;
		b := "something";
	};
}

However, this solution does not work… We get in the result what I will call here a lose element, i.e. an unused object that is put in the root of the hierarchy. In general, a lose element occurs when an element is created, but is not correctly associated to a container object. The problem here is that a Set is an immutable Collection! A immutable Collection is a group of elements that cannot be changed after it has been created. Therefore, each time a Set is modified, a new Set is created. All Collection types defined in OCL are immutable, i.e., Set, OrderedSet, Bag, and Sequence are immutable.

So, how can we return a Collection? A solution is to create an init section. In this section it is possible to define what will be the reference for the “result” variable. Our solution would be:

mapping A::parentA2B():Set(B) {
	init {
		result := Set{};
		result += object B {
			id := self.id;
			b := "something";
		};
	}
}

This solution may have a small problem… If you use mapping inheritance, you will have a problem with your Set. For instance, in the example:

mapping A::parentA2B():Set(B) {
	init {
		if result->isEmpty() then
			result := Set{}
		endif;
		result += object B {
			id := self.id;
			b := "parent";
		};
	}
}

mapping A::sonA2B(): Set(B) inherits A::parentA2B {
	init {
		result := Set{};
		result += object B {
			id := self.id;
			b := "son";
		};
	}
}

Someone would expect that the result of calling sonA2B would be a size 2 Set. But it is not. The problem is that the sonA2B creates a Set and puts an object. ParentA2B uses this Set and creates another Set from it by adding the second element. But the sonA2B uses its original Set, not the one created by parentA2B. So, it will result in a Set of a size 1 (what is more unpleasant is that we get a lose object). (In a future post I will discuss the details of mapping inheritance).

So, what to do? A more elegant solution is to use a List. A List is a mutable Collection, defined in the QVT specification (another mutable Collection is the Dictionary). Therefore, it is possible to change it after its creation. Using a list, the solution would be:

mapping A::A2B(): List(B) {
	result->add(object B {
		id := self.id;
		b := "something";
	});
}

But there is a little problem. A List can have repeated elements, while a Set cannot. So, be careful and select the best solution for you.

(Here it is the QVTo code and the ecore metamodel.)

2 thoughts on “Returning a Set in QVTo Mappings

  1. Phew! This one saved my day, thanks! 🙂 I was trying to return a set from a recursive QVT mapping, and that didn’t work, of course.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

*