1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 Obeo.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Obeo - initial API and implementation
10 *******************************************************************************/
11 package org
.eclipse
.emf
.compare
.domain
.impl
;
13 import com
.google
.common
.collect
.ImmutableCollection
;
14 import com
.google
.common
.collect
.ImmutableList
;
15 import com
.google
.common
.collect
.ImmutableSet
;
17 import java
.util
.ArrayList
;
18 import java
.util
.List
;
20 import org
.eclipse
.core
.runtime
.IStatus
;
21 import org
.eclipse
.core
.runtime
.Status
;
22 import org
.eclipse
.emf
.common
.command
.BasicCommandStack
;
23 import org
.eclipse
.emf
.common
.command
.Command
;
24 import org
.eclipse
.emf
.common
.command
.CommandStack
;
25 import org
.eclipse
.emf
.common
.notify
.Notifier
;
26 import org
.eclipse
.emf
.common
.util
.BasicMonitor
;
27 import org
.eclipse
.emf
.compare
.Comparison
;
28 import org
.eclipse
.emf
.compare
.Diff
;
29 import org
.eclipse
.emf
.compare
.command
.ICompareCommandStack
;
30 import org
.eclipse
.emf
.compare
.command
.ICompareCopyCommand
;
31 import org
.eclipse
.emf
.compare
.command
.impl
.CompareCommandStack
;
32 import org
.eclipse
.emf
.compare
.command
.impl
.DualCompareCommandStack
;
33 import org
.eclipse
.emf
.compare
.command
.impl
.MergeAllNonConflictingCommand
;
34 import org
.eclipse
.emf
.compare
.command
.impl
.MergeCommand
;
35 import org
.eclipse
.emf
.compare
.command
.impl
.TransactionalDualCompareCommandStack
;
36 import org
.eclipse
.emf
.compare
.domain
.ICompareEditingDomain
;
37 import org
.eclipse
.emf
.compare
.domain
.IMergeRunnable
;
38 import org
.eclipse
.emf
.compare
.internal
.domain
.IMergeAllNonConflictingRunnable
;
39 import org
.eclipse
.emf
.compare
.merge
.BatchMerger
;
40 import org
.eclipse
.emf
.compare
.merge
.IBatchMerger
;
41 import org
.eclipse
.emf
.compare
.merge
.IMerger
;
42 import org
.eclipse
.emf
.compare
.merge
.IMerger
.Registry
;
43 import org
.eclipse
.emf
.compare
.provider
.EMFCompareEditPlugin
;
44 import org
.eclipse
.emf
.ecore
.EObject
;
45 import org
.eclipse
.emf
.ecore
.change
.util
.ChangeRecorder
;
46 import org
.eclipse
.emf
.ecore
.resource
.Resource
;
47 import org
.eclipse
.emf
.ecore
.resource
.ResourceSet
;
48 import org
.eclipse
.emf
.edit
.domain
.AdapterFactoryEditingDomain
;
49 import org
.eclipse
.emf
.edit
.domain
.EditingDomain
;
50 import org
.eclipse
.emf
.edit
.provider
.IDisposable
;
51 import org
.eclipse
.emf
.transaction
.TransactionalEditingDomain
;
52 import org
.eclipse
.emf
.transaction
.impl
.AbstractTransactionalCommandStack
;
53 import org
.eclipse
.emf
.transaction
.util
.TransactionUtil
;
56 * Default implementation that use a change recorder in the background to record the changes made by executed
59 * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
61 public class EMFCompareEditingDomain
implements ICompareEditingDomain
, IDisposable
{
63 /** The change recorder instance. */
64 private final ChangeRecorder fChangeRecorder
;
66 /** The notifiers on which the change recorder will be installed. */
67 private final ImmutableCollection
<Notifier
> fNotifiers
;
69 /** The command stack on which the merge commands will be executed. */
70 private final ICompareCommandStack fCommandStack
;
72 /** List of domains we've created ourselves and should thus cleanup ourselves. */
73 private final List
<TransactionalEditingDomain
> disposableDomains
;
76 * Creates a new instance with the given notifiers to be listen to when something will be changed.
79 * the left root notifier of the comparison (i.e. the
80 * {@link org.eclipse.emf.compare.scope.IComparisonScope#getLeft()}
82 * the right root notifier of the comparison (i.e. the
83 * {@link org.eclipse.emf.compare.scope.IComparisonScope#getRight()}
85 * the ancestor root notifier of the comparison (i.e. the
86 * {@link org.eclipse.emf.compare.scope.IComparisonScope#getOrigin()}
88 * the command stack to be used to track execution of commands.
90 public EMFCompareEditingDomain(Notifier left
, Notifier right
, Notifier ancestor
,
91 ICompareCommandStack commandStack
) {
92 if (ancestor
== null) {
93 fNotifiers
= ImmutableList
.of(left
, right
);
95 fNotifiers
= ImmutableList
.of(left
, right
, ancestor
);
98 fCommandStack
= commandStack
;
100 fChangeRecorder
= new ChangeRecorder();
101 fChangeRecorder
.setResolveProxies(false);
102 disposableDomains
= new ArrayList
<TransactionalEditingDomain
>();
106 * Returns the resource set containing the given notifier, the given {@code notifier} if it is a
107 * {@link ResourceSet}, <code>null</code> otherwise.
110 * the notifier from which we have to look for a {@link ResourceSet}.
111 * @return the resource set containing the given notifier, the given {@code notifier} if it is a
112 * {@link ResourceSet}, <code>null</code> otherwise
114 private static ResourceSet
getResourceSet(Notifier notifier
) {
115 ResourceSet resourceSet
= null;
116 if (notifier
instanceof ResourceSet
) {
117 resourceSet
= (ResourceSet
)notifier
;
118 } else if (notifier
instanceof Resource
) {
119 resourceSet
= ((Resource
)notifier
).getResourceSet();
120 } else if (notifier
instanceof EObject
) {
121 Resource eResource
= ((EObject
)notifier
).eResource();
122 if (eResource
!= null) {
123 resourceSet
= eResource
.getResourceSet();
126 // impossible as of today
132 * Creates a new compare editing domain on the given notifier with an appropriate
133 * {@link ICompareCommandStack} set up on it.
136 * the left notifier. Should not be <code>null</code>.
138 * the right notifier. Should not be <code>null</code>.
140 * the ancestor notifier. May be <code>null</code>.
141 * @return a new compare editing domain on the given notifier.
143 public static ICompareEditingDomain
create(Notifier left
, Notifier right
, Notifier ancestor
) {
144 boolean hadLeftED
= getExistingEditingDomain(left
) != null;
145 boolean hadRightED
= getExistingEditingDomain(right
) != null;
147 EditingDomain leftED
= getOrCreateEditingDomain(left
);
148 EditingDomain rightED
= getOrCreateEditingDomain(right
);
150 final ICompareEditingDomain domain
;
151 if (leftED
!= null && rightED
!= null) {
152 CommandStack leftCommandStack
= leftED
.getCommandStack();
153 CommandStack rightCommandStack
= rightED
.getCommandStack();
154 ICompareCommandStack commandStack
;
155 if (leftCommandStack
instanceof AbstractTransactionalCommandStack
156 && rightCommandStack
instanceof AbstractTransactionalCommandStack
) {
157 commandStack
= new TransactionalDualCompareCommandStack(
158 (AbstractTransactionalCommandStack
)leftCommandStack
,
159 (AbstractTransactionalCommandStack
)rightCommandStack
);
160 } else if (leftCommandStack
instanceof BasicCommandStack
161 && rightCommandStack
instanceof BasicCommandStack
) {
162 commandStack
= new DualCompareCommandStack((BasicCommandStack
)leftCommandStack
,
163 (BasicCommandStack
)rightCommandStack
);
170 EMFCompareEditPlugin
.PLUGIN_ID
,
171 "Command stacks of the editing domain of " //$NON-NLS-1$
173 + " and " //$NON-NLS-1$
175 + " are not instances of BasicCommandStack, nor AbstractTransactionalCommandStack, therefore, they will not be used as backing command stacks for the current merge session.")); //$NON-NLS-1$
176 commandStack
= new CompareCommandStack(new BasicCommandStack());
178 domain
= new EMFCompareEditingDomain(left
, right
, ancestor
, commandStack
);
180 domain
= create(left
, right
, ancestor
, new BasicCommandStack());
183 if (!hadLeftED
&& leftED
instanceof TransactionalEditingDomain
) {
184 ((EMFCompareEditingDomain
)domain
).addDomainToDispose((TransactionalEditingDomain
)leftED
);
186 if (!hadRightED
&& rightED
instanceof TransactionalEditingDomain
) {
187 ((EMFCompareEditingDomain
)domain
).addDomainToDispose((TransactionalEditingDomain
)rightED
);
194 * Return an existing editing domain associated with the given {@link Notifier}.
197 * the notifier from which the editing domain has to be linked.
198 * @return an editing domain associated with the given {@link Notifier} if any.
200 private static EditingDomain
getExistingEditingDomain(Notifier notifier
) {
201 EditingDomain editingDomain
= TransactionUtil
.getEditingDomain(notifier
);
202 if (editingDomain
== null) {
203 editingDomain
= AdapterFactoryEditingDomain
.getEditingDomainFor(notifier
);
205 return editingDomain
;
209 * Returns an editing domain associated with the given {@link Notifier}. It will first look for a
210 * {@link TransactionalEditingDomain} then for an {@link AdapterFactoryEditingDomain}. It neither is found
211 * a new {@link TransactionalEditingDomain} is created.
214 * the notifier from which the editing domain has to be linked.
215 * @return an editing domain associated with the given {@link Notifier}
217 private static EditingDomain
getOrCreateEditingDomain(Notifier notifier
) {
218 EditingDomain editingDomain
= getExistingEditingDomain(notifier
);
219 if (editingDomain
== null) {
220 ResourceSet resourceSet
= getResourceSet(notifier
);
221 if (resourceSet
!= null) {
222 editingDomain
= TransactionalEditingDomain
.Factory
.INSTANCE
.createEditingDomain(resourceSet
);
226 return editingDomain
;
230 * Equivalent to {@code create(left, right, ancestor, commandStack, null)}.
233 * the left notifier. Should not be <code>null</code>.
235 * the right notifier. Should not be <code>null</code>.
237 * the ancestor notifier. May be <code>null</code>.
238 * @param commandStack
239 * a command stack to which merge command will be delegated to.
240 * @return a newly created compare editing domain.
242 public static ICompareEditingDomain
create(Notifier left
, Notifier right
, Notifier ancestor
,
243 CommandStack commandStack
) {
244 return create(left
, right
, ancestor
, commandStack
, null);
248 * Creates a new compare editing domain on the given notifier with an appropriate
249 * {@link ICompareCommandStack} set up on it.
252 * the left notifier. Should not be <code>null</code>.
254 * the right notifier. Should not be <code>null</code>.
256 * the ancestor notifier. May be <code>null</code>.
257 * @param leftCommandStack
258 * a command stack to which merge to left command will be delegated to.
259 * @param rightCommandStack
260 * a command stack to which merge to irght command will be delegated to.
261 * @return a newly created compare editing domain.
263 public static ICompareEditingDomain
create(Notifier left
, Notifier right
, Notifier ancestor
,
264 CommandStack leftCommandStack
, CommandStack rightCommandStack
) {
266 final ICompareCommandStack commandStack
;
268 if (leftCommandStack
== null && rightCommandStack
!= null) {
269 if (rightCommandStack
instanceof ICompareCommandStack
) {
270 commandStack
= (ICompareCommandStack
)rightCommandStack
;
272 commandStack
= new CompareCommandStack(rightCommandStack
);
274 } else if (leftCommandStack
!= null && rightCommandStack
== null) {
275 if (leftCommandStack
instanceof ICompareCommandStack
) {
276 commandStack
= (ICompareCommandStack
)leftCommandStack
;
278 commandStack
= new CompareCommandStack(leftCommandStack
);
280 } else if (leftCommandStack
instanceof BasicCommandStack
281 && rightCommandStack
instanceof BasicCommandStack
) {
282 commandStack
= new DualCompareCommandStack((BasicCommandStack
)leftCommandStack
,
283 (BasicCommandStack
)rightCommandStack
);
285 commandStack
= new CompareCommandStack(new BasicCommandStack());
288 return new EMFCompareEditingDomain(left
, right
, ancestor
, commandStack
);
294 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#dispose()
296 public void dispose() {
297 fChangeRecorder
.dispose();
298 if (fCommandStack
instanceof IDisposable
) {
299 ((IDisposable
)fCommandStack
).dispose();
301 for (TransactionalEditingDomain domain
: disposableDomains
) {
309 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#getCommandStack()
311 public ICompareCommandStack
getCommandStack() {
312 return fCommandStack
;
318 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#createCopyCommand(org.eclipse.emf.compare.Diff,
319 * boolean, org.eclipse.emf.compare.merge.IMerger.Registry)
322 public Command
createCopyCommand(List
<?
extends Diff
> differences
, boolean leftToRight
,
323 IMerger
.Registry mergerRegistry
) {
324 ImmutableSet
.Builder
<Notifier
> notifiersBuilder
= ImmutableSet
.builder();
325 for (Diff diff
: differences
) {
326 notifiersBuilder
.add(diff
.getMatch().getComparison());
328 ImmutableSet
<Notifier
> notifiers
= notifiersBuilder
.addAll(fNotifiers
).build();
330 IMergeRunnable runnable
= new IMergeRunnable() {
331 public void merge(List
<?
extends Diff
> diffs
, boolean lTR
, Registry registry
) {
332 final IBatchMerger merger
= new BatchMerger(registry
);
334 merger
.copyAllLeftToRight(diffs
, new BasicMonitor());
336 merger
.copyAllRightToLeft(diffs
, new BasicMonitor());
340 return new MergeCommand(fChangeRecorder
, notifiers
, differences
, leftToRight
, mergerRegistry
,
347 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#createCopyCommand(java.util.List, boolean,
348 * org.eclipse.emf.compare.merge.IMerger.Registry,
349 * org.eclipse.emf.compare.command.ICompareCopyCommand.IMergeRunnable)
351 public ICompareCopyCommand
createCopyCommand(List
<?
extends Diff
> differences
, boolean leftToRight
,
352 Registry mergerRegistry
, IMergeRunnable runnable
) {
353 ImmutableSet
.Builder
<Notifier
> notifiersBuilder
= ImmutableSet
.builder();
354 for (Diff diff
: differences
) {
355 notifiersBuilder
.add(diff
.getMatch().getComparison());
357 ImmutableSet
<Notifier
> notifiers
= notifiersBuilder
.addAll(fNotifiers
).build();
359 return new MergeCommand(fChangeRecorder
, notifiers
, differences
, leftToRight
, mergerRegistry
,
364 * Creates a command that will merge all non-conflicting differences in the given direction.
366 * A "non-conflicting" difference is any difference that is not in a real conflict with another <u>and</u>
367 * that does not, directly or indirectly, depend on the merge of a difference that is in conflict itself.
370 * Note that only the differences originating from the "source" side of the chosen merge direction will be
375 * The comparison which differences to merge.
377 * The direction in which we should merge the differences.
378 * @param mergerRegistry
379 * The registry to query for specific mergers for each difference.
381 * the runnable to execute for the actual merge operation.
382 * @return The copy command, ready for use.
385 public ICompareCopyCommand
createCopyAllNonConflictingCommand(Comparison comparison
, boolean leftToRight
,
386 IMerger
.Registry mergerRegistry
, IMergeAllNonConflictingRunnable runnable
) {
387 ImmutableSet
.Builder
<Notifier
> notifiersBuilder
= ImmutableSet
.builder();
388 notifiersBuilder
.add(comparison
);
389 ImmutableSet
<Notifier
> notifiers
= notifiersBuilder
.addAll(fNotifiers
).build();
390 return new MergeAllNonConflictingCommand(fChangeRecorder
, notifiers
, comparison
, leftToRight
,
391 mergerRegistry
, runnable
);
397 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#getChangeRecorder()
399 public ChangeRecorder
getChangeRecorder() {
400 return fChangeRecorder
;
404 * Sets up a {@link TransactionalEditingDomain} for disposal along with {@code this}.
407 * The domain that should be disposed when we are.
409 private void addDomainToDispose(TransactionalEditingDomain domain
) {
410 disposableDomains
.add(domain
);