| /***************************************************************************** |
| * 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); |
| }; |
| } |
| } |