Bug 487748 - Fragment contribution always replaces model element
Summary: Fragment contribution always replaces model element
Status: CLOSED DUPLICATE of bug 440030
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.6   Edit
Hardware: PC Windows NT
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Jonas Helming CLA
QA Contact:
URL:
Whiteboard:
Keywords: helpwanted
Depends on: 480610
Blocks: 546777
  Show dependency tree
 
Reported: 2016-02-12 10:36 EST by Alexandra Buzila CLA
Modified: 2019-06-01 16:58 EDT (History)
7 users (show)

See Also:


Attachments
test application (19.92 KB, application/x-zip-compressed)
2016-02-12 10:36 EST, Alexandra Buzila CLA
no flags Details
Class cast exception in ModelUtils.mergeList() (218.23 KB, image/png)
2017-12-05 03:54 EST, Wernke zur Borg CLA
no flags Details
Class cast exception in ModelUtils.mergeList() (197.53 KB, image/png)
2017-12-05 03:55 EST, Wernke zur Borg CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alexandra Buzila CLA 2016-02-12 10:36:28 EST
Created attachment 259731 [details]
test application

Consider an application model with a PartStack and a fragment that contributes a Part to that PartStack, configured to be applied as "notexists" (so, only if the contributed elements are not already part of the application model). The expectation would be that the contributed part will not be added to the PartStack if the stack already contains an element with the same element id.

However, it seems that the elements from the fragment will always be merged and will replace the ones from the application model.

To test this you can use the attached application that contains:
- the application model with a  PartStack and a Part (part1)
- a fragment that contributes the same part1 to the application's PartStack (same elementId and even the same resource xmi id) only if it doesn't exist (notexists)
- a pre-processor (Processor1) that adds a new value to part1's persisted state
- a post-processor (Processor2) that reads part1's persisted state

The expectation would be that part1's persisted state still contains the value set by the pre-processor even after the fragments are merged. However, this is not the case, as part1 gets replaced by the element contributed by the fragment.

Even if if part1 is not manually added to the application model, but initially contributed by the fragment, and the application model is then loaded from the persisted state, the results are the same. 


The problem seems to be caused by the ModelUtils#mergeList method that is being used for merging the elements contributed by the fragment. The method checks if the application model contains an element with the same id as the one we are trying to merge and at the same time checks if the app model element is _equal_ to the element we want to merge. Only if both conditions are satisfied, the fragment element is not merged. Moreover, the code does not care at this point under what conditions the fragment element was contributed (notexists, always etc.).

The equality check is performed using EcoreUtil#equals(object1, object2), which will always return false when comparing a model element with a fragment element, because the elements have different parents: one is contained in the application model, while the the other in the model fragment.
Comment 1 Brian de Alwis CLA 2016-02-13 21:40:34 EST
Thank you for the diagnosis!  Could you please provide a patch?

https://wiki.eclipse.org/Platform_UI/How_to_Contribute
Comment 2 Jonas Helming CLA 2016-02-15 06:01:11 EST
we will do...
Comment 3 Wernke zur Borg CLA 2017-12-05 03:52:14 EST
Is there any news on this?

I found another issue with the ModelUtils#mergeList method. In the last else branch labelled "replace" it uses the UsageCrossReferencer#find to find references to the replaced object. The list of settings returned by the UsageCrossReferencer may contain a Setting that is actually a list. Calling Setting#set on a list results in a class cast exception.

See also attached debugger screenshots.

java.lang.ClassCastException: org.eclipse.e4.ui.model.application.commands.impl.BindingContextImpl cannot be cast to java.util.List
	at org.eclipse.emf.ecore.util.EcoreEList.set(EcoreEList.java:457)
	at org.eclipse.e4.ui.model.internal.ModelUtils.mergeList(ModelUtils.java:189)
	at org.eclipse.e4.ui.model.internal.ModelUtils.merge(ModelUtils.java:145)
	at org.eclipse.e4.ui.model.fragment.impl.StringModelFragmentImpl.mergeIdList(StringModelFragmentImpl.java:322)
	at org.eclipse.e4.ui.model.fragment.impl.StringModelFragmentImpl.merge(StringModelFragmentImpl.java:300)
	at org.eclipse.e4.ui.internal.workbench.ModelAssembler.processModelFragment(ModelAssembler.java:272)
	at org.eclipse.e4.ui.internal.workbench.ModelAssembler.processFragmentConfigurationElement(ModelAssembler.java:162)
	at org.eclipse.e4.ui.internal.workbench.ModelAssembler.processFragments(ModelAssembler.java:118)
	at org.eclipse.e4.ui.internal.workbench.ModelAssembler.processModel(ModelAssembler.java:96)
	at org.eclipse.e4.ui.internal.workbench.ResourceHandler.loadMostRecentModel(ResourceHandler.java:197)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.loadApplicationModel(E4Application.java:377)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createE4Workbench(E4Application.java:252)
	at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:632)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:610)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148)
	at esa.open.application.Application.start(Application.java:55)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1519)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1492)
Comment 4 Wernke zur Borg CLA 2017-12-05 03:54:34 EST
Created attachment 271774 [details]
Class cast exception in ModelUtils.mergeList()

Debugger screenshot 1
Comment 5 Wernke zur Borg CLA 2017-12-05 03:55:01 EST
Created attachment 271775 [details]
Class cast exception in ModelUtils.mergeList()

Debugger screenshot 2
Comment 6 Rolf Theunissen CLA 2019-06-01 16:40:59 EDT
*** Bug 547503 has been marked as a duplicate of this bug. ***
Comment 7 Rolf Theunissen CLA 2019-06-01 16:41:32 EDT
The problem is not really in ModelUtils#mergeList

The root cause is in ModelAssembler#processModelFragment, it does check if the element is already applied:

if (checkExist && applicationResource.getIDToEObjectMap().containsKey(r.getID(o))) {
   continue;
}

However, at the end of the method, the fragment is merge unconditionally.

return fragment.merge(application);
Comment 8 Rolf Theunissen CLA 2019-06-01 16:46:22 EDT
(In reply to Wernke zur Borg from comment #3)

I created Bug 547839 for this separate issue.
Comment 9 Rolf Theunissen CLA 2019-06-01 16:58:47 EDT

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