Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » Modeling (top-level project) » EMF Derived Attribute Notifier
EMF Derived Attribute Notifier [message #383873] Wed, 07 January 2009 12:28 Go to next message
No real name is currently offline No real name
Messages: 4
Registered: July 2009
Junior Member
If you have a derived attribute in EMF and change one of the attributes
that the derived value depends on, then no notification will be sent for
the derived attribute - so it will not change in any editor etc. You have
to write these yourself.

Here's a class I wrote that I've found useful and perhaps it may be useful
to others (all comments etc. gratefully received). You can specify your
derived attribute, and also which other attributes it depends on.
Listeners are then added as appropriate and when any of your dependant
attributes change, you'll fire an event for your derived attribute.

E.g. You have a Class Library and a set of Books in it. Each Book has a
pageCount attribute. The Library has a derived attribute totalPageCount
that adds up all the pages of all the books. Then you can just write...

/**
* @generated NOT
*/
protected LibraryImpl() {
super();
DerivedAttributeAdapter daa = new DerivedAttributeAdapter(this,
MyPackage.LIBRARY__TOTALPAGECOUNT);
daa.addNavigatedDependency(MyPackage.LIBRARY__BOOKS,
MyPackage.BOOK__PAGECOUNT);
}

A local dependency is another attribute in the same class. A navigated
dependency is to an attribute in a class that you have an association
with. You can add many local dependencies, but only one remote one (though
it can be a one to many association).

Here it is...

package put.it.where.you.like;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;

public class DerivedAttributeAdapter extends AdapterImpl {
private final EObjectImpl source;
private final int derivedFeature;

private List<Integer> localFeatures = new ArrayList<Integer>();

// TODO this lot could be put into a subclass and put in a list to allow
for
// multiple navigated dependencies
private int dependantFeature = Notification.NO_FEATURE_ID;
private Class<?> dependantClass = null;
private int navigationFeature = Notification.NO_FEATURE_ID;

private AdapterImpl dependantAdapter = new AdapterImpl() {

@Override
public void notifyChanged(Notification msg) {

if (msg.getFeatureID(dependantClass) == dependantFeature &&
msg.getEventType() == Notification.SET) {
notifyDerivedAttributeChange();
}
}
};

/*
* Convenience constructor for a local and navigated dependency
*/
public DerivedAttributeAdapter(EObjectImpl source, int derivedFeature,
int navigationFeature, int dependantFeature, int localFeature) {
this(source, derivedFeature);
addNavigatedDependency(navigationFeature, dependantFeature);
addLocalDependency(localFeature);
}

/*
* Convenience constructor for a navigated dependency
*/
public DerivedAttributeAdapter(EObjectImpl source, int derivedFeature,
int navigationFeature, int dependantFeature) {
this(source, derivedFeature);
addNavigatedDependency(navigationFeature, dependantFeature);
}

/*
* Convenience constructor for a local dependency
*/
public DerivedAttributeAdapter(EObjectImpl source, int derivedFeature,
int localFeature) {
this(source, derivedFeature);
addLocalDependency(localFeature);
}

public DerivedAttributeAdapter(EObjectImpl source, int derivedFeature) {
super();
this.source = source;
this.derivedFeature = derivedFeature;
source.eAdapters().add(this);
}

public void addNavigatedDependency(int navigationFeature, int
dependantFeature) {
this.dependantFeature = dependantFeature;
this.navigationFeature = navigationFeature;
this.dependantClass =
source.eClass().getEStructuralFeature(navigationFeature).get EType().getInstanceClass();
}

public void addLocalDependency(int localFeature) {
localFeatures.add(localFeature);
}

@Override
public void notifyChanged(Notification notification) {
if (notification.getFeatureID(source.getClass()) == navigationFeature) {
switch (notification.getEventType()) {
// TODO support ADD_MANY/REMOVE_MANY?
case Notification.ADD:
EObject added = (EObject) notification.getNewValue();
added.eAdapters().add(dependantAdapter);
break;
case Notification.SET:
EObject newValue = (EObject) notification.getNewValue();
EObject oldValue = (EObject) notification.getOldValue();
if (oldValue != null) oldValue.eAdapters().remove(dependantAdapter);
if (newValue != null) newValue.eAdapters().add(dependantAdapter);
break;
case Notification.REMOVE:
EObject removed = (EObject) notification.getOldValue();
removed.eAdapters().remove(dependantAdapter);
break;
default:
return; // No notification
}
notifyDerivedAttributeChange();
} else if
(localFeatures.contains(notification.getFeatureID(source.get Class()))) {
if (notification.getEventType() == Notification.SET) {
notifyDerivedAttributeChange();
}
}
}

private void notifyDerivedAttributeChange() {
if (source.eNotificationRequired()) {
source.eNotify(new ENotificationImpl(source, Notification.SET,
derivedFeature, null, source.eGet(
derivedFeature, true, true)));
}
}

}
Re: EMF Derived Attribute Notifier [message #383874 is a reply to message #383873] Wed, 07 January 2009 14:22 Go to previous messageGo to next message
Eclipse User
Originally posted by: cdamus.zeligsoft.com

Hi, pvm,

Thanks for the contribution! However, you will get more of your target
audience by posting to the EMF newsgroup, which I have included in my reply.

Cheers,

Christian

pvm wrote:
> If you have a derived attribute in EMF and change one of the attributes
> that the derived value depends on, then no notification will be sent for
> the derived attribute - so it will not change in any editor etc. You
> have to write these yourself.
>
> Here's a class I wrote that I've found useful and perhaps it may be
> useful to others (all comments etc. gratefully received). You can
> specify your derived attribute, and also which other attributes it
> depends on. Listeners are then added as appropriate and when any of your
> dependant attributes change, you'll fire an event for your derived
> attribute.
>
> E.g. You have a Class Library and a set of Books in it. Each Book has a
> pageCount attribute. The Library has a derived attribute totalPageCount
> that adds up all the pages of all the books. Then you can just write...
>
> /**
> * @generated NOT
> */
> protected LibraryImpl() {
> super();
> DerivedAttributeAdapter daa = new DerivedAttributeAdapter(this,
> MyPackage.LIBRARY__TOTALPAGECOUNT);
> daa.addNavigatedDependency(MyPackage.LIBRARY__BOOKS,
> MyPackage.BOOK__PAGECOUNT);
> }
>
> A local dependency is another attribute in the same class. A navigated
> dependency is to an attribute in a class that you have an association
> with. You can add many local dependencies, but only one remote one
> (though it can be a one to many association).
>
> Here it is...
>
> package put.it.where.you.like;
>
> import java.util.ArrayList;
> import java.util.List;
>
> import org.eclipse.emf.common.notify.Notification;
> import org.eclipse.emf.common.notify.impl.AdapterImpl;
> import org.eclipse.emf.ecore.EObject;
> import org.eclipse.emf.ecore.impl.ENotificationImpl;
> import org.eclipse.emf.ecore.impl.EObjectImpl;
>
> public class DerivedAttributeAdapter extends AdapterImpl {
> private final EObjectImpl source;
> private final int derivedFeature;
>
> private List<Integer> localFeatures = new ArrayList<Integer>();
>
> // TODO this lot could be put into a subclass and put in a list to
> allow for
> // multiple navigated dependencies
> private int dependantFeature = Notification.NO_FEATURE_ID;
> private Class<?> dependantClass = null;
> private int navigationFeature = Notification.NO_FEATURE_ID;
>
> private AdapterImpl dependantAdapter = new AdapterImpl() {
>
> @Override
> public void notifyChanged(Notification msg) {
>
> if (msg.getFeatureID(dependantClass) == dependantFeature &&
> msg.getEventType() == Notification.SET) {
> notifyDerivedAttributeChange();
> }
> }
> };
>
> /*
> * Convenience constructor for a local and navigated dependency
> */
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature, int navigationFeature, int dependantFeature, int
> localFeature) {
> this(source, derivedFeature);
> addNavigatedDependency(navigationFeature, dependantFeature);
> addLocalDependency(localFeature);
> }
>
> /*
> * Convenience constructor for a navigated dependency
> */
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature, int navigationFeature, int dependantFeature) {
> this(source, derivedFeature);
> addNavigatedDependency(navigationFeature, dependantFeature);
> }
>
> /*
> * Convenience constructor for a local dependency
> */
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature, int localFeature) {
> this(source, derivedFeature);
> addLocalDependency(localFeature);
> }
>
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature) {
> super();
> this.source = source;
> this.derivedFeature = derivedFeature;
> source.eAdapters().add(this);
> }
>
> public void addNavigatedDependency(int navigationFeature, int
> dependantFeature) {
> this.dependantFeature = dependantFeature;
> this.navigationFeature = navigationFeature;
> this.dependantClass =
> source.eClass().getEStructuralFeature(navigationFeature).get EType().getInstanceClass();
>
> }
>
> public void addLocalDependency(int localFeature) {
> localFeatures.add(localFeature);
> }
>
> @Override
> public void notifyChanged(Notification notification) {
> if (notification.getFeatureID(source.getClass()) ==
> navigationFeature) {
> switch (notification.getEventType()) {
> // TODO support ADD_MANY/REMOVE_MANY?
> case Notification.ADD:
> EObject added = (EObject) notification.getNewValue();
> added.eAdapters().add(dependantAdapter);
> break;
> case Notification.SET:
> EObject newValue = (EObject) notification.getNewValue();
> EObject oldValue = (EObject) notification.getOldValue();
> if (oldValue != null)
> oldValue.eAdapters().remove(dependantAdapter);
> if (newValue != null)
> newValue.eAdapters().add(dependantAdapter);
> break;
> case Notification.REMOVE:
> EObject removed = (EObject) notification.getOldValue();
> removed.eAdapters().remove(dependantAdapter);
> break;
> default:
> return; // No notification
> }
> notifyDerivedAttributeChange();
> } else if
> (localFeatures.contains(notification.getFeatureID(source.get Class()))) {
> if (notification.getEventType() == Notification.SET) {
> notifyDerivedAttributeChange();
> }
> }
> }
>
> private void notifyDerivedAttributeChange() {
> if (source.eNotificationRequired()) {
> source.eNotify(new ENotificationImpl(source,
> Notification.SET, derivedFeature, null, source.eGet(
> derivedFeature, true, true)));
> }
> }
>
> }
>
>
Re: EMF Derived Attribute Notifier [message #383875 is a reply to message #383874] Wed, 07 January 2009 15:21 Go to previous messageGo to next message
Ed Merks is currently offline Ed Merks
Messages: 26070
Registered: July 2009
Senior Member
This is a multi-part message in MIME format.
--------------040707080001020101050909
Content-Type: text/plain; charset=ISO-8859-15; format=flowed
Content-Transfer-Encoding: 7bit

Guys,

I keep wondering about the most general way to do this...

One interesting approach that Boris Bokowski explained to me is if you
have something like read "notification" (as in
https://bugs.eclipse.org/bugs/show_bug.cgi?id=247130) you could
automatically add listeners to all the objects that are accessed while
the derived attribute is being computed. Of course this requires so
global setup---an access listener attached to all objects in the
resource set as well as easy hookup into that global listener---but it
would work for any derivation pattern...

There's also the work underway in
https://bugs.eclipse.org/bugs/show_bug.cgi?id=216701 for specifying a
derived feature's behavior declaratively. This would seem potentially a
good place to try to fit in the design below.

I'll comment specifically on the text below.


Christian W. Damus wrote:
> Hi, pvm,
>
> Thanks for the contribution!
Yes, they're always a good thing. :-)
> However, you will get more of your target audience by posting to the
> EMF newsgroup, which I have included in my reply.
>
> Cheers,
>
> Christian
>
> pvm wrote:
>> If you have a derived attribute in EMF and change one of the
>> attributes that the derived value depends on, then no notification
>> will be sent for the derived attribute - so it will not change in any
>> editor etc. You have to write these yourself.
>>
>> Here's a class I wrote that I've found useful and perhaps it may be
>> useful to others (all comments etc. gratefully received). You can
>> specify your derived attribute, and also which other attributes it
>> depends on. Listeners are then added as appropriate and when any of
>> your dependant attributes change, you'll fire an event for your
>> derived attribute.
A great way to contribute is to write an EMF Recipe for others to follow
and post to the newsgroup to announce you've done that so people will
tend to find it and provide feedback quickly:

http://wiki.eclipse.org/EMF/Recipes

>>
>> E.g. You have a Class Library and a set of Books in it. Each Book has
>> a pageCount attribute. The Library has a derived attribute
>> totalPageCount that adds up all the pages of all the books. Then you
>> can just write...
>>
>> /**
>> * @generated NOT
>> */
>> protected LibraryImpl() {
>> super();
>> DerivedAttributeAdapter daa = new DerivedAttributeAdapter(this,
>> MyPackage.LIBRARY__TOTALPAGECOUNT);
>> daa.addNavigatedDependency(MyPackage.LIBRARY__BOOKS,
>> MyPackage.BOOK__PAGECOUNT);
>> }
Note that an interesting alternative to create an adapter and adding it
to the list is to make your objects "self adapting." I.e., you can
specialize eNotificationRequired to always return true so that when
there are changes eNotify will always be called, even if there are no
adapters listening. Then you can specialize eNotify (remembering to
call super!) just like you specialize the notifyChanged method of the
adapter. It saves quite a bit of space. I see your implementation is
nicely encapsulated and reusable as an adapter though...
>>
>> A local dependency is another attribute in the same class. A
>> navigated dependency is to an attribute in a class that you have an
>> association with. You can add many local dependencies, but only one
>> remote one (though it can be a one to many association).
>>
>> Here it is...
>>
>> package put.it.where.you.like;
>>
>> import java.util.ArrayList;
>> import java.util.List;
>>
>> import org.eclipse.emf.common.notify.Notification;
>> import org.eclipse.emf.common.notify.impl.AdapterImpl;
>> import org.eclipse.emf.ecore.EObject;
>> import org.eclipse.emf.ecore.impl.ENotificationImpl;
>> import org.eclipse.emf.ecore.impl.EObjectImpl;
>>
>> public class DerivedAttributeAdapter extends AdapterImpl {
>> private final EObjectImpl source;
You should generally refer to EObjectImpl only in the extends of a
derived class and no where else. Use EObject or InternalEObject for all
other purposes. After all, other implementation classes can directly
extend BasicEObjectImpl, MinimalEObjectImpl from
https://bugs.eclipse.org/bugs/show_bug.cgi?id=252501 is such an example.
>> private final int derivedFeature;
>>
>> private List<Integer> localFeatures = new ArrayList<Integer>();
>>
>> // TODO this lot could be put into a subclass and put in a list
>> to allow for
>> // multiple navigated dependencies
>> private int dependantFeature = Notification.NO_FEATURE_ID;
>> private Class<?> dependantClass = null;
>> private int navigationFeature = Notification.NO_FEATURE_ID;
>>
>> private AdapterImpl dependantAdapter = new AdapterImpl() {
>>
>> @Override
>> public void notifyChanged(Notification msg) {
>>
>> if (msg.getFeatureID(dependantClass) == dependantFeature
It might be better to use getFeature and then you only need to record an
EStructuralFeature object to compare it to (which you can get via
XyzPackage.Literals.<class-name>__<feature-name>)
>> && msg.getEventType() == Notification.SET) {
The second test is cheaper than the first test, so is likely to be more
efficient to have it first.
>> notifyDerivedAttributeChange();
>> }
>> }
>> };
>>
>> /*
>> * Convenience constructor for a local and navigated dependency
>> */
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature, int navigationFeature, int dependantFeature, int
>> localFeature) {
>> this(source, derivedFeature);
>> addNavigatedDependency(navigationFeature, dependantFeature);
>> addLocalDependency(localFeature);
>> }
>>
>> /*
>> * Convenience constructor for a navigated dependency
>> */
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature, int navigationFeature, int dependantFeature) {
>> this(source, derivedFeature);
>> addNavigatedDependency(navigationFeature, dependantFeature);
>> }
>>
>> /*
>> * Convenience constructor for a local dependency
>> */
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature, int localFeature) {
>> this(source, derivedFeature);
>> addLocalDependency(localFeature);
>> }
>>
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature) {
>> super();
>> this.source = source;
>> this.derivedFeature = derivedFeature;
>> source.eAdapters().add(this);
>> }
>>
>> public void addNavigatedDependency(int navigationFeature, int
>> dependantFeature) {
>> this.dependantFeature = dependantFeature;
>> this.navigationFeature = navigationFeature;
>> this.dependantClass =
>> source.eClass().getEStructuralFeature(navigationFeature).get EType().getInstanceClass();
>>
Definitely it looks like working with EStructuralFeature instances would
be safer and simpler than with feature IDs. (Feature IDs are great for
efficient dispatch but they're kind of error prone because of multiple
inheritance; people assume the same feature ID will work in a derive
class, which is a bad assumption.)
>> }
>>
>> public void addLocalDependency(int localFeature) {
>> localFeatures.add(localFeature);
>> }
>>
>> @Override
>> public void notifyChanged(Notification notification) {
>> if (notification.getFeatureID(source.getClass()) ==
>> navigationFeature) {
Again a dangerous use of feature IDs. It really should be
source.eClass().getInstanceClass()..
>> switch (notification.getEventType()) {
>> // TODO support ADD_MANY/REMOVE_MANY?
EContentAdapter is a good place to snarf logic.
>> case Notification.ADD:
>> EObject added = (EObject) notification.getNewValue();
>> added.eAdapters().add(dependantAdapter);
>> break;
>> case Notification.SET:
>> EObject newValue = (EObject) notification.getNewValue();
>> EObject oldValue = (EObject) notification.getOldValue();
>> if (oldValue != null)
>> oldValue.eAdapters().remove(dependantAdapter);
>> if (newValue != null)
>> newValue.eAdapters().add(dependantAdapter);
>> break;
>> case Notification.REMOVE:
>> EObject removed = (EObject) notification.getOldValue();
>> removed.eAdapters().remove(dependantAdapter);
>> break;
>> default:
>> return; // No notification
>> }
>> notifyDerivedAttributeChange();
>> } else if
>> (localFeatures.contains(notification.getFeatureID(source.get Class()))) {
>> if (notification.getEventType() == Notification.SET) {
>> notifyDerivedAttributeChange();
>> }
>> }
>> }
>>
>> private void notifyDerivedAttributeChange() {
>> if (source.eNotificationRequired()) {
>> source.eNotify(new ENotificationImpl(source,
>> Notification.SET, derivedFeature, null, source.eGet(
>> derivedFeature, true, true)));
A callback to the LibraryImpl that computes the value might be good.
Some issues that jump to mind are what happens while the model is being
read it. We'd be recomputing the total page count a great many
times... Once each time a book is added as it is deserialized, and then
again each time the number of pages is set. And of course no one is
listening or caring about the value at this point... I could imagine
not hooking up the logic (adapter) until the first time the derived
attribute is computed...

I think this is a great start for a new recipe! Thanks!!
>> }
>> }
>>
>> }
>>
>>

--------------040707080001020101050909
Content-Type: text/html; charset=ISO-8859-15
Content-Transfer-Encoding: 8bit

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html;charset=ISO-8859-15"
http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
Guys,<br>
<br>
I keep wondering about the most general way to do this...<br>
<br>
One interesting approach that Boris Bokowski explained to me is if you
have something like read "notification" (as in <a
href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=247130">https://bugs.eclipse.org/bugs/show_bug.cgi?id=247130</a>)
you could automatically add listeners to all the objects that are
accessed while the derived attribute is being computed.
Re: EMF Derived Attribute Notifier [message #556697 is a reply to message #383875] Thu, 02 September 2010 11:24 Go to previous messageGo to next message
Santhosh Injineri is currently offline Santhosh Injineri
Messages: 4
Registered: August 2010
Junior Member
Hello Sir,

Where should I add these 2 classes??? I am not able to follow as I am very new to EMF/Ecore. My project has requirement of Derived Attribute...Please share me any link which tells what are the classes generated using Generate Model/Edit/Editor code??

Where should I create LibraryImpl and DerivedAttributeAdapter should be created???

Thanks in Advance,
Santhosh Injineri
Re: EMF Derived Attribute Notifier [message #556764 is a reply to message #556697] Thu, 02 September 2010 14:37 Go to previous message
Ed Merks is currently offline Ed Merks
Messages: 26070
Registered: July 2009
Senior Member
Santhosh,

It sounds like you might be trying to put something in your model that's
only needed in the UI that visualizes your model. In your EMF newsgroup
question it wasn't clear you were modeling all the necessary non-derived
relationships.


Santhosh Injineri wrote:
> Hello Sir,
>
> Where should I add these 2 classes??? I am not able to follow as I am
> very new to EMF/Ecore. My project has requirement of Derived
> Attribute...Please share me any link which tells what are the classes
> generated using Generate Model/Edit/Editor code??
>
> Where should I create LibraryImpl and DerivedAttributeAdapter should
> be created???
>
> Thanks in Advance,
> Santhosh Injineri
Re: EMF Derived Attribute Notifier [message #613819 is a reply to message #383873] Wed, 07 January 2009 14:22 Go to previous message
Eclipse User
Originally posted by: cdamus.zeligsoft.com

Hi, pvm,

Thanks for the contribution! However, you will get more of your target
audience by posting to the EMF newsgroup, which I have included in my reply.

Cheers,

Christian

pvm wrote:
> If you have a derived attribute in EMF and change one of the attributes
> that the derived value depends on, then no notification will be sent for
> the derived attribute - so it will not change in any editor etc. You
> have to write these yourself.
>
> Here's a class I wrote that I've found useful and perhaps it may be
> useful to others (all comments etc. gratefully received). You can
> specify your derived attribute, and also which other attributes it
> depends on. Listeners are then added as appropriate and when any of your
> dependant attributes change, you'll fire an event for your derived
> attribute.
>
> E.g. You have a Class Library and a set of Books in it. Each Book has a
> pageCount attribute. The Library has a derived attribute totalPageCount
> that adds up all the pages of all the books. Then you can just write...
>
> /**
> * @generated NOT
> */
> protected LibraryImpl() {
> super();
> DerivedAttributeAdapter daa = new DerivedAttributeAdapter(this,
> MyPackage.LIBRARY__TOTALPAGECOUNT);
> daa.addNavigatedDependency(MyPackage.LIBRARY__BOOKS,
> MyPackage.BOOK__PAGECOUNT);
> }
>
> A local dependency is another attribute in the same class. A navigated
> dependency is to an attribute in a class that you have an association
> with. You can add many local dependencies, but only one remote one
> (though it can be a one to many association).
>
> Here it is...
>
> package put.it.where.you.like;
>
> import java.util.ArrayList;
> import java.util.List;
>
> import org.eclipse.emf.common.notify.Notification;
> import org.eclipse.emf.common.notify.impl.AdapterImpl;
> import org.eclipse.emf.ecore.EObject;
> import org.eclipse.emf.ecore.impl.ENotificationImpl;
> import org.eclipse.emf.ecore.impl.EObjectImpl;
>
> public class DerivedAttributeAdapter extends AdapterImpl {
> private final EObjectImpl source;
> private final int derivedFeature;
>
> private List<Integer> localFeatures = new ArrayList<Integer>();
>
> // TODO this lot could be put into a subclass and put in a list to
> allow for
> // multiple navigated dependencies
> private int dependantFeature = Notification.NO_FEATURE_ID;
> private Class<?> dependantClass = null;
> private int navigationFeature = Notification.NO_FEATURE_ID;
>
> private AdapterImpl dependantAdapter = new AdapterImpl() {
>
> @Override
> public void notifyChanged(Notification msg) {
>
> if (msg.getFeatureID(dependantClass) == dependantFeature &&
> msg.getEventType() == Notification.SET) {
> notifyDerivedAttributeChange();
> }
> }
> };
>
> /*
> * Convenience constructor for a local and navigated dependency
> */
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature, int navigationFeature, int dependantFeature, int
> localFeature) {
> this(source, derivedFeature);
> addNavigatedDependency(navigationFeature, dependantFeature);
> addLocalDependency(localFeature);
> }
>
> /*
> * Convenience constructor for a navigated dependency
> */
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature, int navigationFeature, int dependantFeature) {
> this(source, derivedFeature);
> addNavigatedDependency(navigationFeature, dependantFeature);
> }
>
> /*
> * Convenience constructor for a local dependency
> */
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature, int localFeature) {
> this(source, derivedFeature);
> addLocalDependency(localFeature);
> }
>
> public DerivedAttributeAdapter(EObjectImpl source, int
> derivedFeature) {
> super();
> this.source = source;
> this.derivedFeature = derivedFeature;
> source.eAdapters().add(this);
> }
>
> public void addNavigatedDependency(int navigationFeature, int
> dependantFeature) {
> this.dependantFeature = dependantFeature;
> this.navigationFeature = navigationFeature;
> this.dependantClass =
> source.eClass().getEStructuralFeature(navigationFeature).get EType().getInstanceClass();
>
> }
>
> public void addLocalDependency(int localFeature) {
> localFeatures.add(localFeature);
> }
>
> @Override
> public void notifyChanged(Notification notification) {
> if (notification.getFeatureID(source.getClass()) ==
> navigationFeature) {
> switch (notification.getEventType()) {
> // TODO support ADD_MANY/REMOVE_MANY?
> case Notification.ADD:
> EObject added = (EObject) notification.getNewValue();
> added.eAdapters().add(dependantAdapter);
> break;
> case Notification.SET:
> EObject newValue = (EObject) notification.getNewValue();
> EObject oldValue = (EObject) notification.getOldValue();
> if (oldValue != null)
> oldValue.eAdapters().remove(dependantAdapter);
> if (newValue != null)
> newValue.eAdapters().add(dependantAdapter);
> break;
> case Notification.REMOVE:
> EObject removed = (EObject) notification.getOldValue();
> removed.eAdapters().remove(dependantAdapter);
> break;
> default:
> return; // No notification
> }
> notifyDerivedAttributeChange();
> } else if
> (localFeatures.contains(notification.getFeatureID(source.get Class()))) {
> if (notification.getEventType() == Notification.SET) {
> notifyDerivedAttributeChange();
> }
> }
> }
>
> private void notifyDerivedAttributeChange() {
> if (source.eNotificationRequired()) {
> source.eNotify(new ENotificationImpl(source,
> Notification.SET, derivedFeature, null, source.eGet(
> derivedFeature, true, true)));
> }
> }
>
> }
>
>
Re: EMF Derived Attribute Notifier [message #613821 is a reply to message #383874] Wed, 07 January 2009 15:21 Go to previous message
Ed Merks is currently offline Ed Merks
Messages: 26070
Registered: July 2009
Senior Member
This is a multi-part message in MIME format.
--------------040707080001020101050909
Content-Type: text/plain; charset=ISO-8859-15; format=flowed
Content-Transfer-Encoding: 7bit

Guys,

I keep wondering about the most general way to do this...

One interesting approach that Boris Bokowski explained to me is if you
have something like read "notification" (as in
https://bugs.eclipse.org/bugs/show_bug.cgi?id=247130) you could
automatically add listeners to all the objects that are accessed while
the derived attribute is being computed. Of course this requires so
global setup---an access listener attached to all objects in the
resource set as well as easy hookup into that global listener---but it
would work for any derivation pattern...

There's also the work underway in
https://bugs.eclipse.org/bugs/show_bug.cgi?id=216701 for specifying a
derived feature's behavior declaratively. This would seem potentially a
good place to try to fit in the design below.

I'll comment specifically on the text below.


Christian W. Damus wrote:
> Hi, pvm,
>
> Thanks for the contribution!
Yes, they're always a good thing. :-)
> However, you will get more of your target audience by posting to the
> EMF newsgroup, which I have included in my reply.
>
> Cheers,
>
> Christian
>
> pvm wrote:
>> If you have a derived attribute in EMF and change one of the
>> attributes that the derived value depends on, then no notification
>> will be sent for the derived attribute - so it will not change in any
>> editor etc. You have to write these yourself.
>>
>> Here's a class I wrote that I've found useful and perhaps it may be
>> useful to others (all comments etc. gratefully received). You can
>> specify your derived attribute, and also which other attributes it
>> depends on. Listeners are then added as appropriate and when any of
>> your dependant attributes change, you'll fire an event for your
>> derived attribute.
A great way to contribute is to write an EMF Recipe for others to follow
and post to the newsgroup to announce you've done that so people will
tend to find it and provide feedback quickly:

http://wiki.eclipse.org/EMF/Recipes

>>
>> E.g. You have a Class Library and a set of Books in it. Each Book has
>> a pageCount attribute. The Library has a derived attribute
>> totalPageCount that adds up all the pages of all the books. Then you
>> can just write...
>>
>> /**
>> * @generated NOT
>> */
>> protected LibraryImpl() {
>> super();
>> DerivedAttributeAdapter daa = new DerivedAttributeAdapter(this,
>> MyPackage.LIBRARY__TOTALPAGECOUNT);
>> daa.addNavigatedDependency(MyPackage.LIBRARY__BOOKS,
>> MyPackage.BOOK__PAGECOUNT);
>> }
Note that an interesting alternative to create an adapter and adding it
to the list is to make your objects "self adapting." I.e., you can
specialize eNotificationRequired to always return true so that when
there are changes eNotify will always be called, even if there are no
adapters listening. Then you can specialize eNotify (remembering to
call super!) just like you specialize the notifyChanged method of the
adapter. It saves quite a bit of space. I see your implementation is
nicely encapsulated and reusable as an adapter though...
>>
>> A local dependency is another attribute in the same class. A
>> navigated dependency is to an attribute in a class that you have an
>> association with. You can add many local dependencies, but only one
>> remote one (though it can be a one to many association).
>>
>> Here it is...
>>
>> package put.it.where.you.like;
>>
>> import java.util.ArrayList;
>> import java.util.List;
>>
>> import org.eclipse.emf.common.notify.Notification;
>> import org.eclipse.emf.common.notify.impl.AdapterImpl;
>> import org.eclipse.emf.ecore.EObject;
>> import org.eclipse.emf.ecore.impl.ENotificationImpl;
>> import org.eclipse.emf.ecore.impl.EObjectImpl;
>>
>> public class DerivedAttributeAdapter extends AdapterImpl {
>> private final EObjectImpl source;
You should generally refer to EObjectImpl only in the extends of a
derived class and no where else. Use EObject or InternalEObject for all
other purposes. After all, other implementation classes can directly
extend BasicEObjectImpl, MinimalEObjectImpl from
https://bugs.eclipse.org/bugs/show_bug.cgi?id=252501 is such an example.
>> private final int derivedFeature;
>>
>> private List<Integer> localFeatures = new ArrayList<Integer>();
>>
>> // TODO this lot could be put into a subclass and put in a list
>> to allow for
>> // multiple navigated dependencies
>> private int dependantFeature = Notification.NO_FEATURE_ID;
>> private Class<?> dependantClass = null;
>> private int navigationFeature = Notification.NO_FEATURE_ID;
>>
>> private AdapterImpl dependantAdapter = new AdapterImpl() {
>>
>> @Override
>> public void notifyChanged(Notification msg) {
>>
>> if (msg.getFeatureID(dependantClass) == dependantFeature
It might be better to use getFeature and then you only need to record an
EStructuralFeature object to compare it to (which you can get via
XyzPackage.Literals.<class-name>__<feature-name>)
>> && msg.getEventType() == Notification.SET) {
The second test is cheaper than the first test, so is likely to be more
efficient to have it first.
>> notifyDerivedAttributeChange();
>> }
>> }
>> };
>>
>> /*
>> * Convenience constructor for a local and navigated dependency
>> */
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature, int navigationFeature, int dependantFeature, int
>> localFeature) {
>> this(source, derivedFeature);
>> addNavigatedDependency(navigationFeature, dependantFeature);
>> addLocalDependency(localFeature);
>> }
>>
>> /*
>> * Convenience constructor for a navigated dependency
>> */
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature, int navigationFeature, int dependantFeature) {
>> this(source, derivedFeature);
>> addNavigatedDependency(navigationFeature, dependantFeature);
>> }
>>
>> /*
>> * Convenience constructor for a local dependency
>> */
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature, int localFeature) {
>> this(source, derivedFeature);
>> addLocalDependency(localFeature);
>> }
>>
>> public DerivedAttributeAdapter(EObjectImpl source, int
>> derivedFeature) {
>> super();
>> this.source = source;
>> this.derivedFeature = derivedFeature;
>> source.eAdapters().add(this);
>> }
>>
>> public void addNavigatedDependency(int navigationFeature, int
>> dependantFeature) {
>> this.dependantFeature = dependantFeature;
>> this.navigationFeature = navigationFeature;
>> this.dependantClass =
>> source.eClass().getEStructuralFeature(navigationFeature).get EType().getInstanceClass();
>>
Definitely it looks like working with EStructuralFeature instances would
be safer and simpler than with feature IDs. (Feature IDs are great for
efficient dispatch but they're kind of error prone because of multiple
inheritance; people assume the same feature ID will work in a derive
class, which is a bad assumption.)
>> }
>>
>> public void addLocalDependency(int localFeature) {
>> localFeatures.add(localFeature);
>> }
>>
>> @Override
>> public void notifyChanged(Notification notification) {
>> if (notification.getFeatureID(source.getClass()) ==
>> navigationFeature) {
Again a dangerous use of feature IDs. It really should be
source.eClass().getInstanceClass()..
>> switch (notification.getEventType()) {
>> // TODO support ADD_MANY/REMOVE_MANY?
EContentAdapter is a good place to snarf logic.
>> case Notification.ADD:
>> EObject added = (EObject) notification.getNewValue();
>> added.eAdapters().add(dependantAdapter);
>> break;
>> case Notification.SET:
>> EObject newValue = (EObject) notification.getNewValue();
>> EObject oldValue = (EObject) notification.getOldValue();
>> if (oldValue != null)
>> oldValue.eAdapters().remove(dependantAdapter);
>> if (newValue != null)
>> newValue.eAdapters().add(dependantAdapter);
>> break;
>> case Notification.REMOVE:
>> EObject removed = (EObject) notification.getOldValue();
>> removed.eAdapters().remove(dependantAdapter);
>> break;
>> default:
>> return; // No notification
>> }
>> notifyDerivedAttributeChange();
>> } else if
>> (localFeatures.contains(notification.getFeatureID(source.get Class()))) {
>> if (notification.getEventType() == Notification.SET) {
>> notifyDerivedAttributeChange();
>> }
>> }
>> }
>>
>> private void notifyDerivedAttributeChange() {
>> if (source.eNotificationRequired()) {
>> source.eNotify(new ENotificationImpl(source,
>> Notification.SET, derivedFeature, null, source.eGet(
>> derivedFeature, true, true)));
A callback to the LibraryImpl that computes the value might be good.
Some issues that jump to mind are what happens while the model is being
read it. We'd be recomputing the total page count a great many
times... Once each time a book is added as it is deserialized, and then
again each time the number of pages is set. And of course no one is
listening or caring about the value at this point... I could imagine
not hooking up the logic (adapter) until the first time the derived
attribute is computed...

I think this is a great start for a new recipe! Thanks!!
>> }
>> }
>>
>> }
>>
>>

--------------040707080001020101050909
Content-Type: text/html; charset=ISO-8859-15
Content-Transfer-Encoding: 8bit

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html;charset=ISO-8859-15"
http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
Guys,<br>
<br>
I keep wondering about the most general way to do this...<br>
<br>
One interesting approach that Boris Bokowski explained to me is if you
have something like read "notification" (as in <a
href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=247130">https://bugs.eclipse.org/bugs/show_bug.cgi?id=247130</a>)
you could automatically add listeners to all the objects that are
accessed while the derived attribute is being computed.
Re: EMF Derived Attribute Notifier [message #621795 is a reply to message #383875] Thu, 02 September 2010 11:24 Go to previous message
Santhosh Injineri is currently offline Santhosh Injineri
Messages: 4
Registered: August 2010
Junior Member
Hello Sir,

Where should I add these 2 classes??? I am not able to follow as I am very new to EMF/Ecore. My project has requirement of Derived Attribute...Please share me any link which tells what are the classes generated using Generate Model/Edit/Editor code??

Where should I create LibraryImpl and DerivedAttributeAdapter should be created???

Thanks in Advance,
Santhosh Injineri
Re: EMF Derived Attribute Notifier [message #621797 is a reply to message #621795] Thu, 02 September 2010 14:37 Go to previous message
Ed Merks is currently offline Ed Merks
Messages: 26070
Registered: July 2009
Senior Member
Santhosh,

It sounds like you might be trying to put something in your model that's
only needed in the UI that visualizes your model. In your EMF newsgroup
question it wasn't clear you were modeling all the necessary non-derived
relationships.


Santhosh Injineri wrote:
> Hello Sir,
>
> Where should I add these 2 classes??? I am not able to follow as I am
> very new to EMF/Ecore. My project has requirement of Derived
> Attribute...Please share me any link which tells what are the classes
> generated using Generate Model/Edit/Editor code??
>
> Where should I create LibraryImpl and DerivedAttributeAdapter should
> be created???
>
> Thanks in Advance,
> Santhosh Injineri
Previous Topic:Creating feature-based EMF product
Next Topic:Generating and modifying UML class diagram programmatically
Goto Forum:
  


Current Time: Wed Sep 24 04:49:51 GMT 2014

Powered by FUDForum. Page generated in 0.12415 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software