blob: dcd7554b7aff7cf7665a2132b4b2ee3e63d64742 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2013 Obeo.
* 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
*******************************************************************************/
package org.eclipse.emf.compare.match.eobject;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.match.eobject.internal.AccessBasedLRUCache;
import org.eclipse.emf.ecore.EObject;
/**
* This class is able to measure similarity between "URI like" strings, basically strings separated by "/".
* This is mainly intended to be used with EMF's fragments.
*
* @author <a href="mailto:cedric.brun@obeo.fr">Cedric Brun</a>
*/
public class URIDistance implements Function<EObject, Iterable<String>> {
/**
* The upper bound distance we can get using this function.
*/
private static final int MAX_DISTANCE = 10;
/**
* A computing cache for the locations.
*/
private Map<EObject, Iterable<String>> locationCache;
/**
* A computing cache for the uri fragments.
*/
private Map<EObject, String> fragmentsCache;
/**
* The function used to compute the fragment of an {@link EObject}.
*/
private Function<EObject, String> fragmentComputation;
/**
* Create a new {@link URIDistance}.
*/
public URIDistance() {
locationCache = new AccessBasedLRUCache<EObject, Iterable<String>>(1000, 1000, .75F);
fragmentsCache = new AccessBasedLRUCache<EObject, String>(1000, 1000, .75F);
fragmentComputation = new EUriFragmentFunction();
}
/**
* Set an optional comparison used to retrieve matches already computed. This will impact the way the uri
* is computed by making sure two matching objects will have the same URI.
*
* @param comparison
* the comparison to use to retrieve the matches.
*/
public void setComparison(Comparison comparison) {
// Unused by the default implementation
}
/**
* Return a metric result URI similarities. It compares 2 strings splitting those by "/" and return an int
* representing the level of similarity. 0 - they are exactly the same to 10 - they are completely
* different. "adding a fragment", "removing a fragment".
*
* @param a
* First of the two {@link EObject}s to compare.
* @param b
* Second of the two {@link EObject}s to compare.
* @return The number of changes to transform one uri to another one.
*/
public int proximity(EObject a, EObject b) {
Iterable<String> aPath = getOrComputeLocation(a);
Iterable<String> bPath = getOrComputeLocation(b);
return proximity(aPath, bPath);
}
/**
* Return a metric result URI similarities. It compares 2 lists of fragments and return an int
* representing the level of similarity. 0 - they are exactly the same to 10 - they are completely
* different. "adding a fragment", "removing a fragment".
*
* @param aPath
* First of the two list of {@link String}s to compare.
* @param bPath
* Second of the two list of {@link String}s to compare.
* @return The number of changes to transform one uri to another one.
*/
public int proximity(Iterable<String> aPath, Iterable<String> bPath) {
int aSize = 0;
int bSize = 0;
Iterator<String> itA = aPath.iterator();
Iterator<String> itB = bPath.iterator();
boolean areSame = true;
int commonSegments = 0;
int remainingASegments = 0;
int remainingBSegments = 0;
while (itA.hasNext() && itB.hasNext() && areSame) {
String a = itA.next();
String b = itB.next();
if (a.equals(b)) {
commonSegments++;
} else {
areSame = false;
}
aSize++;
bSize++;
}
if (commonSegments == 0) {
return MAX_DISTANCE;
}
remainingASegments = aSize + Iterators.size(itA) - commonSegments;
remainingBSegments = bSize + Iterators.size(itB) - commonSegments;
int nbSegmentsToGoFromAToB = remainingASegments + remainingBSegments;
return (nbSegmentsToGoFromAToB * 10) / (commonSegments * 2 + nbSegmentsToGoFromAToB);
}
/**
* {@inheritDoc}
*
* @see com.google.common.base.Function#apply(java.lang.Object)
*/
public Iterable<String> apply(EObject input) {
String result = ""; //$NON-NLS-1$
EObject container = input.eContainer();
if (container != null) {
result = retrieveFragment(input);
} else {
result = "0"; //$NON-NLS-1$
}
final List<String> resultList = Lists.newArrayList(result);
if (container != null) {
Iterables.addAll(resultList, getOrComputeLocation(container));
}
return resultList;
}
/**
* The method return the location of an EObject represented as a list of fragments.
*
* @param container
* any EObject.
* @return a list of fragments.
*/
private Iterable<String> getOrComputeLocation(EObject container) {
Iterable<String> result = locationCache.get(container);
if (result == null) {
result = apply(container);
locationCache.put(container, result);
}
return result;
}
/**
* the containing fragment for a given {@link EObject}.
*
* @param input
* an EObject.
* @return a String representation of its containing fragment.
*/
public String retrieveFragment(EObject input) {
String result = fragmentsCache.get(input);
if (result == null) {
result = fragmentComputation.apply(input);
fragmentsCache.put(input, result);
}
return result;
}
/**
* return the maximum value we can get for this distance.
*
* @return the maximum value we can get for this distance.
*/
public int getUpperBoundDistance() {
return MAX_DISTANCE;
}
}