/*******************************************************************************
* Copyright (c) 2013, 2016 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
* Martin Fleck - bug 483798
*******************************************************************************/
package org.eclipse.emf.compare.internal.adapterfactory;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.compare.internal.adapterfactory.context.ContextUtil;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory.Descriptor;
/**
* The default implementation of the {@link Descriptor.Registry}.
*
* @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
*/
public class RankedAdapterFactoryDescriptorRegistryImpl implements RankedAdapterFactoryDescriptor.Registry {
/** The delegate registry. */
private final ComposedAdapterFactory.Descriptor.Registry delegateRegistry;
/** The store of registered adapter factory descriptors. */
private final Multimap<Collection<?>, RankedAdapterFactoryDescriptor> emfCompareAdapterFactoryRegistry;
/** The context for which factories can be registered. */
private final Map<Object, Object> context;
/**
* Creates an instance.
*
* @param delegateRegistry
* <code>null</code> or a registration that should act as the delegate.
* @param adapterFactoryRegistryBackingMultimap
* Multimap backing all {@link RankedAdapterFactoryDescriptor} registered into EMF Compare.
*/
public RankedAdapterFactoryDescriptorRegistryImpl(
ComposedAdapterFactory.Descriptor.Registry delegateRegistry,
Multimap<Collection<?>, RankedAdapterFactoryDescriptor> adapterFactoryRegistryBackingMultimap) {
this(delegateRegistry, adapterFactoryRegistryBackingMultimap, Maps.newLinkedHashMap());
}
/**
* Creates an instance.
*
* @param delegateRegistry
* <code>null</code> or a registration that should act as the delegate.
* @param adapterFactoryRegistryBackingMultimap
* Multimap backing all {@link RankedAdapterFactoryDescriptor} registered into EMF Compare.
* @param context
* context for which factories can be registered. This context cannot be null but may be empty.
* @throws NullPointerException
* if <code>context</code> is null.
*/
public RankedAdapterFactoryDescriptorRegistryImpl(
ComposedAdapterFactory.Descriptor.Registry delegateRegistry,
Multimap<Collection<?>, RankedAdapterFactoryDescriptor> adapterFactoryRegistryBackingMultimap,
Map<Object, Object> context) {
this.delegateRegistry = delegateRegistry;
this.emfCompareAdapterFactoryRegistry = adapterFactoryRegistryBackingMultimap;
this.context = Collections.unmodifiableMap(context);
}
/**
* Returns the appropriate Descriptor for the given types. Returns uppermost the ranked adapter factory
* descriptor with the highest ranking. If no ranked adapter factory descriptor found, delegates to the
* delegate registry.
*
* @param types
* the given types.
* @return the appropriate Descriptor for the given types.
*/
public Descriptor getDescriptor(Collection<?> types) {
ComposedAdapterFactory.Descriptor ret = getRankedDescriptor(types);
if (ret == null) {
ret = delegatedGetDescriptor(types);
}
return ret;
}
/**
* Gets the {@link ComposedAdapterFactory.Descriptor} that handles the given types.
*
* @param types
* Types that the {@link ComposedAdapterFactory.Descriptor} should handle.
* @return {@link ComposedAdapterFactory.Descriptor}
*/
private RankedAdapterFactoryDescriptor getRankedDescriptor(Collection<?> types) {
List<String> stringTypes = new ArrayList<String>(types.size());
for (Object key : types) {
if (key instanceof EPackage) {
stringTypes.add(((EPackage)key).getNsURI());
} else if (key instanceof Package) {
stringTypes.add(((Package)key).getName());
} else if (key instanceof Class<?>) {
stringTypes.add(((Class<?>)key).getName());
}
}
return getHighestRankedDescriptor(stringTypes);
}
/**
* Gets the highest ranked {@link ComposedAdapterFactory.Descriptor} registered in
* emfCompareAdapterFactoryRegistry.
*
* @param stringTypes
* Types that the {@link ComposedAdapterFactory.Descriptor} should handle.
* @return {@link RankedAdapterFactoryDescriptor}
*/
private RankedAdapterFactoryDescriptor getHighestRankedDescriptor(List<String> stringTypes) {
RankedAdapterFactoryDescriptor result = null;
Iterator<? extends RankedAdapterFactoryDescriptor> descriptors = emfCompareAdapterFactoryRegistry
.get(stringTypes).iterator();
while (descriptors.hasNext()) {
RankedAdapterFactoryDescriptor descriptor = descriptors.next();
if (ContextUtil.apply(descriptor, context)) {
boolean firstMatch = result == null;
boolean higherRank = result != null && descriptor.getRanking() > result.getRanking();
boolean equalRankButContextTester = result != null
&& descriptor.getRanking() == result.getRanking() && result.getContextTester() == null
&& descriptor.getContextTester() != null;
if (firstMatch || higherRank || equalRankButContextTester) {
result = descriptor;
}
}
}
return result;
}
/**
* This is called when local lookup fails.
*
* @param types
* the given types.
* @return the appropriate Descriptor for the given types.
*/
protected Descriptor delegatedGetDescriptor(Collection<?> types) {
if (delegateRegistry != null) {
return delegateRegistry.getDescriptor(types);
}
return null;
}
/**
* {@inheritDoc}
*
* @see {org.eclipse.emf.compare.internal.adapterfactory.RankedAdapterFactoryDescriptor.Registry.Registry#
* getDescriptors()}
*/
public Set<RankedAdapterFactoryDescriptor> getDescriptors() {
return ImmutableSet.copyOf(emfCompareAdapterFactoryRegistry.values());
}
}