Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 289859 - Recursive bounds on abstract associations
Summary: Recursive bounds on abstract associations
Status: CLOSED DUPLICATE of bug 382774
Alias: None
Product: EMF
Classification: Modeling
Component: Core (show other bugs)
Version: 2.5.0   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Ed Merks CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-09-18 10:28 EDT by Xavier Maysonnave CLA
Modified: 2013-01-06 09:39 EST (History)
3 users (show)

See Also:


Attachments
Test case, several generated models with compilation erros. (310.28 KB, application/x-zip-compressed)
2009-09-18 10:28 EDT, Xavier Maysonnave CLA
no flags Details
new test case (284.63 KB, application/zip)
2009-09-18 15:08 EDT, Xavier Maysonnave CLA
no flags Details
Test Case - EMF Project (182.19 KB, model/zip)
2010-03-30 12:22 EDT, Greg Mising name CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Xavier Maysonnave CLA 2009-09-18 10:28:06 EDT
Created attachment 147564 [details]
Test case, several generated models with compilation erros.

Recursive bounds on an abstract associations cause compilations errors in model
and edit.
You will find here attached a test case to reproduce such errors.
Comment 1 Ed Merks CLA 2009-09-18 11:42:14 EDT
I'm having trouble wrapping my mind around the error message. I.e., how does one correctly call this create method? Or in other words, what should we be generating?  I suppose it's even possible it's a compiler error...
Comment 2 Xavier Maysonnave CLA 2009-09-18 15:08:33 EDT
Created attachment 147593 [details]
new test case

new test case with no compilation error. However the generic contract is not respected while retrieving the edit newChildDescriptors.
Comment 3 Xavier Maysonnave CLA 2009-09-18 15:09:15 EDT
I made a mistake in my modelcore model. A class who should be abstract was a concrete one. Anyway I fixed it and here is a new test case. The second problem I face relies on the extended associations all the possible children are collected.
The generic contract is not respected. here is the code snippet :

     @Override
      public <T extends ExtensibleModelElement<T>> Object caseExtensibleModelElement(ExtensibleModelElement<T> object) {
        newChildDescriptors.add
          (createChildParameter
            (ModelcorePackage.Literals.EXTENSIBLE_MODEL_ELEMENT__EXTENDEDS,
             ViewpointperformanceFactory.eINSTANCE.createPerformanceLC()));

        newChildDescriptors.add
          (createChildParameter
            (ModelcorePackage.Literals.EXTENSIBLE_MODEL_ELEMENT__EXTENDEDS,
             ViewpointperformanceFactory.eINSTANCE.createPerformancePC()));

        return null;
      }

all the collected newChilsDescriptors are retrieved. T should LC or PC but not both, I guess filtering such things would be nice. Anyway model validation should enforce that rule. I will investigate this one tomorrow.

Bye
Comment 4 Ed Merks CLA 2009-09-18 15:20:17 EDT
I imagine this doesn't block anything so I'm reducing the severity.

I'm not sure I understand your comment.  The children are supposed to include all possible children so I don't understand the comment about it should have one or the other but not both...
Comment 5 Xavier Maysonnave CLA 2009-09-23 03:58:38 EDT
(In reply to comment #4)
> I imagine this doesn't block anything so I'm reducing the severity.
> 
> I'm not sure I understand your comment.  The children are supposed to include
> all possible children so I don't understand the comment about it should have
> one or the other but not both...

LC is a concrete class and a subclass of ExtensibleModelElement. As such the 'extendeds' association is typed with LC. When I work on an instance of LC I expected to see only commands related to LC. However all the commands who create ExtendedModelElement are available. I can populate my containment with all of them with no complain. In this use case children are supposed to include children of their own type. If I don't use generics the result is virtually the same. I hope you have better understanding.
Comment 6 Greg Mising name CLA 2010-03-30 12:16:48 EDT
I've come across the same issues over the past couple of days and find it rather frustrating.

I have re-created a simple test case based on the article: "Generics in Eclipse Modelling Framework" - http://eclipse.dzone.com/articles/generics-eclipse-modelling-fra?page=0,0

My model consists of the following:
EClasses:
ModelObject
BasketContainer -> ModelObject
FoodBasket<F extends Food> -> ModelObject
FruitBasket<F extends Fruit> -> FoodBasket<F>
AppleBasket -> FruitBasket
OrangeBasket -> FruitBasket
Food -> ModelObject
Fruit -> Food
Apple -> Fruit
Orange -> Fruit

EReferences:
BasketContainer contains 0..* FoodBasket<?>
FoodBasket<F extends Food> contains 0..* F

Now I'm really happy that the eCore model is able to express the specialization that I want in the FoodBasket to Food reference, however I'm disappointed that the generated code fails in the two areas that Xavier mentioned.

1) Type-safety of the getFood() EList
This list is created with the top-level class Food as the dataClass for its storage array.  You can't really blame them here because Java provides no run-time information on generic types, so how is it to know the actual dataClass of that instance?  There is a solution using Java generics that is described in this article "Reflecting generics": http://www.artima.com/weblogs/viewpost.jsp?thread=208860.  My test case includes a TypeUtility class that can be used to get a list of all the generic type runtime values (in my case there's just the one - F).  I've modified his code a bit so that it returns a list of EClasses instead of classes.  Then in the FoodBasket() constructor we can easily ask for the EClass's instance class, which we will use as the dataClass parameter in the getFood EList constructor.

2) ItemProviders present inappropriate NewChildDescriptors resulting in inappropriate CreateChild commands
In my case, I should not be allowed to add an Apple to an Orange Basket (and vice versa).  If we've implemented the proposal in (1) then the command will still be executable but will throw a runtime exception when the EList tries to add the inappropriate object to its (now) type-safe array.  So at least the object doesn't get added and the CommandStack handles the exception gracefully.  However, I think we can do better and, furthermore, I think the code generator can do it for us.

First of all, the FoodBasketItemProvider.collectNewChildDescriptors looks like this, which means that both Apple and Orange child descriptors will always be added no matter what the base class that initiated the call (e.g. AppleBasketItemProvider)

	protected void collectNewChildDescriptors(Collection<Object> newChildDescriptors, Object object) {
		super.collectNewChildDescriptors(newChildDescriptors, object);

		newChildDescriptors.add
			(createChildParameter
				(FoodPackage.Literals.FOOD_BASKET__FOOD,
				 FoodFactory.eINSTANCE.createApple()));

		newChildDescriptors.add
			(createChildParameter
				(FoodPackage.Literals.FOOD_BASKET__FOOD,
				 FoodFactory.eINSTANCE.createOrange()));
	}

I propose that the generator takes the same approach as it does with property descriptors, which is to create a separate, protected function for creating each one.  It would look something like this:

	@Override
	protected void collectNewChildDescriptors(Collection<Object> newChildDescriptors, Object object) {
		super.collectNewChildDescriptors(newChildDescriptors, object);

		addAppleNewChildDescriptor(newChildDescriptors, object);
		addOrangeNewChildDescriptor(newChildDescriptors, object);
	}
	
	protected void addAppleNewChildDescriptor (Collection<Object> newChildDescriptors, Object object) {
		newChildDescriptors.add
		(createChildParameter
			(FoodPackage.Literals.FOOD_BASKET__FOOD,
			 FoodFactory.eINSTANCE.createApple()));
	}
	
	protected void addOrangeNewChildDescriptor (Collection<Object> newChildDescriptors, Object object) {
		newChildDescriptors.add
		(createChildParameter
			(FoodPackage.Literals.FOOD_BASKET__FOOD,
			 FoodFactory.eINSTANCE.createOrange()));
	}

With this approach we (i.e. the code generator) can override the specific Add???NewChildDescriptor functions.  In the case of AppleBasketItemProvider, since AppleBasket specifies that it can contain only Apples, then all other (non-abstract) subclasses of Food must not have a NewChildDescriptor.  In our case there is only one: Orange.  Here's the overridden function in AppleBasketItemProvider:

	/*
	 * (non-Javadoc)
	 * @see food.provider.FoodBasketItemProvider#addOrangeNewChildDescriptor(java.util.Collection, java.lang.Object)
	 * 
	 * We override this because we don't support creating Oranges in an Apple Basket, so it just doesn't add anything
	 * 
	 * Ideally, this would be generated. 
	 * In the eCore model we've specified a specific eClassifier (Apple) for the type parameter F, so we need
	 * an overridden method for each of the other subclasses of Fruit
	 */
	@Override
	protected void addOrangeNewChildDescriptor (Collection<Object> newChildDescriptors, Object object) {
		// Do Nothing
	}

Now when we work our way up the chain of ItemProviders, successively calling super.collectNewChildDescriptors, we eventually get to FoodBasketItemProvider.collectNewChildDescriptors, and when it calls addOrangeNewChildDescriptor then the overriden function in AppleBasketItemProvider is called, thus adding no NewChildDescriptor for Oranges.

Well, that's it.  I hope I've explained that clearly.  I'll upload my test case asap.
Cheers,
Greg
Comment 7 Greg Mising name CLA 2010-03-30 12:22:00 EDT
Created attachment 163420 [details]
Test Case - EMF Project

See my previous comment
Comment 8 Ed Merks CLA 2013-01-06 09:39:28 EST
The changes I committed for 382774 address the problem reported here (as far as I can tell).  It would be great if you could confirm that and report any oversights!

*** This bug has been marked as a duplicate of bug 382774 ***