Home » Modeling » EMF » getParent() on children split across multiple transient tree nodes
getParent() on children split across multiple transient tree nodes [message #821935] |
Fri, 16 March 2012 01:02 |
Olivier Labrosse Messages: 49 Registered: November 2011 |
Member |
|
|
Hi,
Our EEF-based model editor has an EMF-specific issue: a custom item provider's getParent(Object) method cannot access its tree parent due to its adapterFactory being a different instance than that of the tree. I explain...
First, the code:
public Object getParent(Object object) {
Object parent = super.getParent(object);
if (parent == null || parent instanceof Foo) {
return parent;
}
CustomBarItemProvider provider = (CustomBarItemProvider)adapterFactory.adapt(parent,
IEditingDomainItemProvider.class);
return provider == null ? null : provider.getTransientFooCategoryItemProvider(((Foo)object)
.getFooCategory());
}
Foo is contained in Bar, but can also itself contain sub-Foos, hence the check for the parent being an instance of Foo. The CustomBarItemProvider is stateful and holds references to three TransientFooCategoryItemProvider instances which are returned by getChildren(Object). There's one transient per category of Foo, and each transient's getChildren(Object) method returns the respective Bar's Foos of the respective category.
Now the problem is, when I do a drag & drop operation on a Foo, somehow its item provider's adapterFactory is different than its Bar container's item provider's, so what I get is a new CustomBarItemProvider instance which doesn't hold any transient item providers yet, let alone Foo's parents.
I tried finding out why the adapterFactory is a different instance but it appears to be like looking for a needle in a haystack.
Can anyone help me figure out a solution to this problem?
Thanks in advance!
-Olivier
|
|
|
Re: getParent() on children split across multiple transient tree nodes [message #822097 is a reply to message #821935] |
Fri, 16 March 2012 07:00 |
Ed Merks Messages: 33139 Registered: July 2009 |
Senior Member |
|
|
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Olivier,<br>
<br>
Comments below.<br>
<br>
On 16/03/2012 2:02 AM, Olivier Labrosse wrote:
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">Hi,
<br>
<br>
Our EEF-based model editor has an EMF-specific issue: a custom
item provider's getParent(Object) method cannot access its tree
parent due to its adapterFactory being a different instance than
that of the tree. I explain...
<br>
<br>
First, the code:
<br>
<br>
public Object getParent(Object object) {
<br>
Object parent = super.getParent(object);
<br>
if (parent == null || parent instanceof Foo) {
<br>
return parent;
<br>
}
<br>
</blockquote>
This is in the customized item provider for a Foo. You're trying to
avoid it returning a Bar as the parent..<br>
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">
<br>
CustomBarItemProvider provider =
(CustomBarItemProvider)adapterFactory.adapt(parent,
<br>
IEditingDomainItemProvider.class);
<br>
return provider == null ? null :
provider.getTransientFooCategoryItemProvider(((Foo)object)
<br>
.getFooCategory());
<br>
}
<br>
</blockquote>
So here you're trying to get the statefull item provider for Bar...<br>
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">
<br>
Foo is contained in Bar, but can also itself contain sub-Foos,
hence the check for the parent being an instance of Foo. The
CustomBarItemProvider is stateful and holds references to three
TransientFooCategoryItemProvider instances which are returned by
getChildren(Object). There's one transient per category of Foo,
and each transient's getChildren(Object) method returns the
respective Bar's Foos of the respective category.
<br>
</blockquote>
I see. And these correctly return bar as the parent...<br>
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">
<br>
Now the problem is, when I do a drag & drop operation on a
Foo, somehow its item provider's adapterFactory is different than
its Bar container's item provider's, so what I get is a new
CustomBarItemProvider instance which doesn't hold any transient
item providers yet, let alone Foo's parents.
<br>
</blockquote>
You could set breakpoints to see why there is more than one factory
instance...<br>
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">
<br>
I tried finding out why the adapterFactory is a different instance
but it appears to be like looking for a needle in a haystack.
<br>
</blockquote>
Generally when you customize an item provider specifically for a
view, you create a customized item provider adapter factory and use
it to create them. I'm not sure if you're doing that...<br>
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">
<br>
Can anyone help me figure out a solution to this problem?
<br>
</blockquote>
Looking closely I think the issue is very subtle and has to do with
composition of factories. Have a look at the generated
XyzItemProviderAdapterFactories two specialized adapt methods.<br>
<br>
@Override<br>
public Adapter adapt(Notifier notifier, Object type)<br>
{<br>
return super.adapt(notifier, this);<br>
}<br>
<br>
@Override<br>
public Object adapt(Object object, Object type)<br>
{<br>
if (isFactoryForType(type))<br>
{<br>
Object adapter = super.adapt(object, type);<br>
if (!(type instanceof Class<?>) ||
(((Class<?>)type).isInstance(adapter)))<br>
{<br>
return adapter;<br>
}<br>
}<br>
<br>
return null;<br>
}<br>
<br>
You see the first one replaces the type object with the factory
itself as the key but your call is directly calling the second one:<br>
<br>
CustomBarItemProvider provider =
(CustomBarItemProvider)adapterFactory.adapt(parent,
IEditingDomainItemProvider.class);
<br>
<br>
This approach will call the first form and will return the
already-associated adapter or create one if necessary.<br>
<blockquote>CustomBarItemProvider provider =
(CustomBarItemProvider)adapterFactory.adapt((Notifier)parent,
IEditingDomainItemProvider.class);<br>
</blockquote>
In general, when you're dealing with models from many packages, it's
important you go back to the root adapter factory and consult with
it.<br>
<blockquote>(((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory()<br>
<br>
</blockquote>
So I expect this approach would also avoid the subtle misdelegation.<br>
<blockquote>CustomBarItemProvider provider =
(CustomBarItemProvider)(((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory().adapt(parent,
IEditingDomainItemProvider.class);<br>
</blockquote>
<br>
<blockquote cite="mid:jju3es$6mq$1@news.eclipse.org" type="cite">
<br>
Thanks in advance!
<br>
<br>
-Olivier
<br>
</blockquote>
</body>
</html>
Ed Merks
Professional Support: https://www.macromodeling.com/
|
|
|
Re: getParent() on children split across multiple transient tree nodes [message #822473 is a reply to message #822097] |
Fri, 16 March 2012 17:19 |
Olivier Labrosse Messages: 49 Registered: November 2011 |
Member |
|
|
Thank you Ed for the quick response.
Please see my comments below.
Ed Merks wrote on Fri, 16 March 2012 03:00Olivier,
Comments below.
On 16/03/2012 2:02 AM, Olivier Labrosse wrote:Hi,
Our EEF-based model editor has an EMF-specific issue: a custom item provider's getParent(Object) method cannot access its tree parent due to its adapterFactory being a different instance than that of the tree. I explain...
First, the code:
public Object getParent(Object object) {
Object parent = super.getParent(object);
if (parent == null || parent instanceof Foo) {
return parent;
}
This is in the customized item provider for a Foo. You're trying to avoid it returning a Bar as the parent..
Quote:
CustomBarItemProvider provider = (CustomBarItemProvider)adapterFactory.adapt(parent, IEditingDomainItemProvider.class);
return provider == null ? null : provider.getTransientFooCategoryItemProvider(((Foo)object).getFooCategory());
}
So here you're trying to get the statefull item provider for Bar...
Quote:
Foo is contained in Bar, but can also itself contain sub-Foos, hence the check for the parent being an instance of Foo. The CustomBarItemProvider is stateful and holds references to three TransientFooCategoryItemProvider instances which are returned by getChildren(Object). There's one transient per category of Foo, and each transient's getChildren(Object) method returns the respective Bar's Foos of the respective category.
I see. And these correctly return bar as the parent...
Quote:
Now the problem is, when I do a drag & drop operation on a Foo, somehow its item provider's adapterFactory is different than its Bar container's item provider's, so what I get is a new CustomBarItemProvider instance which doesn't hold any transient item providers yet, let alone Foo's parents.
You could set breakpoints to see why there is more than one factory instance...
I had done that but hadn't found anything meaningful.
I just had another go at debugging this, and I noticed our custom TreeViewer didn't override getAdapterFactory(), making the default ComposedAdapterFactory create new instances of our custom adapter factory for each of the adpter types (e.g. ITableItemLabelProvider), using the descriptor registry (ComposedAdapterFactory.Descriptor.Registry.INSTANCE).
Am I wrong or should a single instance of an adapter factory be used no matter how many types are supported?
For the time being, I overrode my TreeViewer's getAdapterFactory() method to return our custom editing domain's adapter factory where an instance of our custom adapter factory is found. Now the TreeViewer's EObjects are all adapted using the same adapter factory instance, resolving this particular problem.
Please tell me if you know this to be wrong, as I'm not yet fully aware of all the implications of such an implementation.
Quote:
Quote:
I tried finding out why the adapterFactory is a different instance but it appears to be like looking for a needle in a haystack.
Generally when you customize an item provider specifically for a view, you create a customized item provider adapter factory and use it to create them. I'm not sure if you're doing that...
Yes, I was already doing that. Good call.
Quote:
Quote:
Can anyone help me figure out a solution to this problem?
Looking closely I think the issue is very subtle and has to do with composition of factories. Have a look at the generated XyzItemProviderAdapterFactories two specialized adapt methods.
@Override
public Adapter adapt(Notifier notifier, Object type)
{
return super.adapt(notifier, this);
}
@Override
public Object adapt(Object object, Object type)
{
if (isFactoryForType(type))
{
Object adapter = super.adapt(object, type);
if (!(type instanceof Class<?>) || (((Class<?>)type).isInstance(adapter)))
{
return adapter;
}
}
return null;
}
You see the first one replaces the type object with the factory itself as the key but your call is directly calling the second one:
CustomBarItemProvider provider = (CustomBarItemProvider)adapterFactory.adapt(parent, IEditingDomainItemProvider.class);
This approach will call the first form and will return the already-associated adapter or create one if necessary.
CustomBarItemProvider provider = (CustomBarItemProvider)adapterFactory.adapt((Notifier)parent, IEditingDomainItemProvider.class);
I'm afraid casting parent as a Notifier doesn't really make a difference, short of shaving a few cycles. ItemProviderAdapter.isAdapterForType(Object) eventually gets called and the adapter factory instances get compared the same way as when you don't cast parent to a Notifier. Unless I'm missing something?
Quote:
In general, when you're dealing with models from many packages, it's important you go back to the root adapter factory and consult with it.
(((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory()
So I expect this approach would also avoid the subtle misdelegation.
CustomBarItemProvider provider = (CustomBarItemProvider)(((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory().adapt(parent, IEditingDomainItemProvider.class);
Thanks for the tip!
And thanks again for your timely support.
|
|
|
Re: getParent() on children split across multiple transient tree nodes [message #822756 is a reply to message #822473] |
Sat, 17 March 2012 07:35 |
Ed Merks Messages: 33139 Registered: July 2009 |
Senior Member |
|
|
Olivier,
Comments below.
On 16/03/2012 6:19 PM, Olivier Labrosse wrote:
> Thank you Ed for the quick response.
>
> Please see my comments below.
>
> Ed Merks wrote on Fri, 16 March 2012 03:00
>> Olivier,
>> Comments below.
>> On 16/03/2012 2:02 AM, Olivier Labrosse wrote:
>> > Hi,
>> > > Our EEF-based model editor has an EMF-specific issue: a
>> custom item provider's getParent(Object) method cannot access its
>> tree parent due to its adapterFactory being a different instance than
>> that of the tree. I explain...
>> > > First, the code:
>> > > > public Object getParent(Object object) {
>> > Object parent = super.getParent(object);
>> > if (parent == null || parent instanceof Foo) {
>> > return parent;
>> > }
>>
>> This is in the customized item provider for a Foo. You're trying
>> to avoid it returning a Bar as the parent..
>>
>> Quote:
>> > CustomBarItemProvider provider =
>> (CustomBarItemProvider)adapterFactory.adapt(parent,
>> IEditingDomainItemProvider.class);
>> > return provider == null ? null :
>> provider.getTransientFooCategoryItemProvider(((Foo)object).getFooCategory());
>> > }
>>
>> So here you're trying to get the statefull item provider for Bar...
>>
>> Quote:
>> > Foo is contained in Bar, but can also itself contain sub-Foos,
>> hence the check for the parent being an instance of Foo. The
>> CustomBarItemProvider is stateful and holds references to three
>> TransientFooCategoryItemProvider instances which are returned by
>> getChildren(Object). There's one transient per category of Foo, and
>> each transient's getChildren(Object) method returns the respective
>> Bar's Foos of the respective category.
>>
>> I see. And these correctly return bar as the parent...
>>
>> Quote:
>> > Now the problem is, when I do a drag & drop operation on a Foo,
>> somehow its item provider's adapterFactory is different than its Bar
>> container's item provider's, so what I get is a new
>> CustomBarItemProvider instance which doesn't hold any transient item
>> providers yet, let alone Foo's parents.
>>
>> You could set breakpoints to see why there is more than one
>> factory instance...
>
> I had done that but hadn't found anything meaningful.
>
> I just had another go at debugging this, and I noticed our custom
> TreeViewer didn't override getAdapterFactory(), making the default
> ComposedAdapterFactory create new instances of our custom adapter
> factory for each of the adpter types (e.g. ITableItemLabelProvider),
> using the descriptor registry
> (ComposedAdapterFactory.Descriptor.Registry.INSTANCE).
ComposedAdapterFactory.getFactoryForTypes should find an existing
factory if that factory returns true for isFactoryForType(). Tracking
down why that's not matching an already created factory is important.
>
> Am I wrong or should a single instance of an adapter factory be used
> no matter how many types are supported?
I would expect it to reuse an existing instance, unless it does, you're
not going to find the right item provider instance by consulting the
composed adapter factory.
>
> For the time being, I overrode my TreeViewer's getAdapterFactory()
> method to return our custom editing domain's adapter factory where an
> instance of our custom adapter factory is found.
TreeViewer doesn't have such a method so I'm not sure what you mean.
> Now the TreeViewer's EObjects are all adapted using the same adapter
> factory instance, resolving this particular problem.
>
> Please tell me if you know this to be wrong, as I'm not yet fully
> aware of all the implications of such an implementation.
What types are added in your generated item provider adapter factory's
supportedTypes field (which is initialized in the constructor).
>
> Quote:
>> Quote:
>> > I tried finding out why the adapterFactory is a different instance
>> but it appears to be like looking for a needle in a haystack.
>>
>> Generally when you customize an item provider specifically for a
>> view, you create a customized item provider adapter factory and use
>> it to create them. I'm not sure if you're doing that...
>
> Yes, I was already doing that. Good call.
>
> Quote:
>> Quote:
>> > Can anyone help me figure out a solution to this problem?
>>
>> Looking closely I think the issue is very subtle and has to do
>> with composition of factories. Have a look at the generated
>> XyzItemProviderAdapterFactories two specialized adapt methods.
>> @Override
>> public Adapter adapt(Notifier notifier, Object type)
>> {
>> return super.adapt(notifier, this);
>> }
>>
>> @Override
>> public Object adapt(Object object, Object type)
>> {
>> if (isFactoryForType(type))
>> {
>> Object adapter = super.adapt(object, type);
>> if (!(type instanceof Class<?>) ||
>> (((Class<?>)type).isInstance(adapter)))
>> {
>> return adapter;
>> }
>> }
>>
>> return null;
>> }
>> You see the first one replaces the type object with the
>> factory itself as the key but your call is directly calling the
>> second one:
>> CustomBarItemProvider provider =
>> (CustomBarItemProvider)adapterFactory.adapt(parent,
>> IEditingDomainItemProvider.class);
>> This approach will call the first form and will return the
>> already-associated adapter or create one if necessary.
>> CustomBarItemProvider provider =
>> (CustomBarItemProvider)adapterFactory.adapt((Notifier)parent,
>> IEditingDomainItemProvider.class);
>
> I'm afraid casting parent as a Notifier doesn't really make a
> difference, short of shaving a few cycles.
> ItemProviderAdapter.isAdapterForType(Object) eventually gets called
> and the adapter factory instances get compared the same way as when
> you don't cast parent to a Notifier. Unless I'm missing something?
Are you seeing that this method in your generated item provider adapter
factory is being called?
public Adapter adapt(Notifier notifier, Object type)
{
return super.adapt(notifier, this);
}
and that it calls this method:
public Adapter adapt(Notifier target, Object type)
{
for (Adapter adapter : target.eAdapters())
{
if (adapter.isAdapterForType(type))
{
return adapter;
}
}
return adaptNew(target, type);
}
(with type being the adapter factory) which will return an existing
adapter created for that adapter factory (which you've asserted
definitely exists).
That's certainly what I'd expect.
>
> Quote:
>> In general, when you're dealing with models from many packages, it's
>> important you go back to the root adapter factory and consult with it.
>> (((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory()
>> So I expect this approach would also avoid the subtle
>> misdelegation.
>> CustomBarItemProvider provider =
>> (CustomBarItemProvider)(((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory().adapt(parent,
>> IEditingDomainItemProvider.class);
Did you try this? Is this returning a newly created adapter and not
finding the existing one? Certainly every generated adapter factory
includes
supportedTypes.add(IEditingDomainItemProvider.class);
So it's hard to imagine how it's possible for the existing adapter
factory not to be found.
>
> Thanks for the tip!
>
> And thanks again for your timely support.
I suspect there's some important little detail that you've not described
that lies at the root of the problems.
Ed Merks
Professional Support: https://www.macromodeling.com/
|
|
| | |
Goto Forum:
Current Time: Sat Apr 20 10:58:28 GMT 2024
Powered by FUDForum. Page generated in 0.03217 seconds
|