Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: b1bc5a649da8f5fe35272e8f09f53068dc6f75df (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
/*****************************************************************************
 * Copyright (c) 2012 CEA LIST.
 *
 *    
 * 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:
 *  Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.uml.compare.merger.internal.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.compare.diff.merge.IMergeListener;
import org.eclipse.emf.compare.diff.merge.IMerger;
import org.eclipse.emf.compare.diff.merge.service.MergeFactory;
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.transaction.TransactionalEditingDomain;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.tools.util.ReflectHelper;
import org.eclipse.papyrus.uml.compare.merger.Activator;
import org.eclipse.papyrus.uml.compare.merger.internal.commands.FireMergeDiffEndCommand;
import org.eclipse.papyrus.uml.compare.merger.internal.commands.FireMergeDiffStartCommand;
import org.eclipse.papyrus.uml.compare.merger.internal.commands.FireMergeOperationEndCommand;
import org.eclipse.papyrus.uml.compare.merger.internal.commands.FireMergeOperationStartCommand;
import org.eclipse.papyrus.uml.compare.merger.utils.ITransactionalMerger;

/**
 * 
 * This service should replace the EMF-Compare MergeService
 * 
 */
//TODO not used
public class TransactionalMergeService {

	private static Method addMergeListenerMethod = null;

	private static Method removeMergeListenerMethod = null;

	//	/** This copier will be used when merging references. */
	//	private static EMFCompareEObjectCopier copier;

	/**
	 * Default constructor.
	 */
	private TransactionalMergeService() {
		// hides default constructor
	}

	/**
	 * TODO this method should be covered with a JUnit test
	 * Registers a new merge listener for notifications about merge operations. Has no effect if the listener
	 * is already registered.
	 * 
	 * @param listener
	 *        New Listener to register for notifications.
	 */
	public static void addMergeListener(final IMergeListener listener) {
		if(addMergeListenerMethod == null) {
			Class<?>[] parameterTypes = new Class[1];
			parameterTypes[0] = IMergeListener.class;
			try {
				addMergeListenerMethod = ReflectHelper.getMethod(MergeService.class, "addMergeListener", parameterTypes);
			} catch (SecurityException e) {
				Activator.log.error(e);
			} catch (NoSuchMethodException e) {
				Activator.log.error(e);
			}
		}
		final Object[] parameters = new Object[1];
		parameters[0] = listener;
		try {
			addMergeListenerMethod.invoke(null, parameters);
		} catch (IllegalArgumentException e) {
			Activator.log.error(e);
		} catch (IllegalAccessException e) {
			Activator.log.error(e);
		} catch (InvocationTargetException e) {
			Activator.log.error(e);
		}
	}


	//	/**
	//	 * Returns the copier given the diff it should merge.
	//	 * 
	//	 * @param diff
	//	 *            The DiffElement for which a copier is needed.
	//	 * @return The copier for a given diff.
	//	 */
	//	public static EMFCompareEObjectCopier getCopier(final DiffElement diff) {
	//		final DiffModel diffModel = getContainerDiffModel(diff);
	//		if (diffModel == null)
	//			throw new IllegalArgumentException("The diff element should be contained in a DiffModel instance"); //$NON-NLS-1$
	//		if (diffModel.eContainer() instanceof DiffResourceSet) {
	//			if (copier == null) {
	//				copier = new EMFCompareEObjectCopier((DiffResourceSet)diffModel.eContainer());
	//			} else if (copier.getDiffResourceSet() != diffModel.eContainer()) {
	//				copier.clear();
	//				copier = new EMFCompareEObjectCopier((DiffResourceSet)diffModel.eContainer());
	//			}
	//		} else {
	//			if (copier == null) {
	//				copier = new EMFCompareEObjectCopier(diffModel);
	//			} else if (copier.getDiffModel() != diffModel) {
	//				copier.clear();
	//				copier = new EMFCompareEObjectCopier(diffModel);
	//			}
	//		}
	//		return copier;
	//	}

	//	/**
	//	 * Browse the diff model from the leaf to the top to find the containing {@link DiffModel} instance.
	//	 * 
	//	 * @param diff
	//	 *            any {@link DiffElement}.
	//	 * @return the containing {@link DiffModel} instance, null if not found.
	//	 */
	//	private static DiffModel getContainerDiffModel(final DiffElement diff) {
	//		EObject container = diff.eContainer();
	//		while (container != null) {
	//			if (container instanceof DiffModel)
	//				return (DiffModel)container;
	//			container = container.eContainer();
	//		}
	//		return null;
	//	}

	/**
	 * Merges a single DiffElement in the direction specified by <code>leftToRight</code>.
	 * <p>
	 * Will notify the list of its merge listeners before, and after the operation.
	 * </p>
	 * 
	 * @param element
	 *        {@link DiffElement} containing the information to merge.
	 * @param leftToRight
	 *        <code>True</code> if the changes must be applied from the left to the right model, <code>False</code> when they have to be applied the
	 *        other way around.
	 */
	//	public static void merge(final DiffElement element, final boolean leftToRight) {
	//		fireMergeOperationStart(element);
	//		doMerge(element, leftToRight);
	//		fireMergeOperationEnd(element);
	//	}

	public static Command getMergeCommand(final TransactionalEditingDomain domain, final DiffElement element, final boolean leftToRight) {
		CompoundCommand cmd = new CompoundCommand("MergeCommand");
		cmd.append(new FireMergeOperationStartCommand(element, getMergeListeners()));
		cmd.append(getDoMergeCommand(domain, element, leftToRight));
		cmd.append(new FireMergeOperationEndCommand(element, getMergeListeners()));
		return cmd;
	}

	/**
	 * Applies the changes implied by a given {@link DiffElement} in the direction specified by <code>leftToRight</code>.
	 * <p>
	 * Will notify the list of its merge listeners before, and after the merge.
	 * </p>
	 * 
	 * @param element
	 *        {@link DiffElement} containing the information to merge.
	 * @param leftToRight
	 *        <code>True</code> if the changes must be applied from the left to the right model, <code>False</code> when they have to be applied the
	 *        other way around.
	 */
	//	protected static void doMerge(final DiffElement element, final boolean leftToRight) {
	//		fireMergeDiffStart(element);
	//		final IMerger merger;
	//		if(element instanceof ConflictingDiffElement)
	//			merger = MergeFactory.createMerger(element.getSubDiffElements().get(0));
	//		else
	//			merger = MergeFactory.createMerger(element);
	//		if(leftToRight && merger.canUndoInTarget()) {
	//			merger.undoInTarget();
	//		} else if(!leftToRight && merger.canApplyInOrigin()) {
	//			merger.applyInOrigin();
	//		}
	//		fireMergeDiffEnd(element);
	//	}

	private static Command getDoMergeCommand(final TransactionalEditingDomain domain, final DiffElement element, final boolean leftToRight) {
		final CompoundCommand cmd = new CompoundCommand("DoMergeCommand");
		cmd.append(new FireMergeDiffStartCommand(element, getMergeListeners()));
		final IMerger merger;
		if(element instanceof ConflictingDiffElement) {
			merger = MergeFactory.createMerger(element.getSubDiffElements().get(0));
		} else {
			merger = MergeFactory.createMerger(element);
		}
		if(merger instanceof ITransactionalMerger) {
			if(leftToRight && merger.canUndoInTarget()) {
				cmd.append(((ITransactionalMerger)merger).getUndoInTargetCommand(domain));
			} else if(!leftToRight && merger.canApplyInOrigin()) {
				cmd.append(((ITransactionalMerger)merger).getApplyInOriginCommand(domain));
			}
		} else {
			throw new UnsupportedOperationException(NLS.bind("I can't found the Papyrus Merger for {0}.", element));
		}

		cmd.append(new FireMergeDiffEndCommand(element, getMergeListeners()));
		return cmd;
	}


	//	/**
	//	 * Merges a list of DiffElements in the direction specified by <code>leftToRight</code>.
	//	 * <p>
	//	 * Will notify the list of its merge listeners before, and after the operation.
	//	 * </p>
	//	 * 
	//	 * @param elements
	//	 *        {@link DiffElement}s containing the information to merge.
	//	 * @param leftToRight
	//	 *        <code>True</code> if the changes must be applied from the left to the right model, <code>False</code> when they have to be applied the
	//	 *        other way around.
	//	 */
	//	public static void merge(final List<DiffElement> elements, final boolean leftToRight) {
	//		fireMergeOperationStart(elements);
	//		for(DiffElement element : new ArrayList<DiffElement>(elements))
	//			// we might remove the diff from the list before merging it
	//			// (eOpposite reference)
	//			if(element.eContainer() != null)
	//				doMerge(element, leftToRight);
	//		fireMergeOperationEnd(elements);
	//	}

	public static Command getMergeCommand(final TransactionalEditingDomain domain, final List<DiffElement> elements, final boolean leftToRight) {
		final CompoundCommand cmd = new CompoundCommand("MergeCommand");
		cmd.append(new FireMergeOperationStartCommand(elements, getMergeListeners()));
		for(DiffElement element : new ArrayList<DiffElement>(elements)) {
			// we might remove the diff from the list before merging it
			// (eOpposite reference)
			if(element.eContainer() != null) {
				cmd.append(getDoMergeCommand(domain, element, leftToRight));
			}
		}
		cmd.append(new FireMergeOperationEndCommand(elements, getMergeListeners()));
		return cmd;
	}

	/**
	 * removes a merge listener from the list of registered listeners. This will have no effect if the given
	 * listener is not registered for notifications on this service.
	 * 
	 * @param listener
	 *        New Listener to register for notifications.
	 */
	public static void removeMergeListener(final IMergeListener listener) {
		if(removeMergeListenerMethod == null) {
			Class<?>[] parameterTypes = new Class[1];
			parameterTypes[0] = IMergeListener.class;
			try {
				removeMergeListenerMethod = ReflectHelper.getMethod(MergeService.class, "removeMergeListener", parameterTypes);
			} catch (SecurityException e) {
				Activator.log.error(e);
			} catch (NoSuchMethodException e) {
				Activator.log.error(e);
			}
		}
		final Object[] parameters = new Object[1];
		parameters[0] = listener;
		try {
			removeMergeListenerMethod.invoke(null, parameters);
		} catch (IllegalArgumentException e) {
			Activator.log.error(e);
		} catch (IllegalAccessException e) {
			Activator.log.error(e);
		} catch (InvocationTargetException e) {
			Activator.log.error(e);
		}
	}


	@SuppressWarnings("unchecked")
	//we suppress this warning because the field is always a List of IMergeListener
	private static List<IMergeListener> getMergeListeners() {
		List<IMergeListener> listeners = null;
		Field myField = null;
		try {
			myField = MergeService.class.getDeclaredField("MERGE_LISTENERS");
		} catch (SecurityException e) {
			Activator.log.error(e);
		} catch (NoSuchFieldException e) {
			Activator.log.error(e);
		}
		myField.setAccessible(true);
		try {
			listeners = (List<IMergeListener>)myField.get(null);
		} catch (IllegalArgumentException e) {
			Activator.log.error(e);
		} catch (IllegalAccessException e) {
			Activator.log.error(e);
		}
		return listeners;
	}

}

Back to the top