Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
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 Go to next message
Olivier Labrosse is currently offline Olivier LabrosseFriend
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 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
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 &amp; 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&lt;?&gt;) ||
(((Class&lt;?&gt;)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 Go to previous messageGo to next message
Olivier Labrosse is currently offline Olivier LabrosseFriend
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: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 &amp; 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 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
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/
Re: getParent() on children split across multiple transient tree nodes [message #824358 is a reply to message #822756] Mon, 19 March 2012 15:19 Go to previous messageGo to next message
Olivier Labrosse is currently offline Olivier LabrosseFriend
Messages: 49
Registered: November 2011
Member
Thank you Ed for your support and especially your patience.

I had tried your suggestion to cast to ComposeableAdapterFactory and getting the root adapter factory, but I failed to tell you it worked. I think it might be because despite this fix, I still need to override the EEFTreeMasterPart's getAdapterFactory() method (not TreeViewer's as I mistakenly typed before) to make drag & drop work properly. At this point I'm not exactly sure why I need it, but it makes sense to me to use the editing domain's adapter factory when the editing domain is an AdapterFactoryEditingDomain. I'll go ahead and suggest this modification to the EEF team.

Thank you again for your invaluable help.

-Olivier
Re: getParent() on children split across multiple transient tree nodes [message #824401 is a reply to message #824358] Mon, 19 March 2012 16:20 Go to previous message
Olivier Labrosse is currently offline Olivier LabrosseFriend
Messages: 49
Registered: November 2011
Member
Actually, just to be clear, I've changed my suggestion to the EEF team. I realized it doesn't make sense to override getAdapterFactory(). Instead, I suggested calling setAdapterFactory() as soon as the editing domain is set and is an AdapterFactoryEditingDomain, in createModelRoot().
Previous Topic:Update resource
Next Topic:How to handle EObject fields which are omitted by EcoreUtil.copy?
Goto Forum:
  


Current Time: Sat Apr 20 10:58:28 GMT 2024

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

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

Back to the top