blob: 22ae847f62018e3e923a2cd9b8383a753e480b4c [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019 CEA LIST, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.gitlight.compare.internal;
import com.google.common.collect.LinkedHashMultimap;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceSource;
import org.eclipse.emf.compare.merge.ResourceChangeAdapter;
import org.eclipse.emf.compare.merge.ResourceChangeAdapter.IResourceChangeParticipant;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* This class materializes the post-comparison treatment. This treatment manages relations between notation
* changes and UML semantic changes when such changes should be linked. For instance, when the target of a
* connector is changed in the representation, it is also changed in the UML model. This treatment also
* installs a {@link IResourceChangeParticipant} on the comparison to make sure Papyrus resources are
* created/deleted together.
*/
public class PapyrusDiagramPostComparison implements Runnable {
/** The comparison. */
private final Comparison comparison;
/** The indexer. */
private final DiffIndexer indexer;
/**
* Constructor.
*
* @param comparison
* The comparison
*/
public PapyrusDiagramPostComparison(Comparison comparison) {
this.comparison = comparison;
indexer = new DiffIndexer();
}
/**
* Executes the treatment.
* <p>
* In a first step, a participant is registered on the comparison to deal with resources to delete.
* </p>
* <p>
* In a second step, implications are created. The algorithm takes 2 passes:
* <ol>
* <li>Index all {@link Diff}s that may be related with another {@link Diff}, using specific keys</li>
* <li>for each key in the index, create bi-directional implication links between the concerned diffs</li>
* </ol>
* This algorithm is linear, indexing in the first pass prevents fancy searches in the comparison model
* which may impede scalability.
* </p>
*/
public void run() {
registerResourceChangeParticipant();
IDiffHandler handler = getHandler();
for (Diff diff : comparison.getDifferences()) {
handler.handle(diff);
}
linkEquivalentDiffsWithRequiresRelations();
}
/**
* Register a {@link IResourceChangeParticipant} on the comparison for Papyrus resources.
*/
protected void registerResourceChangeParticipant() {
final Adapter adapter = EcoreUtil.getAdapter(comparison.eAdapters(), ResourceChangeAdapter.class);
if (adapter instanceof ResourceChangeAdapter) {
((ResourceChangeAdapter)adapter)
.addParticipant(new PapyrusResourceChangeParticipant((ResourceChangeAdapter)adapter));
}
}
/**
* Once all related differences are indexed, walk through the index and create the implication links
* between all concerned {@link Diff}s. The indexing of {@link Diff}s must have been done before calling
* this method.
*/
private void linkEquivalentDiffsWithRequiresRelations() {
for (Object key : indexer.getEquivalentDiffsKeySet()) {
Set<Diff> diffs = indexer.getEquivalentDiffs(key);
if (diffs.size() > 1) {
// Keep a map of equivalent diffs based on having the same source.
LinkedHashMultimap<DifferenceSource, Diff> sourceDiffs = LinkedHashMultimap.create();
try {
for (Diff diff : diffs) {
sourceDiffs.put(diff.getSource(), diff);
// Turn off notification delivery because we can end up populating very large
// relations; processing all those notifications is unnecessary at this point in time,
// because the reference involved is bi-directional, cross referencers don't need to
// monitor it.
diff.eSetDeliver(false);
}
for (DifferenceSource differenceSource : sourceDiffs.keySet()) {
Set<Diff> equivalentDiffs = sourceDiffs.get(differenceSource);
for (Diff diff : equivalentDiffs) {
List<Diff> requires = diff.getRequires();
requires.addAll(equivalentDiffs);
// Remove the circular requirement.
requires.remove(diff);
}
}
} finally {
for (Diff diff : diffs) {
// Turn notification delivery back on after updating the requires references.
diff.eSetDeliver(true);
}
}
}
}
}
/**
* Provide the {@link IDiffHandler} for the comparison used by this instance.
*
* @return An indexer that manages 3-way comparisons if the comparison is 3-way, or an indexer that
* manages 2-way comparisons otherwise.
*/
protected IDiffHandler getHandler() {
if (comparison.isThreeWay()) {
return new PapyrusDiagram3WayDiffHandler(indexer);
} else {
return new PapyrusDiagram2WayDiffHandler(indexer);
}
}
}