Question

I have a simple EMF model of the following type:

EClass FooGroup {
    EString name;
    @Properties(containment=true, upper=-1)
    List<Node> nodes;
}
EClass BarGroup {
    EString name;
    @Properties(containment=true, upper=-1)
    List<Node> nodes;
}
EClass Node {
    EString name;
}

I want to show all names of the nodes in a simple TableViewer, together with the name of their parent group. Unfortunately, eContainer() is an operation and not a feature. This means that I can not use the JFace Databinding framework to link this property to the GUI.

How can I solve this problem? Do I create a derived feature? Are there any tricks?

Was it helpful?

Solution

I can't think of a way to use the eContainer in EMF-databinding, but it wouldn't be so hard to add a designated parent-reference to a node, and to update it programmatically whenever the eContainer changes.

Another solution is to add a parent and then change the generated code:

/*
 * @generated NOT
 */
public Group getParent() {
    if (eContainer() instanceof Group) {
        return (Group) eContainer();
    }
    return null;
}

Then you could use a FeaturePath to get the EMFProperty like this:

    IEMFProperty groupNameProperty = EMFProperties.value(
      FeaturePath.fromList(
        ModelPackage.Literals.NODE__PARENT,
        ModelPackage.Literals.GROUP__NAME
      )
    );

OTHER TIPS

If you never need to move the nodes from one of the containers to the other, and you never mix nodes from the different contains together, then you can used the following solution.

The idea is to create sub-classes for the node, one for each location in which they are stored. The sub-classes can then have a normal container reference.

EDIT: Reading your question more carefully I see that you must be able to treat the objects uniformly and it that case this doesn't work. But maybe my third solution will!

I'll leave this answer here anyway. Maybe it will be useful for other people.

class FooGroup {
    String name
    contains FooNode[] nodes opposite group
}

class BarGroup {
    String name
    contains BarNode[] nodes opposite group
}

class Node {
    String name
}

class BarNode extends Node {
    container BarGroup group opposite nodes
}

class FooNode extends Node {
    container FooGroup group opposite nodes
}

There is a standard way to do this:

Create a reference in the child object to the parent object with the appropriate opposite and container attributes set to it.

In this way the relationship between the classes is apparent in model, and it is maintained automatically so you don't have to write any custom code.

The following is an example of this in Xcore:

class Master {
    contains Slave[] slaves opposite master
}

class Slave {
    container Master master opposite slaves
}

And why not have the same thing in Ecore also, while we're here:

<eClassifiers xsi:type="ecore:EClass" name="Master">
  <eStructuralFeatures xsi:type="ecore:EReference" name="slaves" eType="#//Slave"
      containment="true" eOpposite="#//Slave/master"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Slave">
  <eStructuralFeatures xsi:type="ecore:EReference" name="master" eType="#//Master"
      eOpposite="#//Master/slaves"/>
</eClassifiers>

It is possible to create custom container-property class that solves this.

It that way you don't have to modify your model classes to be able to bind to the container. The new property will be useful for all e-objects.

public static IValueProperty containerProperty() {
    return new ContainerProperty();
}

public class ContainerProperty extends ValueProperty {
    @Override
    public IObservableValue observe(Realm realm, Object source) {
        return Observables.constantObservableValue(realm, 
            source == null ? null : ((EObject) source).eContainer(), 
            getValueType());
    }

    @Override
    protected Object doGetValue(Object source) {
        return ((EObject) source).eContainer();
    }

    @Override
    public Object getValueType() {
        return EObject.class;
    }
};

Example use:

IObservableValue obs = containerProperty().observeDetail(node);

This class doesn't listen to changes of the container. But I think it would be possible to implement such a property. Maybe using the SimpleValueProperty class would be suitable for that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top