blob: 66afb0566c99f33edbac074cb309036475594ec6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Christian W. Damus and others.
*
* 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:
* Christian W. Damus - initial API and implementation
*******************************************************************************/
package org.eclipse.papyrus.compare.uml2.internal.postprocessor;
import static org.eclipse.emf.compare.utils.MatchUtil.getMatchedObject;
import static org.eclipse.papyrus.compare.uml2.internal.postprocessor.PapyrusPostProcessor.IS_RESOURCE_REFACTORING_MOVE;
import static org.eclipse.papyrus.compare.uml2.internal.postprocessor.PapyrusPostProcessor.IS_STEREOTYPE_APPLICATION_RESOURCE_MOVE;
import static org.eclipse.papyrus.compare.uml2.internal.postprocessor.PapyrusResourceIndex.getResource;
import static org.eclipse.papyrus.compare.uml2.internal.postprocessor.PapyrusResourceIndex.index;
import static org.eclipse.papyrus.compare.uml2.internal.postprocessor.PapyrusResourceIndex.opposite;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceSource;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.MatchResource;
import org.eclipse.emf.compare.ResourceAttachmentChange;
import org.eclipse.emf.compare.merge.ResourceAttachmentChangeMerger;
import org.eclipse.emf.compare.merge.ResourceChangeAdapter;
import org.eclipse.emf.compare.uml2.internal.postprocessor.util.UMLCompareUtil;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.papyrus.compare.uml2.internal.UMLPapyrusComparePlugin;
import org.eclipse.uml2.uml.Element;
/**
* A specialized merger for the {@link ResourceAttachmentChange}s in resources that are refactored (renamed or
* moved) and which, therefore, are not actually logically moving those objects.
*
* @author Christian W. Damus
*/
public class ResourceRefactoringMerger extends ResourceAttachmentChangeMerger {
/**
* Initializes me.
*/
public ResourceRefactoringMerger() {
super();
}
@Override
public boolean isMergerFor(Diff target) {
return IS_STEREOTYPE_APPLICATION_RESOURCE_MOVE.apply(target)
|| IS_RESOURCE_REFACTORING_MOVE.apply(target);
}
@Override
protected Resource findOrCreateTargetResource(Match match, boolean rightToLeft) {
DifferenceSource targetSide = rightToLeft ? DifferenceSource.LEFT : DifferenceSource.RIGHT;
EObject movedObject = getMatchedObject(match, targetSide);
if (movedObject instanceof Element) {
// This is not the stereotype application case
return super.findOrCreateTargetResource(match, rightToLeft);
} else {
// We were moved to another resource. If our base element was, also, then
// we should be moved to its resource regardless
Element baseElement = UMLCompareUtil.getBaseElement(movedObject);
if (baseElement != null) {
return baseElement.eResource();
}
}
return super.findOrCreateTargetResource(match, rightToLeft);
}
@Override
protected void move(ResourceAttachmentChange diff, boolean rightToLeft) {
Match match = diff.getMatch();
Resource left = match.getLeft().eResource();
Resource right = match.getRight().eResource();
super.move(diff, rightToLeft);
if (IS_RESOURCE_REFACTORING_MOVE.apply(diff)) {
// We've moved the UML element. Bring along all stereotype applications
// and other related content and delete the originating resources
PapyrusResourceIndex index = index(match);
if (rightToLeft) {
MatchResource leftMatch = index.getMatch(left);
Resource newLeft = demandResource(leftMatch, DifferenceSource.LEFT);
newLeft.getContents().addAll(left.getContents());
markForDeletion(left);
for (Map.Entry<String, MatchResource> next : index.getGroup(leftMatch, DifferenceSource.LEFT)
.entrySet()) {
if (next.getValue() != leftMatch) {
left = next.getValue().getLeft();
newLeft = demandResource(next.getValue(), DifferenceSource.LEFT);
newLeft.getContents().addAll(left.getContents());
markForDeletion(left);
}
}
} else {
MatchResource rightMatch = index.getMatch(right);
Resource newRight = demandResource(rightMatch, DifferenceSource.RIGHT);
newRight.getContents().addAll(right.getContents());
markForDeletion(right);
for (Map.Entry<String, MatchResource> next : index
.getGroup(rightMatch, DifferenceSource.RIGHT).entrySet()) {
if (next.getValue() != rightMatch) {
right = next.getValue().getRight();
newRight = demandResource(next.getValue(), DifferenceSource.RIGHT);
newRight.getContents().addAll(right.getContents());
markForDeletion(right);
}
}
}
}
}
/**
* Add a {@code resource} the collection of resources to be deleted when the merge result is saved.
*
* @param resource
* a resource to be deleted on save of the merge
*/
protected void markForDeletion(Resource resource) {
ResourceChangeAdapter rca = (ResourceChangeAdapter)EcoreUtil.getExistingAdapter(resource,
ResourceChangeAdapter.class);
if (rca != null) {
try {
Field resourcesToDelete = rca.getClass().getDeclaredField("resourcesToDelete"); //$NON-NLS-1$
resourcesToDelete.setAccessible(true);
@SuppressWarnings("unchecked")
Collection<? super Resource> resourcesToDeleteValue = (Collection<? super Resource>)resourcesToDelete
.get(rca);
resourcesToDeleteValue.add(resource);
} catch (Exception e) {
UMLPapyrusComparePlugin.getDefault().getLog()
.log(new Status(IStatus.ERROR, UMLPapyrusComparePlugin.PLUGIN_ID,
"Failed to mark resource for deletion: " + resource.getURI(), //$NON-NLS-1$
e));
}
}
}
/**
* Obtains the new (refactored) resource on the given {@code side} of the comparison, based on the URI of
* the resource opposite to it in the {@code match}. The resulting resource is created if it does not yet
* exist in the resource set on this {@code side}.
*
* @param match
* a resource match
* @param side
* the side of the comparison in which to get the refactored resource
* @return the refactored resource
*/
protected Resource demandResource(MatchResource match, DifferenceSource side) {
ResourceSet rset = getResource(match, side).getResourceSet();
URI uri = getResource(match, opposite(side)).getURI();
Resource result = rset.getResource(uri, false);
if (result == null) {
result = rset.createResource(uri);
}
return result;
}
}