/** * <copyright> * * Copyright (c) 2010-2016 Thales Global Services S.A.S. * 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: * Thales Global Services S.A.S. - initial API and implementation * * </copyright> */ package org.eclipse.emf.diffmerge.impl.helpers; import java.util.Collection; import java.util.Collections; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.diffmerge.Messages; import org.eclipse.emf.diffmerge.api.IComparison; import org.eclipse.emf.diffmerge.api.IMapping; import org.eclipse.emf.diffmerge.api.IMatch; import org.eclipse.emf.diffmerge.api.IMergeSelector; import org.eclipse.emf.diffmerge.api.Role; import org.eclipse.emf.diffmerge.api.diff.IDifference; import org.eclipse.emf.diffmerge.api.diff.IMergeableDifference; import org.eclipse.emf.diffmerge.util.structures.FArrayList; /** * An operation which merges a given set of differences in a given direction. * @author Olivier Constant */ public class MergeOperation extends AbstractExpensiveOperation { /** The non-null comparison */ protected final IComparison _comparison; /** The optional merger, non-null iff isGlobal() */ protected final IMergeSelector _merger; /** The non-null set of differences to merge (relevant only if !isGlobal()) */ protected final Collection<? extends IDifference> _toMerge; /** The optional destination role (TARGET or REFERENCE), null iff isGlobal() */ protected final Role _destinationRole; /** Whether references of the elements added must be set */ protected final boolean _updateReferences; /** The non-null set of differences that have actually been merged (initially empty) */ protected final Collection<IDifference> _actuallyMerged; /** * Constructor for a selected subset of differences * @param comparison_p a non-null comparison * @param differences_p a non-null, potentially empty set of differences to merge * @param destination_p a role which is TARGET or REFERENCE * @param updateReferences_p whether references of the elements added must be set */ public MergeOperation(IComparison comparison_p, Collection<? extends IDifference> differences_p, Role destination_p, boolean updateReferences_p) { super(); _comparison = comparison_p; _toMerge = differences_p; _destinationRole = destination_p; _merger = null; _updateReferences = updateReferences_p; _actuallyMerged = new FArrayList<IDifference>(); } /** * Constructor for a global merger * @param comparison_p a non-null comparison * @param merger_p a non-null merger * @param updateReferences_p whether references of the elements added must be set */ public MergeOperation(IComparison comparison_p, IMergeSelector merger_p, boolean updateReferences_p) { super(); _comparison = comparison_p; _toMerge = Collections.emptySet(); _destinationRole = null; _merger = merger_p; _updateReferences = updateReferences_p; _actuallyMerged = new FArrayList<IDifference>(); } /** * Return the set of differences which have actually been merged * @return a non-null, potentially empty, unmodifiable collection */ public Collection<IDifference> getOutput() { return Collections.unmodifiableCollection(_actuallyMerged); } /** * @see org.eclipse.emf.diffmerge.util.IExpensiveOperation#getOperationName() */ public String getOperationName() { return Messages.MergeOperation_Name; } /** * @see org.eclipse.emf.diffmerge.impl.helpers.AbstractExpensiveOperation#getWorkAmount() */ @Override protected int getWorkAmount() { // 1 init, 1 for each diff, 1 for ref update if required int nbDiffs = isGlobal()? _comparison.getMapping().size(): _toMerge.size(); return 1 + nbDiffs + (_updateReferences? 1: 0); } /** * Return whether the merge operation is global (based on a merger) or local * (based on a predefined subset of the differences) */ protected boolean isGlobal() { return _merger != null; } /** * @see org.eclipse.emf.diffmerge.util.IExpensiveOperation#run() */ public IStatus run() { getMonitor().worked(1); IStatus result; if (isGlobal()) result = runOnComparison(); else result = runOnSet(); if (_updateReferences && result != null && result.isOK()) { checkProgress(); IMapping.Editable mapping = (IMapping.Editable)_comparison.getMapping(); if (_destinationRole != null) { mapping.completeReferences(_destinationRole); } else { mapping.completeReferences(Role.TARGET); mapping.completeReferences(Role.REFERENCE); } getMonitor().worked(1); } return result; } /** * Run the merge operation on the whole comparison with a merger * @return a non-null status */ protected IStatus runOnComparison() { for (IMatch match : _comparison.getMapping().getContents()) { for (IDifference difference : match.getAllDifferences()) { checkProgress(); Role mergeDirection = _merger.getMergeDirection(difference); if (mergeDirection != null && difference.canMergeTo(mergeDirection)) { try { Collection<IDifference> merged = ((IMergeableDifference)difference).mergeTo(mergeDirection); _actuallyMerged.addAll(merged); } catch (UnsupportedOperationException e) { // Required differences cannot be merged: proceed } } } getMonitor().worked(1); } return Status.OK_STATUS; } /** * Run the merge operation on a selected subset of differences * @return a non-null status */ protected IStatus runOnSet() { for (IDifference difference : _toMerge) { checkProgress(); try { if (difference instanceof IMergeableDifference) { Collection<IDifference> merged = ((IMergeableDifference)difference).mergeTo(_destinationRole); _actuallyMerged.addAll(merged); } } catch (UnsupportedOperationException e) { // Cannot merge this difference: proceed } getMonitor().worked(1); } return Status.OK_STATUS; } }