Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » Compare » MOVE DifferenceKind semantics(How to interpret MOVE semantics)
MOVE DifferenceKind semantics [message #1049089] Thu, 25 April 2013 10:53 Go to next message
Victor Roldan Betancort is currently offline Victor Roldan BetancortFriend
Messages: 524
Registered: July 2009
Senior Member
Hi guys,

I'm facing an extrange behaviour with a MOVE kind of change. Just want to make sure
im interpreting correctly what a MOVE means, or otherwise have found some kind of bug.

First of all: is it possible to deactivate globally the MOVE type of change without customizing the DiffEngine? The default engine seems to find MOVE operations where there should actually be a ADD + REMOVE. (I think I already asked this like a year ago... please excuse me then, but 2.x is a whole different story)

My actual example: the engine thinks certain element has changed its eContainer. Im ok with that (I can manage to decompose in our client app a MOVE into a ADD+REMOVE), but I can't seem to understand how this kind of Diffs are interpreted.

These come in the shape of a ReferenceChange. One would expect that a Move operation has:
- a source of the move (the source/old eContainer of the moved element)
- a target of the move (the target/new eContainer of the moved element)
- the actual moved element

However, I can't find anything in the ReferenceChange instance that points me to the first element listed before, this is, there is no reference to the source of the move. In fact, if I take a look at how the editor shows this, it suggests that an element has moved from a container to the same container! Sounds like something that cannot happen (in any case, the would be a reordering operation).

So... am I missinterpreting the meaning of the MOVE operation? Or is the default DiffEngine doing something wrong?

I found org.eclipse.emf.compare.diff.DefaultDiffEngine.computeContainmentDifferencesTwoWay(Match, EReference, boolean), looks like the place to start looking for a fix... I'm ok?

Related to the reordering operation: I can seem to be able to differentiate the two kinds of MOVE operations without some extra coding: eContainer kind of MOVE or reordering kind of MOVE. I was forced to check if the containers where matched to derive if its reordering or econtainer change. Is this the way?

Sorry for the long post... new API, a whole new world to discover!

And of course, many thanks in advance for your feedback Smile

Víctor [Open Canarias]
Re: MOVE DifferenceKind semantics [message #1049144 is a reply to message #1049089] Thu, 25 April 2013 12:38 Go to previous messageGo to next message
Laurent Goubet is currently offline Laurent GoubetFriend
Messages: 1639
Registered: July 2009
Senior Member
Victor,

I had started describing all of these on the wiki. This is far from complete, but the different kind of MOVE are there.

In short,
- we detect reordering as individual "MOVE"s (we decompose "reordered reference x" in "E1 has moved within x" and "E2 has moved within x"), which allows users to only accept a given set of moves instead of reordering everything.
- we detect the "change of containment reference" as a move, even within the same EObject. A given EObject A may have two containment references rc1 and rc2. If you move an element from rc1 to rc2 or the reverse, we detect it as a move.

We have greatly simplified the difference metamodel, which is why you can't find what you're searching for Smile. In EMF Compare 1, we recorded just about every bit of information we could gather... most of which was useless as it still had to be re-computed for every merge operation (since it can change because of *other* merge operations). And yet, ironically, the new metamodel actually stores more information... though it needs to be looked up.

A "Diff" is contained within a "Match", that is the greatest difference between compare 1 and compare 2, and how you have to look for information. For example, if you have a MOVE for element B within the reference "ref1" of another element A, calling "matching" elements "A" in the left model and "A'" in the right, "B" in left and "B'" in right...

diff.getMatch().getLeft() is A
diff.getMatch().getRight() is A'

((ReferenceChange)diff).getValue() is B
diff.getMatch().getComparison().getMatch(((ReferenceChange)diff).getValue()) is the match of "B" (from which you'll find B and B' as you did with A)

Note that these are true if you are in two-way or if the MOVE is local (i.e. diff.getSource() is "left"). If you are in a three-way comparison and the MOVE is remote (diff.getSource() == DifferenceSource.RIGHT), then diff.getValue() will be the "B" from the right model.

The metamodel has been created with merging in mind. If you need to try and make sense of all this, it may be easier to look at our mergers (ReferenceChangeMerger for example) since that is where we really use everything.

[edit: if you need to determine whether a move is an ordering move or not, see how we do in ReferenceChangeMerger#moveElement()]

Laurent Goubet
Obeo

[Updated on: Thu, 25 April 2013 12:44]

Report message to a moderator

Re: MOVE DifferenceKind semantics [message #1049174 is a reply to message #1049144] Thu, 25 April 2013 13:24 Go to previous messageGo to next message
Victor Roldan Betancort is currently offline Victor Roldan BetancortFriend
Messages: 524
Registered: July 2009
Senior Member
Laurent,

comments below

Quote:

A "Diff" is contained within a "Match", that is the greatest difference between compare 1 and compare 2, and how you have to look for information. For example, if you have a MOVE for element B within the reference "ref1" of another element A, calling "matching" elements "A" in the left model and "A'" in the right, "B" in left and "B'" in right...

diff.getMatch().getLeft() is A
diff.getMatch().getRight() is A'

((ReferenceChange)diff).getValue() is B
diff.getMatch().getComparison().getMatch(((ReferenceChange)diff).getValue()) is the match of "B" (from which you'll find B and B' as you did with A)


Correct me if wrong but... isn't the above scenario you're describing the reordering scenario? What I'd like to know is the containment change scenario, where there are three parties, lets call them:

- A receiver container of the moved object
- B the moved object
- C source container of the moved object

So, to avoid ambiguity if it isn't clear, object B moves from a containment feature in C to another containment feature in A. In this scenario what you wrote above applies:

diff.getMatch().getLeft() is A (the receiver of the moved object)
diff.getMatch().getRight() is A' (the match of A in the original/previous/right model)

((ReferenceChange)diff).getValue() is B (the moved object)
diff.getMatch().getComparison().getMatch(((ReferenceChange)diff).getValue()) (the match in the original/previous/right model of the moved object).

So what im missing here is how to fetch C. I had to infer if from the model, but I feel there should be some kind of API in ReferenceChange (I believe it used the exist its counterpart in compare 1.X).

I could calculate C anyway. Calculating it is not what bothers me. The real problem is that,assumming the above scenario is true, there should be a second ReferenceChange for C. Why? Because two objects where changed: A and C. We could either have a single Diff indicating both suffered changes, or we could have 2 diffs, one for A "receiving" B and another one for C "emitting" B.

In fact, since the second diff does not exist, I had to use IPostProcessor to add it myself. That way it worked for me, but I wonder why the framework does not consider this.

Im probably still missing something, it sounds too obvious that you guys already considered this scenario during development :S
Re: MOVE DifferenceKind semantics [message #1049240 is a reply to message #1049174] Thu, 25 April 2013 14:52 Go to previous messageGo to next message
Laurent Goubet is currently offline Laurent GoubetFriend
Messages: 1639
Registered: July 2009
Senior Member
Victor,

You can infer "old" and "new" container quite easily as soon as you have the match of the "moved" value, even with only the Diff as input :

Comparison comparison = diff.getMatch().getComparison();
Match valueMatch = comparison.getMatch(((ReferenceChange)diff).getValue());
EObject leftValue = valueMatch.getLeft();
EObject rightValue = valueMatch.getRight();

EObject leftContainer = leftValue.eContainer();
EObject rightContainer = rightValue.eContainer();


This was done that way since, depending on the Diff, depending on the source, depending on the direction of the merge, what we actually have to do is a different operation. Cancel the move, move to an existing container, move to a new container, move into another resource, move from one containing reference to another, reorder two elements in a containment reference, reorder two roots of a resource...

You can also change the DiffProcessor instead of post processing and override how we react to "move" by default... creating two Diff elements instead of one...

However, in the scenario you describe here, we do see only one difference. The element that changed was B: it moved, not A or C. It moved "from A to C", yes... but as far as merging is concerned, that is not an information we need... nor can we really determine it before the user actually asks to merge in one direction or the other.

All that matters is "B" and its target container. Setting the container will have EMF do the move by itself.

left:
|-A
|-C
|- B

right
|-A'
| |-B'
|-C'

If we copy the move "from right to left" : we need to copy B into A. Nothing to lookup here : diff.getMatch().getleft().eGet(ref).add(diff.getValue()).
If we try and copy "from left to right" : we need to move B' into C'. Nothing much to do either :
Match valueMatch = comparison.getMatch(diff.getValue); //left.eContainer == C, right.eContainer() == A'
EObject leftContainer = valueMatch.getLeft().eContainer();
Match containerMatch = comparison.getMatch(leftContainer);
// move
containerMatch.getRight().eGet(ref).add(valueMatch.getRight());


There is indeed a little more to compute ... but in fact this code is exactly the same for both directions, save for the "getLeft" and "getRight" calls Wink.

Then again, I don't really know why you are trying to get two diffs here.

Laurent Goubet
Obeo

[Updated on: Thu, 25 April 2013 14:52]

Report message to a moderator

Re: MOVE DifferenceKind semantics [message #1049271 is a reply to message #1049240] Thu, 25 April 2013 15:47 Go to previous messageGo to next message
Victor Roldan Betancort is currently offline Victor Roldan BetancortFriend
Messages: 524
Registered: July 2009
Senior Member
Laurent,

this more clear now, at least confirmed the beaviour is right, and which measures I have to take to tackle it.

Quote:

You can also change the DiffProcessor instead of post processing and override how we react to "move" by default... creating two Diff elements instead of one...


Any particular over IPostProcessor?

Quote:

Then again, I don't really know why you are trying to get two diffs here.


I guess our interpreter could have derived one add and one remove from a single move diff. But the issues is trying to make old code work right... I could also rewrite everything Razz

The key that made me decide triggering 2 differnces is easy:

In EMF compare 1.x, when you call getDifferences() over our "C" object (this means, if I ask the framework "are there any differences affecting C object, the source of the move in our scenario), it used to return a difference, since C lost one contained child. Now in EMF Compare 2.X, we don't get any difference (as you said, it was B the one that changed, not A nor C).

You feedback is very much appreciated!

Víctor [Open Canarias]
Re: MOVE DifferenceKind semantics [message #1049711 is a reply to message #1049271] Fri, 26 April 2013 07:19 Go to previous message
Laurent Goubet is currently offline Laurent GoubetFriend
Messages: 1639
Registered: July 2009
Senior Member
Hi,

Quote:
Any particular over IPostProcessor?


On the link I gave for the wiki entry Smile.

The interface is really simple. Basically, the diff engine "detects" differences, and it notifies a diff processor that such or such feature has changed, passing it the details. It is then the diff processor's reponsibility to actually create the difference and add it to the comparison model. You can thus override the diff processor and react differently to some notifications, in this case, the MOVE.

A naive implementation given what you said above :

IDiffProcessor customDiffProcessor = new DiffBuilder() {
	@Override
	public void attributeChange(Match match, EAttribute attribute, Object value, DifferenceKind kind, DifferenceSource source) {
		if (kind == DifferenceKind.MOVE) {
			super.attributeChange(match, attribute, value, DifferenceKind.ADD, source);
			super.attributeChange(match, attribute, value, DifferenceKind.DELETE, source);
		} else {
			super.attributeChange(match, attribute, value, kind, source);
		}
	}

	@Override
	public void referenceChange(Match match, EReference reference, EObject value, DifferenceKind kind, DifferenceSource source) {
		if (kind == DifferenceKind.MOVE) {
			super.referenceChange(match, reference, value, DifferenceKind.ADD, source);
			super.referenceChange(match, reference, value, DifferenceKind.DELETE, source);
		} else {
			super.referenceChange(match, reference, value, kind, source);
		}
	}
};

IDiffEngine diffEngine = new DefaultDiffEngine(customDiffProcessor);
EMFCompare.builder().setDiffEngine(diffEngine).build().compare(scope);


Laurent Goubet
Obeo

[Updated on: Fri, 26 April 2013 07:22]

Report message to a moderator

Previous Topic:Programmatically opening compare editor
Next Topic:[EMF Compare] Retrieve original value of AttributeChange
Goto Forum:
  


Current Time: Thu Dec 18 16:23:49 GMT 2014

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

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