blob: fbfe3d76d79275c6120801ff0313498627003bb1 [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.merge;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Lists.newArrayList;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.EMFCompareMessages;
/**
* Mergers are used by EMF Compare to merge specific differences from one side to the other. A number of
* default mergers are provided by EMF Compare, but they can be sub-classed and extended by clients through
* the extension point "org.eclipse.emf.compare.ide.mergerExtension".
* <p>
* Clients can either implement the whole merger contract or extend {@link AbstractMerger} instead.
* </p>
*
* @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
* @since 3.0
*/
public interface IMerger {
/**
* Check if the merger is a good candidate to merge the given difference.
*
* @param target
* The given target difference.
* @return True if it is the good candidate, false otherwise.
*/
boolean isMergerFor(Diff target);
/**
* Returns the ranking of this merger.
*
* @return The ranking.
*/
int getRanking();
/**
* Set the ranking of this merger.
*
* @param parseInt
* The ranking.
*/
void setRanking(int parseInt);
/**
* Executes the copy from right to left.
*
* @param target
* The difference to handle.
* @param monitor
* Monitor.
*/
void copyRightToLeft(Diff target, Monitor monitor);
/**
* Executes the copy from left to right.
*
* @param target
* The difference to handle.
* @param monitor
* Monitor.
*/
void copyLeftToRight(Diff target, Monitor monitor);
/**
* Set the registry containing this merger.
*
* @param registry
* The merger registry.
*/
void setRegistry(Registry registry);
/**
* Get the registry.
*
* @return The registry.
*/
Registry getRegistry();
/**
* This will hold all registered mergers. Mergers can be registered manually in the registry, but they are
* usually registered through the "org.eclipse.emf.compare.ide.mergerExtension" extension point.
* <p>
* An instance of the registry is usually accessed through
* "EMFCompareIDEPlugin.getDefault().getMergerRegistry()". However, if you need an instance of the
* registry in a standalone environment, you should use "IMerger.RegistryImpl.createStandaloneInstance()"
* so that the default registrations are taken care of.
* </p>
*/
public interface Registry {
/**
* Returns the merger, for the given target, owning the highest ranking.
*
* @param target
* The given target difference.
* @return The found merger.
*/
IMerger getHighestRankingMerger(Diff target);
/**
* Returns the list of the candidate mergers for the given difference.
*
* @param target
* The given difference.
* @return The list of the found mergers.
*/
Collection<IMerger> getMergers(Diff target);
/**
* Adds a merger to the registry.
*
* @param merger
* The merger.
* @return The previously registered merger.
*/
IMerger add(IMerger merger);
/**
* Removes a merger from the registry, from its class name.
*
* @param className
* The class name.
* @return The previously registered merger.
*/
IMerger remove(String className);
/**
* Clear the registry.
*/
void clear();
}
/**
* A default implementation of an {@link IMerger.Registry}. This is the implementation EMF Compare will
* use through its GUI.
*/
public class RegistryImpl implements Registry {
/**
* Map which references the registered mergers per their class name.
*/
private final Map<String, IMerger> map;
/**
* Constructor.
*/
public RegistryImpl() {
map = new ConcurrentHashMap<String, IMerger>();
}
/**
* Returns a registry filled with the default mergers provided by EMF Compare; namely
* {@link AttributeChangeMerger}, {@link ReferenceChangeMerger} and
* {@link ResourceAttachmentChangeMerger}.
*
* @return A registry filled with the default mergers provided by EMF Compare.
*/
public static IMerger.Registry createStandaloneInstance() {
final IMerger.Registry registry = new RegistryImpl();
// We need our pseudo-conflict merger to have a slightly higher ranking than default.
final int defaultRanking = 10;
final int pseudoConflictRanking = 15;
final IMerger attributeMerger = new AttributeChangeMerger();
attributeMerger.setRanking(defaultRanking);
final IMerger referenceMerger = new ReferenceChangeMerger();
referenceMerger.setRanking(defaultRanking);
final IMerger resourceAttachmentMerger = new ResourceAttachmentChangeMerger();
resourceAttachmentMerger.setRanking(defaultRanking);
final IMerger pseudoConflictMerger = new PseudoConflictMerger();
pseudoConflictMerger.setRanking(pseudoConflictRanking);
registry.add(attributeMerger);
registry.add(referenceMerger);
registry.add(resourceAttachmentMerger);
return registry;
}
/**
* Returns the predicate to check if the current merger is a good candidate to handle the given target
* difference.
*
* @param target
* The given difference.
* @return The predicate.
*/
private static Predicate<IMerger> isMergerFor(final Diff target) {
return new Predicate<IMerger>() {
/**
* {@inheritDoc}
*
* @see com.google.common.base.Predicate#apply(java.lang.Object)
*/
public boolean apply(IMerger d) {
return d.isMergerFor(target);
}
};
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger.Registry#add(org.eclipse.emf.compare.merge.IMerger)
*/
public IMerger add(IMerger merger) {
Preconditions.checkNotNull(merger);
merger.setRegistry(this);
return map.put(merger.getClass().getName(), merger);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger.Registry#remove(java.lang.String)
*/
public IMerger remove(String className) {
IMerger previous = map.remove(className);
if (previous != null) {
previous.setRegistry(null);
}
return previous;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger.Registry#clear()
*/
public void clear() {
map.clear();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger.Registry#getHighestRankingMerger(org.eclipse.emf.compare.Diff)
*/
public IMerger getHighestRankingMerger(Diff target) {
Iterator<IMerger> mergers = getMergers(target).iterator();
IMerger ret = null;
if (mergers.hasNext()) {
IMerger highestRanking = mergers.next();
while (mergers.hasNext()) {
IMerger merger = mergers.next();
if (merger.getRanking() > highestRanking.getRanking()) {
highestRanking = merger;
}
}
ret = highestRanking;
}
if (ret == null) {
throw new IllegalStateException(EMFCompareMessages.getString("IMerger.MissingMerger", target //$NON-NLS-1$
.getClass().getSimpleName()));
}
return ret;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger.Registry#getMergers(org.eclipse.emf.compare.Diff)
*/
public Collection<IMerger> getMergers(Diff target) {
Iterable<IMerger> mergers = filter(map.values(), isMergerFor(target));
List<IMerger> ret = newArrayList();
for (IMerger merger : mergers) {
ret.add(merger);
}
return ret;
}
}
}