Skip to main content
summaryrefslogtreecommitdiffstats
blob: e0c570c6369273d2e6f4ff8ce2bc51b463b4b600 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*******************************************************************************
 * Copyright (c) 2006, 2011 Obeo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.emf.compare.diff.merge;

import org.eclipse.emf.compare.diff.merge.service.MergeService;
import org.eclipse.emf.compare.diff.metamodel.ConflictingDiffElement;
import org.eclipse.emf.compare.diff.metamodel.DiffElement;
import org.eclipse.emf.compare.diff.metamodel.DiffGroup;
import org.eclipse.emf.compare.diff.metamodel.DiffModel;
import org.eclipse.emf.compare.diff.metamodel.ModelElementChangeLeftTarget;
import org.eclipse.emf.compare.diff.metamodel.ModelElementChangeRightTarget;
import org.eclipse.emf.compare.diff.metamodel.ReferenceChange;
import org.eclipse.emf.compare.diff.metamodel.ReferenceChangeLeftTarget;
import org.eclipse.emf.compare.diff.metamodel.ReferenceChangeRightTarget;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;

/**
 * Basic implementation of an {@link IMerger}. Clients can extend this class instead of implementing IMerger
 * to avoid reimplementing all methods.
 * 
 * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
 */
public class DefaultMerger implements IMerger {
	/** {@link DiffElement} to be merged by this merger. */
	protected DiffElement diff;

	/** Keeps a reference on the left resource for this merger. */
	@Deprecated
	protected Resource leftResource;

	/** Keeps a reference on the right resource for this merger. */
	@Deprecated
	protected Resource rightResource;

	/**
	 * {@inheritDoc}
	 * 
	 * @see org.eclipse.emf.compare.diff.merge.IMerger#applyInOrigin()
	 */
	public void applyInOrigin() {
		handleMutuallyDerivedReferences();
		removeFromContainer(diff);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see org.eclipse.emf.compare.diff.merge.IMerger#canApplyInOrigin()
	 */
	public boolean canApplyInOrigin() {
		return true;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see org.eclipse.emf.compare.diff.merge.IMerger#canUndoInTarget()
	 */
	public boolean canUndoInTarget() {
		return true;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see org.eclipse.emf.compare.diff.merge.IMerger#setDiffElement(org.eclipse.emf.compare.diff.metamodel.DiffElement)
	 */
	public void setDiffElement(DiffElement element) {
		diff = element;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see org.eclipse.emf.compare.diff.merge.IMerger#undoInTarget()
	 */
	public void undoInTarget() {
		handleMutuallyDerivedReferences();
		removeFromContainer(diff);
	}

	/**
	 * Removes the given {@link DiffGroup} from its container if it was its last child, also calls for the
	 * same cleanup operation on its hierarchy.
	 * 
	 * @param diffGroup
	 *            {@link DiffGroup} we want to cleanup.
	 */
	protected void cleanDiffGroup(DiffGroup diffGroup) {
		if (diffGroup != null && diffGroup.getSubchanges() == 0) {
			final EObject parent = diffGroup.eContainer();
			if (parent instanceof DiffGroup) {
				EcoreUtil.remove(diffGroup);
				cleanDiffGroup((DiffGroup)parent);
			}
		}
	}

	/**
	 * Creates a copy of the given EObject as would {@link EcoreUtil#copy(EObject)} would, except we use
	 * specific handling for unmatched references.
	 * 
	 * @param eObject
	 *            The object to copy.
	 * @return the copied object.
	 */
	protected EObject copy(EObject eObject) {
		final EMFCompareEObjectCopier copier = MergeService.getCopier(diff);
		final EObject result = copier.copy(eObject);
		copier.copyReferences();
		copier.copyXMIIDs();
		return result;
	}

	/**
	 * Returns the {@link DiffModel} containing the {@link DiffElement} this merger is intended to merge.
	 * 
	 * @return The {@link DiffModel} containing the {@link DiffElement} this merger is intended to merge.
	 */
	protected DiffModel getDiffModel() {
		EObject container = diff.eContainer();
		while (container != null) {
			if (container instanceof DiffModel)
				return (DiffModel)container;
			container = container.eContainer();
		}
		return null;
	}

	/**
	 * Returns the XMI ID of the given {@link EObject} or <code>null</code> if it cannot be resolved.
	 * 
	 * @param object
	 *            Object which we seek the XMI ID of.
	 * @return <code>object</code>'s XMI ID, <code>null</code> if not applicable.
	 */
	protected String getXMIID(EObject object) {
		String objectID = null;
		if (object != null && object.eResource() instanceof XMIResource) {
			objectID = ((XMIResource)object.eResource()).getID(object);
		}
		return objectID;
	}

	/**
	 * Removes all references to the given {@link EObject} from the {@link DiffModel}.
	 * 
	 * @param deletedObject
	 *            Object to remove all references to.
	 */
	protected void removeDanglingReferences(EObject deletedObject) {
		// EObject root = EcoreUtil.getRootContainer(deletedObject);
		// if (root instanceof ComparisonResourceSnapshot) {
		// root = ((ComparisonResourceSnapshot)root).getDiff();
		// }
		// if (root != null) {
		// // FIXME performance, find a way to cache this referencer
		// final Resource res = root.eResource();
		// final EcoreUtil.CrossReferencer referencer;
		// if (res != null && res.getResourceSet() != null) {
		// referencer = new EcoreUtil.CrossReferencer(res.getResourceSet()) {
		// private static final long serialVersionUID = 616050158241084372L;
		//
		// // initializer for this anonymous class
		// {
		// crossReference();
		// }
		//
		// @Override
		// protected boolean crossReference(EObject eObject, EReference eReference,
		// EObject crossReferencedEObject) {
		// if (eReference.isChangeable() && !eReference.isDerived())
		// return crossReferencedEObject.eResource() == null;
		// return false;
		// }
		// };
		// } else if (res != null) {
		// referencer = new EcoreUtil.CrossReferencer(res) {
		// private static final long serialVersionUID = 616050158241084372L;
		//
		// // initializer for this anonymous class
		// {
		// crossReference();
		// }
		//
		// @Override
		// protected boolean crossReference(EObject eObject, EReference eReference,
		// EObject crossReferencedEObject) {
		// if (eReference.isChangeable() && !eReference.isDerived())
		// return crossReferencedEObject.eResource() == null;
		// return false;
		// }
		// };
		// } else {
		// referencer = new EcoreUtil.CrossReferencer(root) {
		// private static final long serialVersionUID = 616050158241084372L;
		//
		// // initializer for this anonymous class
		// {
		// crossReference();
		// }
		//
		// @Override
		// protected boolean crossReference(EObject eObject, EReference eReference,
		// EObject crossReferencedEObject) {
		// if (eReference.isChangeable() && !eReference.isDerived())
		// return crossReferencedEObject.eResource() == null;
		// return false;
		// }
		// };
		// }
		// final Iterator<Map.Entry<EObject, Collection<EStructuralFeature.Setting>>> i = referencer
		// .entrySet().iterator();
		// while (i.hasNext()) {
		// final Map.Entry<EObject, Collection<EStructuralFeature.Setting>> entry = i.next();
		// final Iterator<EStructuralFeature.Setting> j = entry.getValue().iterator();
		// while (j.hasNext()) {
		// EcoreUtil.remove(j.next(), entry.getKey());
		// }
		// }
		// }
	}

	/**
	 * Removes a {@link DiffElement} from its {@link DiffGroup}.
	 * 
	 * @param diffElement
	 *            {@link DiffElement} to remove from its container.
	 */
	protected void removeFromContainer(DiffElement diffElement) {
		final EObject parent = diffElement.eContainer();
		EcoreUtil.remove(diffElement);
		removeDanglingReferences(parent);

		// If diff was contained by a ConflictingDiffElement, we call back this on it
		if (parent instanceof ConflictingDiffElement) {
			removeFromContainer((DiffElement)parent);
		}

		// if diff was in a diffGroup and it was the last one, we also remove the diffgroup
		if (parent instanceof DiffGroup) {
			cleanDiffGroup((DiffGroup)parent);
		}
	}

	/**
	 * Sets the XMI ID of the given {@link EObject} if it belongs in an {@link XMIResource}.
	 * 
	 * @param object
	 *            Object we want to set the XMI ID of.
	 * @param id
	 *            XMI ID to give to <code>object</code>.
	 */
	protected void setXMIID(EObject object, String id) {
		if (object != null && object.eResource() instanceof XMIResource) {
			((XMIResource)object.eResource()).setID(object, id);
		}
	}

	/**
	 * Mutually derived references need specific handling : merging one will implicitely merge the other and
	 * there are no way to tell such references apart.
	 * <p>
	 * Currently known references raising such issues :
	 * <table>
	 * <tr>
	 * <td>{@link EcorePackage#ECLASS__ESUPER_TYPES}</td>
	 * <td>{@link EcorePackage#ECLASS__EGENERIC_SUPER_TYPES}</td>
	 * </tr>
	 * </table>
	 * </p>
	 */
	private void handleMutuallyDerivedReferences() {
		DiffElement toRemove = null;
		if (diff instanceof ReferenceChange) {
			final EReference reference = ((ReferenceChange)diff).getReference();
			if (reference == EcorePackage.eINSTANCE.getEClass_ESuperTypes()) {
				final EObject referenceType;
				if (diff instanceof ReferenceChangeLeftTarget) {
					referenceType = ((ReferenceChangeLeftTarget)diff).getRightTarget();
				} else {
					referenceType = ((ReferenceChangeRightTarget)diff).getLeftTarget();
				}
				for (final DiffElement siblingDiff : ((DiffGroup)diff.eContainer()).getSubDiffElements()) {
					if (siblingDiff instanceof ModelElementChangeLeftTarget) {
						if (((ModelElementChangeLeftTarget)siblingDiff).getLeftElement() instanceof EGenericType
								&& ((EGenericType)((ModelElementChangeLeftTarget)siblingDiff)
										.getLeftElement()).getEClassifier() == referenceType) {
							toRemove = siblingDiff;
							break;
						}
					} else if (siblingDiff instanceof ModelElementChangeRightTarget) {
						if (((ModelElementChangeRightTarget)siblingDiff).getRightElement() instanceof EGenericType
								&& ((EGenericType)((ModelElementChangeRightTarget)siblingDiff)
										.getRightElement()).getEClassifier() == referenceType) {
							toRemove = siblingDiff;
							break;
						}
					}
				}
			}
		} else if (diff instanceof ModelElementChangeLeftTarget
				&& ((ModelElementChangeLeftTarget)diff).getLeftElement() instanceof EGenericType) {
			final ModelElementChangeLeftTarget theDiff = (ModelElementChangeLeftTarget)diff;
			final EClassifier referenceType = ((EGenericType)theDiff.getLeftElement()).getEClassifier();
			for (final DiffElement siblingDiff : ((DiffGroup)diff.eContainer()).getSubDiffElements()) {
				if (siblingDiff instanceof ReferenceChangeLeftTarget
						&& ((ReferenceChangeLeftTarget)siblingDiff).getReference().getFeatureID() == EcorePackage.ECLASS__ESUPER_TYPES) {
					if (((ReferenceChangeLeftTarget)siblingDiff).getRightTarget() == referenceType) {
						toRemove = siblingDiff;
						break;
					}
				}
			}
		} else if (diff instanceof ModelElementChangeRightTarget
				&& ((ModelElementChangeRightTarget)diff).getRightElement() instanceof EGenericType) {
			final ModelElementChangeRightTarget theDiff = (ModelElementChangeRightTarget)diff;
			final EClassifier referenceType = ((EGenericType)theDiff.getRightElement()).getEClassifier();
			for (final DiffElement siblingDiff : ((DiffGroup)diff.eContainer()).getSubDiffElements()) {
				if (siblingDiff instanceof ReferenceChangeRightTarget
						&& ((ReferenceChangeRightTarget)siblingDiff).getReference().getFeatureID() == EcorePackage.ECLASS__ESUPER_TYPES) {
					if (((ReferenceChangeRightTarget)siblingDiff).getLeftTarget() == referenceType) {
						toRemove = siblingDiff;
						break;
					}
				}
			}
		}
		if (toRemove != null) {
			removeFromContainer(toRemove);
		}
	}

}

Back to the top