Bug 469495 - [delta] deltas improperly merged (CHANGE being lost)
Summary: [delta] deltas improperly merged (CHANGE being lost)
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.5   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Jay Arthanareeswaran CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks:
 
Reported: 2015-06-05 11:20 EDT by Carsten Pfeiffer CLA
Modified: 2022-07-04 16:13 EDT (History)
4 users (show)

See Also:


Attachments
JUnit-Plugin-Test project (15.02 KB, application/zip)
2015-06-05 11:20 EDT, Carsten Pfeiffer CLA
no flags Details
JUnit-Plugin-Test project v2 (14.89 KB, application/zip)
2015-07-15 08:00 EDT, Carsten Pfeiffer CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Carsten Pfeiffer CLA 2015-06-05 11:20:11 EDT
Created attachment 254149 [details]
JUnit-Plugin-Test project

I believe that org.eclipse.jdt.internal.core.DeltaProcessor.mergeDeltas(Collection) improperly discards CHANGE deltas.

Consider a simple refactoring where a compilation unit is renamed from A to B and rename participant additional changes the same compilation unit in some way (e.g. adds a field, method or annotation).

After the refactoring I only get the following delta, which says nothing about the modification of the compilation unit.
Java Model[*]: {CHILDREN}
	DeltaProcessorTest[*]: {CHILDREN}
		src[*]: {CHILDREN}
			test.deltaprocessor1[*]: {CHILDREN}
				ClassBeingMoved1.java[-]: {MOVED_TO(ClassBeingMoved2.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}
				ClassBeingMoved2.java[+]: {MOVED_FROM(ClassBeingMoved1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}


But that information was actually available in mergeDeltas:
[DeltaProcessorTest[*]: {CHILDREN}
	src[*]: {CHILDREN}
		test.deltaprocessor1[*]: {CHILDREN}
			ClassBeingMoved1.java[-]: {MOVED_TO(ClassBeingMoved2.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}
			ClassBeingMoved2.java[+]: {MOVED_FROM(ClassBeingMoved1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}, Java Model[*]: {CHILDREN}
	DeltaProcessorTest[*]: {CHILDREN}
		src[*]: {CHILDREN}
			test.deltaprocessor1[*]: {CHILDREN}
				ClassBeingMoved2.java[*]: {CHILDREN | FINE GRAINED}
					ClassBeingMoved2[*]: {CHILDREN | FINE GRAINED}
						participantField[+]: {}, Java Model[*]: {CHILDREN} <------- this field was added by the participant
	DeltaProcessorTest[*]: {CHILDREN}
		src[*]: {CHILDREN}
			test.deltaprocessor1[*]: {CHILDREN}
				ClassBeingMoved1.java[-]: {MOVED_TO(ClassBeingMoved2.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}
				ClassBeingMoved2.java[+]: {MOVED_FROM(ClassBeingMoved1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}]

So the merging of the deltas discards valuable information which is essential for properly handling the change.

A testcase is attached.
Comment 1 Carsten Pfeiffer CLA 2015-07-02 08:25:04 EDT
FWIW, this bug only occurs when the compilation unit being refactored is *not open* in a java editor.

When you open the compilation unit in the testcase like this:

@Test
public void testDeltaProcessor() throws Exception {
	Assert.assertNotNull(cu);
	Assert.assertTrue(cu.exists());

	DeltaListener tempListener = registerDeltaListener(); 

	// ---> when open in an editor, the test succeeds
	IEditorPart tempEditor = JavaUI.openInEditor(cu);
	Assert.assertNotNull(tempEditor);
[...]

the testcase succeeeds.

Any idea what could cause this?
Comment 2 Vladimir Piskarev CLA 2015-07-02 08:50:20 EDT
Hi Carsten,

AFAIK, FINE_GRAINED deltas are only available for working copies. So in general you cannot expect them to be reported for compilation units not open in the Java editor.

HTH,
Vladimir
Comment 3 Carsten Pfeiffer CLA 2015-07-03 04:07:23 EDT
Hi Vladimir,

thanks for your comment. Indeed, when I change the testcase to create a working copy in the beginning like this:

cu.becomeWorkingCopy(null);

I do get the expected delta.

Now I'm not sure how to proceed and integrate this into our tooling...

Can anyone confirm that this is intended behavior?

FWIW, the code discarding the fine grained delta is at JavaElementDelta.insertDeltaTree() and the line says:

	if (!equalsAndSameParent(element, getElement())) { // handle case of two jars that can be equals but not in the same project
		addAffectedChild(childDelta);
	}
Comment 4 Jay Arthanareeswaran CLA 2015-07-06 04:10:13 EDT
I will take a look.
Comment 5 Jay Arthanareeswaran CLA 2015-07-06 06:08:18 EDT
At some point (In reply to Carsten Pfeiffer from comment #3)
> cu.becomeWorkingCopy(null);
> 
> I do get the expected delta.

What exactly are you expecting in the delta? I think the testcase passes when the delta has the following:

[[Working copy] ClassBeingRenamed1.java[*]: {AST AFFECTED}, Java Model[*]: {CHILDREN}

Which I believe is added by reconciler but perhaps not the one you are interested in?
Comment 6 Carsten Pfeiffer CLA 2015-07-06 07:30:31 EDT
Thanks for taking a look at this, Jay.

Indeed, when I have a closer look at what I get with having a working copy at hand, the problem is still there. When invoking becomeWorkingCopy(), I get a CHANGE delta before even performing the refactoring, and the testcase happily accepts that change.

So I move the registering of the delta listener after the becomeWorkingCopy() call, like this:

cu.becomeWorkingCopy(null);
DeltaListener tempListener = registerDeltaListener();

and then the testcase fails again because no CHANGE delta is being reported. I only get this, which says nothing about the field being added.

Java Model[*]: {CHILDREN}
	DeltaProcessorTest[*]: {CHILDREN}
		src[*]: {CHILDREN}
			test.deltaprocessor1[*]: {CHILDREN}
				[Working copy] ClassBeingRenamed1.java[-]: {MOVED_TO(ClassBeingRenamed2.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}
				ClassBeingRenamed2.java[+]: {MOVED_FROM([Working copy] ClassBeingRenamed1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}


What I would additionally expect is this:

ClassBeingMoved2.java[*]: {CHILDREN | FINE GRAINED}
					ClassBeingMoved2[*]: {CHILDREN | FINE GRAINED}
						participantField[+]: {}, Java Model[*]: {CHILDREN} <------- this field was added by the participant


This delta is actually produced by the reconciler, but it is discarded by DeltaProcessor.mergeDeltas(), or more concretely, JavaElementDelta# insertDeltaTree(IJavaElement element, JavaElementDelta delta).
Comment 7 Jay Arthanareeswaran CLA 2015-07-07 00:28:47 EDT
I see the problem. As you said, we take no action when we find out that there is an existing child delta on the same element (in this case, the CU). Perhaps we should check for fine grained changes and add it too. I will take a look.
Comment 8 Jay Arthanareeswaran CLA 2015-07-08 01:14:24 EDT
On closer look, this seems like a well thought out strategy of not reporting finer changes when an element is either added or removed.

I wonder if it makes sense in most cases. In this particular case, though, it might, because even though the delta kind is ADDED, it was actually moved from another element.

ClassBeingRenamed2.java[+]: {CHILDREN | MOVED_FROM([Working copy] ClassBeingRenamed1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]]) | FINE GRAINED}]

But I am still wondering whether we also want the children (participantField) to be part of the delta tree.
Comment 9 Jay Arthanareeswaran CLA 2015-07-09 02:44:08 EDT
The Gerrit patch is here with some test changes. 

https://git.eclipse.org/r/#/c/51634/

While it works, the following delta makes me bit uncomfortable:
test.deltaprocessor1[*]: {CHILDREN}
				[Working copy] ClassBeingRenamed1.java[-]: {MOVED_TO(ClassBeingRenamed2.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]])}
				ClassBeingRenamed2.java[+]: {CHILDREN | MOVED_FROM([Working copy] ClassBeingRenamed1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]]) | FINE GRAINED}
					ClassBeingRenamed2[+]: {CHILDREN | MOVED_FROM(ClassBeingRenamed1 [in [Working copy] ClassBeingRenamed1.java [in test.deltaprocessor1 [in src [in DeltaProcessorTest]]]]) | FINE GRAINED}
						participantField[+]: {}]

Will see how the test goes.
Comment 10 Jay Arthanareeswaran CLA 2015-07-09 03:55:44 EDT
(In reply to Carsten Pfeiffer from comment #0)
> Created attachment 254149 [details]
> JUnit-Plugin-Test project

Hi Carsten, can you please update the patch to match the detla I posted in comment #9? Thanks!
Comment 11 Carsten Pfeiffer CLA 2015-07-13 05:13:08 EDT
Thanks a lot Jay,

I will check it out tomorrow.
Comment 12 Jay Arthanareeswaran CLA 2015-07-13 05:44:32 EDT
(In reply to Jay Arthanareeswaran from comment #9)
> The Gerrit patch is here with some test changes. 
> 
> https://git.eclipse.org/r/#/c/51634/

All JDT Core and UI tests pass with this patch. But ...

this violates the following documentation on IJavaElementDelta#ADDED:

	/**
	 * Status constant indicating that the element has been added.
	 * Note that an added java element delta has no children, as they are all implicitly added.
	 */

Now I am not entirely sure if we will be breaking any clients. May be we can make exceptions for 'move' deltas? Copying Markus on behalf of JDT UI who will be the foremost client.
Comment 13 Carsten Pfeiffer CLA 2015-07-15 08:00:28 EDT
Created attachment 255211 [details]
JUnit-Plugin-Test project v2

The updated testcase, now looking explicitly for the added field.

Since there is no modification delta, I removed the code looking for that. The testcase succeeds with your changes in https://git.eclipse.org/r/#/c/51634/
Comment 14 Carsten Pfeiffer CLA 2015-07-15 08:02:40 EDT
(In reply to Jay Arthanareeswaran Away till 20 July from comment #12)
> Now I am not entirely sure if we will be breaking any clients. May be we can
> make exceptions for 'move' deltas? Copying Markus on behalf of JDT UI who
> will be the foremost client.

Would there be an alternative like adding a MODIFIED delta after the ADDED delta?

Thanks again for looking into this!
Comment 15 Jay Arthanareeswaran CLA 2016-04-05 04:33:04 EDT
No progress yet and unlikely to get time during 4.6. Moving out.
Comment 16 Manoj N Palat CLA 2018-05-21 06:07:03 EDT
Bulk move out of 4.8
Comment 17 Eclipse Genie CLA 2020-06-01 07:23:49 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 18 Eclipse Genie CLA 2022-07-04 16:13:17 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.