blob: 290e950747b9eb82f8e0000f3b7ea5a4a574e96b [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2022 CEA LIST.
*
* 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:
* CEA LIST - Initial API and implementation
*****************************************************************************/
package org.eclipse.emf.compare.uml2.cdo;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.cdo.CDOElement;
import org.eclipse.emf.cdo.compare.CDOCompare.CDOMatchEngine;
import org.eclipse.emf.cdo.compare.CDOCompare.CDOMatcher;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.CompareFactory;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.match.IComparisonFactory;
import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
import org.eclipse.emf.compare.scope.IComparisonScope;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.UMLPackage;
/**
* Specialization of {@link CDOMatchEngine}.
* In practice this engine includes UML base Elements of StereotypeApplication objects in the
* CDO minimal Match tree. This allows the support of existing UML post processors such as StereotypedElementChangePostProcessor that make
* the assumption the match tree always contains the UML base elements associated to stereotype application changes.
*/
public class UMLCDOMatchEngine extends CDOMatchEngine {
public UMLCDOMatchEngine(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
super(matcher, comparisonFactory);
}
/**
*The addition of the UML base elements is triggered as an immediate post processing of the default CDO matching step.
*/
@Override
public Comparison match(IComparisonScope scope, Monitor monitor) {
Comparison result = super.match(scope, monitor);
matchUMLBaseElements(scope, result, monitor);
return result;
}
/**
* This method walk through the existing Match tree and finds changes of stereotype applications whose base UML Element are not
* already included in the Match tree. A new match tree is created for those base elements only, and it is then merged with the
* original match tree. If new UML elements have been added in the Match tree, the {@link IComparisonScope} nsURI list
* is updated with UML nsURI to trigger related post processors.
* @param scope the original scope to update with UML nsURI if new elements have to be added.
* @param comparison the comparison to update
*/
public void matchUMLBaseElements(IComparisonScope scope, Comparison comparison, Monitor monitor) {
Set<EObject> leftElements = new HashSet<>();
Set<EObject> rightElements = new HashSet<>();
Set<EObject> originElements = new HashSet<>();
Comparison tempComparison = CompareFactory.eINSTANCE.createComparison();
CDOMatcher matcher = (CDOMatcher) getEObjectMatcher();
for (Match match : comparison.getMatches()) {
collectBaseElementAndParents(match, leftElements, rightElements, originElements);
for (Match subMatch : match.getAllSubmatches()) {
collectBaseElementAndParents(subMatch, leftElements, rightElements, originElements);
}
matcher.createMatches(tempComparison, leftElements.iterator(), rightElements.iterator(), originElements.iterator(), monitor);
}
if (!tempComparison.getMatches().isEmpty()) {
mergeMatches(comparison, tempComparison);
scope.getNsURIs().add(UMLPackage.eNS_URI);
}
}
/**
* This method takes the root of the new match trees and tries to find if a Match already exists for the parent
* of those root objects. If yes, the new match trees is added as a submatch of the parent Match, else the match tree
* is added a new match tree in the receiving comparison
*/
private void mergeMatches(Comparison receivingComparison, Comparison newComparison) {
Map<Match, List<Match>> matchToReceiver = new HashMap<>();
for (Match match : newComparison.getMatches()) {
EObject matchRootObject = match.getLeft();
if (matchRootObject == null) {
matchRootObject = match.getRight();
}
if (matchRootObject == null) {
matchRootObject = match.getOrigin();
}
if (matchRootObject != null) {
EObject parent = getParentOf(matchRootObject);
Match parentMatch = null;
if (parent != null) {
parentMatch = receivingComparison.getMatch(parent);
}
if (parentMatch != null) {
matchToReceiver.put(match, parentMatch.getSubmatches());
} else {
matchToReceiver.put(match, receivingComparison.getMatches());
}
}
}
for (Map.Entry<Match, List<Match>> entry : matchToReceiver.entrySet()) {
entry.getValue().add(entry.getKey());
}
}
protected EObject getParentOf(EObject eObject) {
return CDOElement.getParentOf(eObject);
}
private void collectBaseElementAndParents(Match match, Set<EObject> leftElements, Set<EObject> rightElements,
Set<EObject> originElements) {
Comparison comparison = match.getComparison();
addBaseElementAndParents(leftElements, comparison, match.getLeft());
addBaseElementAndParents(rightElements, comparison, match.getRight());
addBaseElementAndParents(originElements, comparison, match.getOrigin());
}
/**
* This method tries to find if the {@link EObject} application parameters is a stereotype application with a base_XXX eReference.
* If yes, the base element and all its parents which not already in the original Match tree are added in the receivingSetOfElements.
*/
private void addBaseElementAndParents(Set<EObject> receivingSetOfElements, Comparison comparison, EObject application) {
if (application != null && !(application instanceof Element)) {
@SuppressWarnings("restriction")
Element baseElement = org.eclipse.emf.compare.uml2.internal.postprocessor.util.UMLCompareUtil.getBaseElement(application);
if (baseElement != null && comparison.getMatch(baseElement) == null) {
receivingSetOfElements.add(baseElement);
EObject container = getParentOf(baseElement);
Match containerMatch = null;
// we also add in the element to match the owning tree of the base element
// until we find an existing match in the parent objects
while (container != null && containerMatch == null) {
containerMatch = comparison.getMatch(container);
// if there is not an existing match for the container,
// we have to have create a new one for it...
if (containerMatch == null) {
receivingSetOfElements.add(container);
}
container = getParentOf(container);
}
}
}
}
public static class Factory extends CDOMatchEngine.Factory {
/**
* Default factory used the EMF Compare extension point
*/
public Factory() {
}
protected CDOMatchEngine createMatchEngine(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
return new UMLCDOMatchEngine(matcher, comparisonFactory);
};
}
}