Scripting Containment Relationships

The BOM editor allows you to say that one class is contained within another. For example, if there were classes for Car and Widget, the Widget class can be said to be contained by the Car class. For the contained relationship, the contained objects are affected by the container’s lifecycle events. So when the Container (Car in our example) is destroyed, all the contained (Widget in our example) objects will be destroyed too.

The diagram below shows that Widget objects can be contained by a Car or a Bike. However, for an individual Widget object instance, it can only belong to a Car or a Bike instance, not both at once, as when its parent object is destroyed, the child object is destroyed also.

There are two ways to model this in the BOM editor. First, a Composition link can be drawn between the two classes, like this:

Alternatively, the Car and Bike class can be given an attribute called widgets of type Widget, as shown below:

Both of these two Car/Widget relationships appear the same when scripting. There is an attribute of the Car object called widgets, which will be a List type, that can contain Widget objects. This would be processed in a similar way to the List processing examples already covered.

For example, to create a Car and add two Widgets, we can write:

var car = com_example_scriptingguide_containment_Factory.createCar();
car.model = "Saloon";
var widget = com_example_scriptingguide_containment_Factory.createWidget();
widget.description = "M8 Bolt";	
car.widgets.add(widget);
widget = com_example_scriptingguide_containment_Factory.createWidget ();
widget.description = "M8 Nut";
car.widgets.add(widget);

We have already mentioned that the contained objects can only be contained by one container, so an object cannot be added to the same container more than once. Another aspect of the way that this relationship is enforced is that if an object is contained within container A, and it is then added to container B, as part of the process of inserting the object into container B, it is implicitly removed from container A.

For example, if you have a car object that contains a number of Widgets, and you attempt to copy them into another Car or Bike object using the following script, it will fail as described below:

for (var iter = car.widgets.listIterator(); iter.hasNext(); )
{
    bike.widgets.add(iter.next());
}

As mentioned above, adding the Car’s Widgets to the Bike removes them from the Car. This interferes with the iterator which is attempting to iterate over a changing list. When a list is being iterated over, it should not be changed unless by means of the ListIterator methods. Instead, the following should be done:

for (var iter = car.widgets.listIterator(); iter.hasNext(); )
{
    bike.widgets.add(ScriptUtil.copy(iter.next()));
}

The above script takes copies of the objects, and leaves the original copies contained in the Car object. You can also use the copyAll() method discussed in the previous section:

bike.widgets.addAll(ScriptUtil.copyAll(car.widgets));