Last active
August 29, 2015 14:27
-
-
Save sdirix/cad7dc71267920cce087 to your computer and use it in GitHub Desktop.
RootIDMatchingStrategy Alpha Refactoring
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
* Copyright (c) 2012, 2015 Obeo 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: | |
* Obeo - initial API and implementation | |
* Stefan Dirix - only return unique matches | |
*******************************************************************************/ | |
package org.eclipse.emf.compare.match.resource; | |
import com.google.common.collect.Lists; | |
import com.google.common.collect.Sets; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.LinkedHashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.eclipse.emf.compare.CompareFactory; | |
import org.eclipse.emf.compare.MatchResource; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.emf.ecore.resource.Resource; | |
import org.eclipse.emf.ecore.util.EcoreUtil; | |
import org.eclipse.emf.ecore.xmi.XMIResource; | |
/** | |
* This implementation of a matching strategy checks for the IDs of the resources' roots, and consider that | |
* resources match if the identifiers of their roots do. | |
* | |
* @author <a href="mailto:[email protected]">Laurent Goubet</a> | |
*/ | |
public class RootIDMatchingStrategy implements IResourceMatchingStrategy { | |
/** | |
* Matches the given resources according to the IDs found in their roots. | |
* <p> | |
* When the root IDs of two resources intersect, they are considered as matching. This strategy will only | |
* return unique matches between all resources. | |
* </p> | |
* | |
* @param left | |
* Resources we are to match in the left. | |
* @param right | |
* Resources we are to match in the right. | |
* @param origin | |
* Resources we are to match in the origin. | |
* @return The list of unique mappings this strategy managed to determine. | |
*/ | |
public List<MatchResource> matchResources(Iterable<? extends Resource> left, | |
Iterable<? extends Resource> right, Iterable<? extends Resource> origin) { | |
final List<MatchResource> mappings = Lists.newArrayList(); | |
final List<Resource> leftCopy = Lists.newArrayList(left); | |
final List<Resource> rightCopy = Lists.newArrayList(right); | |
final List<Resource> originCopy = Lists.newArrayList(origin); | |
final Map<Resource, List<Resource>> leftRightMap = new LinkedHashMap<Resource, List<Resource>>(); | |
final Map<Resource, List<Resource>> leftOriginMap = new LinkedHashMap<Resource, List<Resource>>(); | |
final Map<Resource, List<Resource>> rightOriginMap = new LinkedHashMap<Resource, List<Resource>>(); | |
for (Resource leftResource : leftCopy) { | |
final List<Resource> matchingRights = findMatches(leftResource, rightCopy); | |
leftRightMap.put(leftResource, matchingRights); | |
final List<Resource> matchingOrigins = findMatches(leftResource, originCopy); | |
leftOriginMap.put(leftResource, matchingOrigins); | |
} | |
for (Resource rightResource : rightCopy) { | |
final List<Resource> matchingLefts = findMatches(rightResource, leftCopy); | |
leftRightMap.put(rightResource, matchingLefts); | |
final List<Resource> matchingOrigins = findMatches(rightResource, originCopy); | |
rightOriginMap.put(rightResource, matchingOrigins); | |
} | |
for (Resource originResource : originCopy) { | |
final List<Resource> matchingLefts = findMatches(originResource, leftCopy); | |
leftOriginMap.put(originResource, matchingLefts); | |
final List<Resource> matchingRights = findMatches(originResource, rightCopy); | |
rightOriginMap.put(originResource, matchingRights); | |
} | |
for (Resource leftResource : leftCopy) { | |
List<Resource> rightObjects = leftRightMap.get(leftResource); | |
Resource rightResource = null; | |
Resource originResource = null; | |
if (rightObjects.size() > 1) { | |
rightCopy.removeAll(rightObjects); | |
continue; | |
} | |
if (rightObjects.size() == 1) { | |
final Resource rightCandidate = rightObjects.get(0); | |
// check left | |
if (leftRightMap.get(rightCandidate).size() == 1) { | |
rightResource = rightCandidate; | |
} | |
} | |
// check origins | |
if (!originCopy.isEmpty()) { | |
Set<Resource> originObjects = Sets.newHashSet(); | |
originObjects.addAll(leftOriginMap.get(leftResource)); | |
if (rightResource != null) { | |
originObjects.addAll(rightOriginMap.get(rightResource)); | |
} | |
if (originObjects.size() > 1) { | |
rightCopy.remove(rightResource); | |
continue; | |
} | |
if (originObjects.size() == 1) { | |
final Resource originCandidate = originObjects.iterator().next(); | |
// check origin does not map to more | |
Set<Resource> mappedResources = Sets.newHashSet(); | |
mappedResources.addAll(leftOriginMap.get(originCandidate)); | |
mappedResources.addAll(rightOriginMap.get(originCandidate)); | |
mappedResources.add(leftResource); | |
mappedResources.add(rightResource); | |
if (mappedResources.size() > 2) { | |
rightCopy.remove(rightResource); | |
continue; | |
} else { | |
originResource = originCandidate; | |
} | |
} | |
} | |
if (rightResource != null || originResource != null) { | |
mappings.add(createMatchResource(leftResource, rightResource, originResource)); | |
rightCopy.remove(rightResource); | |
} | |
} | |
if (!originCopy.isEmpty()) { | |
for (Resource rightResource : rightCopy) { | |
Resource leftResource = null; | |
Resource originResource = null; | |
List<Resource> originObjects = rightOriginMap.get(rightResource); | |
if (originObjects.size() == 1) { | |
originResource = originObjects.get(0); | |
// check right side | |
if (rightOriginMap.get(originResource).size() > 1) { | |
continue; | |
} | |
// check left side | |
List<Resource> leftCandidates = leftOriginMap.get(originResource); | |
if (leftCandidates.size() > 1) { | |
continue; | |
} else if (leftCandidates.size() == 1) { | |
Resource leftCandidate = leftCandidates.get(0); | |
// check right and origin | |
if (leftOriginMap.get(leftCandidate).size() > 1) { | |
continue; | |
} | |
if (!leftRightMap.get(leftCandidate).isEmpty()) { | |
continue; | |
} | |
leftResource = leftCandidate; | |
} | |
mappings.add(createMatchResource(leftResource, rightResource, originResource)); | |
} | |
} | |
} | |
return mappings; | |
} | |
/** | |
* Returns the first two matches of <code>reference</code> in <code>candidates</code>. This implementation | |
* will consider two Resources to be "matches" if their roots have IDs, and these IDs intersect. | |
* <p> | |
* Subclasses may return more than two elements if considered useful. | |
* </p> | |
* | |
* @param reference | |
* The reference resource. | |
* @param candidates | |
* The list of potential candidates that may match <code>reference</code>. | |
* @return The first two matches of <code>reference</code> in <code>candidates</code>. Empty list if none. | |
* @since 3.3 | |
*/ | |
protected List<Resource> findMatches(Resource reference, Iterable<Resource> candidates) { | |
final Set<String> referenceIDs = getResourceIdentifiers(reference); | |
if (referenceIDs.isEmpty()) { | |
return Lists.newArrayList(); | |
} | |
final List<Resource> matches = new ArrayList<Resource>(2); | |
final Iterator<Resource> candidateIterator = candidates.iterator(); | |
// optimize for size 2 since we do not need more at the moment | |
while (candidateIterator.hasNext() && matches.size() < 2) { | |
final Resource candidate = candidateIterator.next(); | |
final Set<String> candidateIDs = getResourceIdentifiers(candidate); | |
if (!candidateIDs.isEmpty() && !Sets.intersection(candidateIDs, referenceIDs).isEmpty()) { | |
matches.add(candidate); | |
} | |
} | |
return matches; | |
} | |
/** | |
* Returns the first match of <code>reference</code> in <code>candidates</code>. This implementation will | |
* consider two Resources to be "matches" if their roots have IDs, and these IDs are the same. | |
* | |
* @param reference | |
* The reference resource. | |
* @param candidates | |
* The list of potential candidates that may match <code>reference</code>. | |
* @return The first match of <code>reference</code> in <code>candidates</code>. <code>null</code> if | |
* none. | |
* @deprecated use {@link RootIDMatchingStrategy#findMatches(Resource, Iterable)} instead. | |
*/ | |
@Deprecated | |
protected Resource findMatch(Resource reference, Iterable<Resource> candidates) { | |
final Set<String> referenceIDs = getResourceIdentifiers(reference); | |
if (referenceIDs.isEmpty()) { | |
return null; | |
} | |
Resource match = null; | |
final Iterator<Resource> candidateIterator = candidates.iterator(); | |
while (candidateIterator.hasNext() && match == null) { | |
final Resource candidate = candidateIterator.next(); | |
final Set<String> candidateIDs = getResourceIdentifiers(candidate); | |
if (!candidateIDs.isEmpty() && !Sets.intersection(candidateIDs, referenceIDs).isEmpty()) { | |
match = candidate; | |
} | |
} | |
return match; | |
} | |
/** | |
* Retrieves the set of identifiers for the given resource's root. | |
* | |
* @param resource | |
* The resource for which we need the identifiers. | |
* @return The identifiers (both XMI:ID and eAttribute ID) of the resource's roots, if any. May be empty | |
* if the resource has no roots or if they have no ID. | |
*/ | |
protected Set<String> getResourceIdentifiers(Resource resource) { | |
final Set<String> identifiers = Sets.newHashSet(); | |
for (EObject root : resource.getContents()) { | |
if (resource instanceof XMIResource) { | |
String resourceId = ((XMIResource)resource).getID(root); | |
if (resourceId != null) { | |
identifiers.add(resourceId); | |
} | |
} | |
String rootID = EcoreUtil.getID(root); | |
if (rootID != null) { | |
identifiers.add(rootID); | |
} | |
} | |
return identifiers; | |
} | |
/** | |
* Creates a {@link MatchResource} instance and sets all three resources of the mapping on it. | |
* | |
* @param left | |
* The left resource of this mapping. | |
* @param right | |
* The right resource of this mapping. | |
* @param origin | |
* The origin resource of this mapping. | |
* @return The create mapping. | |
*/ | |
protected MatchResource createMatchResource(Resource left, Resource right, Resource origin) { | |
final MatchResource match = CompareFactory.eINSTANCE.createMatchResource(); | |
match.setLeft(left); | |
match.setRight(right); | |
match.setOrigin(origin); | |
if (left != null && left.getURI() != null) { | |
match.setLeftURI(left.getURI().toString()); | |
} | |
if (right != null && right.getURI() != null) { | |
match.setRightURI(right.getURI().toString()); | |
} | |
if (origin != null && origin.getURI() != null) { | |
match.setOriginURI(origin.getURI().toString()); | |
} | |
return match; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment