[578422] Extend subscriber retrieval through extension points
- Manage providers in registry and expose registry through UI plugin
- Providers can map from comparison input data to Team subscriber
- Providers may be ranked to allow for easier execution control
- Use previous subscriber retrieval behavior in new subscriber provider
Bug: 578422
Change-Id: Ifd1dc9e7d5dbc19cdd2f9c1d6899ea1049475845
Signed-off-by: Martin Fleck <mfleck@eclipsesource.com>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml b/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml
index 9b26a00..64dab16 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml
+++ b/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml
@@ -23,6 +23,7 @@
<extension-point id="modelMinimizers" name="Logical Model Minimizers" schema="schema/modelMinimizers.exsd"/>
<extension-point id="modelDependencyProvider" name="Model Dependency Provider" schema="schema/modelDependencyProvider.exsd"/>
<extension-point id="mergeResolutionListener" name="Merge Resolution Listener" schema="schema/mergeResolutionListener.exsd"/>
+ <extension-point id="subscriberProvider" name="Subscriber Provider" schema="schema/subscriberProvider.exsd"/>
<extension
point="org.eclipse.compare.structureMergeViewers">
@@ -602,4 +603,11 @@
contextId="org.eclipse.compare.compareEditorScope">
</key>
</extension>
+ <extension
+ point="org.eclipse.emf.compare.ide.ui.subscriberProvider">
+ <provider
+ class="org.eclipse.emf.compare.ide.ui.subscriber.TeamSubscriberProvider"
+ ranking="0">
+ </provider>
+ </extension>
</plugin>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/schema/subscriberProvider.exsd b/plugins/org.eclipse.emf.compare.ide.ui/schema/subscriberProvider.exsd
new file mode 100644
index 0000000..04abfc3
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/schema/subscriberProvider.exsd
@@ -0,0 +1,122 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.compare.ide.ui" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.emf.compare.ide.ui" id="subscriberProvider" name="Subscriber Provider"/>
+ </appinfo>
+ <documentation>
+ The extension point can be used to support the mapping from a comparison input to a Team Subscriber that manages the mapping between local resources and a remote resources.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="provider" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="provider">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ The fully qualified name of a class that implements org.eclipse.emf.compare.ide.ui.subscriber.ISubscriberProvider
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.emf.compare.ide.ui.subscriber.ISubscriberProvider"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="ranking" type="string" use="default" value="0">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ 4.4.3
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ <extension
+ point="org.eclipse.emf.compare.ide.ui.subscriberProvider">
+ <provider
+ class="org.eclipse.emf.compare.ide.ui.subscriber.TeamSubscriberProvider"
+ ranking="0">
+ </provider>
+</extension>
+ </documentation>
+ </annotation>
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ See org.eclipse.emf.compare.ide.ui/plugin.xml for existing contributions.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2022 EclipseSource 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:
+ Martin Fleck - initial API and implementation
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java
index 504b606..3ef3114 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2017 Obeo and others.
+ * Copyright (c) 2012, 2022 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
@@ -10,6 +10,7 @@
* Stefan Dirix - Bug 456699
* Michael Borkowski - Bug 462863
* Martin Fleck - Bug 512562
+ * Martin Fleck - Bug 578422
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal;
@@ -33,7 +34,9 @@
import org.eclipse.emf.compare.ide.ui.internal.logical.view.registry.LogicalModelViewHandlerRegistryListener;
import org.eclipse.emf.compare.ide.ui.internal.mergeresolution.MergeResolutionListenerRegistry;
import org.eclipse.emf.compare.ide.ui.internal.mergeresolution.MergeResolutionListenerRegistryListener;
+import org.eclipse.emf.compare.ide.ui.internal.subscriber.SubscriberProviderRegistryListener;
import org.eclipse.emf.compare.ide.ui.logical.IModelMinimizer;
+import org.eclipse.emf.compare.ide.ui.subscriber.SubscriberProviderRegistry;
import org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
@@ -45,6 +48,7 @@
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
+@SuppressWarnings("restriction")
public class EMFCompareIDEUIPlugin extends AbstractUIPlugin {
/** The plugin ID. */
public static final String PLUGIN_ID = "org.eclipse.emf.compare.ide.ui"; //$NON-NLS-1$
@@ -67,6 +71,9 @@
/** Merge resolution listener extension point. */
private static final String MERGE_RESOLUTION_PPID = "mergeResolutionListener"; //$NON-NLS-1$
+ /** Subscriber provider extension point. */
+ private static final String SUBSCRIBER_PROVIDER_PPID = "subscriberProvider"; //$NON-NLS-1$
+
/** keep track of resources that should be freed when exiting. */
private static Map<String, Image> resourcesMapper = new HashMap<String, Image>();
@@ -100,6 +107,12 @@
/** Registry for the merge resolution listener extension point. */
private MergeResolutionListenerRegistry mergeResolutionListenerRegistry;
+ /** Registry for the subscriber provider extension point. */
+ private SubscriberProviderRegistry subscriberProviderRegistry;
+
+ /** Listener for the subscriber provider extension point. */
+ private SubscriberProviderRegistryListener subscriberProviderRegistryListener;
+
/** Default constructor. */
public EMFCompareIDEUIPlugin() {
// Empty constructor
@@ -120,6 +133,7 @@
logicalModelViewHandlerRegistry = new LogicalModelViewHandlerRegistry();
modelMinimizerRegistry = new ModelMinimizerRegistry();
mergeResolutionListenerRegistry = new MergeResolutionListenerRegistry();
+ subscriberProviderRegistry = new SubscriberProviderRegistry();
final IExtensionRegistry globalRegistry = Platform.getExtensionRegistry();
@@ -148,6 +162,11 @@
globalRegistry.addListener(modelMinimizerRegistryListener);
modelMinimizerRegistryListener.readRegistry(globalRegistry);
+ subscriberProviderRegistryListener = new SubscriberProviderRegistryListener(PLUGIN_ID,
+ SUBSCRIBER_PROVIDER_PPID, getLog(), subscriberProviderRegistry);
+ globalRegistry.addListener(subscriberProviderRegistryListener);
+ subscriberProviderRegistryListener.readRegistry(globalRegistry);
+
Platform.getAdapterManager().registerAdapters(new PropertySheetAdapterFactory(), CompareEditor.class);
}
@@ -165,6 +184,8 @@
modelResolverRegistry.clear();
globalRegistry.removeListener(modelDependencyProviderRegistryListener);
modelDependencyProviderRegistry.clear();
+ globalRegistry.removeListener(subscriberProviderRegistryListener);
+ subscriberProviderRegistry.clear();
plugin = null;
super.stop(context);
}
@@ -280,6 +301,15 @@
}
/**
+ * Returns the registry containing all known subscriber providers.
+ *
+ * @return The registry containing all known subscriber providers.
+ */
+ public SubscriberProviderRegistry getSubscriberProviderRegistry() {
+ return subscriberProviderRegistry;
+ }
+
+ /**
* Log an {@link Exception} in the {@link #getLog() current logger}.
*
* @param e
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java
index 41c75d5..934c0f0 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/ComparisonScopeBuilder.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2017 Obeo and others.
+ * Copyright (c) 2013, 2022 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
@@ -10,6 +10,7 @@
* Stefan Dirix - bug 466607
* Philip Langer - add support for setting initial file URIs to scope
* Martin Fleck - bug 512562
+ * Martin Fleck - bug 578422
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical;
@@ -22,9 +23,6 @@
import com.google.common.base.Predicate;
import com.google.common.collect.Sets;
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
@@ -66,9 +64,6 @@
import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.team.core.subscribers.Subscriber;
-import org.eclipse.team.core.subscribers.SubscriberMergeContext;
-import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
-import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant;
/**
* This will be used by EMF Compare in order to construct its comparison scope given a "starting point".
@@ -188,31 +183,29 @@
}
}
- /**
- * Resolves and minimizes the logical model for the given three typed element as would be done by
- * {@link #build(ITypedElement, ITypedElement, ITypedElement, IProgressMonitor)}, but returns
- * directly the SynchronizationModel DTO instead of the actual IComparisonScope.
- * <p>
- * This internal API is only intended for use by the resource mapping mergers.
- * </p>
- *
- * @param left
- * The element that will be used as the starting point to resolve the left logical
- * model.
- * @param right
- * Element that will be used as the starting point to resolve the left logical model.
- * @param origin
- * The origin resource, starting point of the logical model we are to resolve as the
- * origin one. Can be <code>null</code>.
- * @param monitor
- * The monitor on which to report progress information to the user.
- * @return The newly created SynchronizationModel.
- * @throws InterruptedException
- * In case of user interruption.
- */
- /* package */SynchronizationModel buildSynchronizationModel(ITypedElement left,
- ITypedElement right, ITypedElement origin, IProgressMonitor monitor)
- throws InterruptedException {
+ /**
+ * Resolves and minimizes the logical model for the given three typed element as would be done by
+ * {@link #build(ITypedElement, ITypedElement, ITypedElement, IProgressMonitor)}, but returns directly the
+ * SynchronizationModel DTO instead of the actual IComparisonScope.
+ * <p>
+ * This internal API is only intended for use by the resource mapping mergers.
+ * </p>
+ *
+ * @param left
+ * The element that will be used as the starting point to resolve the left logical model.
+ * @param right
+ * Element that will be used as the starting point to resolve the left logical model.
+ * @param origin
+ * The origin resource, starting point of the logical model we are to resolve as the origin
+ * one. Can be <code>null</code>.
+ * @param monitor
+ * The monitor on which to report progress information to the user.
+ * @return The newly created SynchronizationModel.
+ * @throws InterruptedException
+ * In case of user interruption.
+ */
+ /* package */SynchronizationModel buildSynchronizationModel(ITypedElement left, ITypedElement right,
+ ITypedElement origin, IProgressMonitor monitor) throws InterruptedException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("buildSynchronizationModel - START"); //$NON-NLS-1$
}
@@ -254,8 +247,10 @@
*/
public static IComparisonScope create(ICompareContainer container, ITypedElement left,
ITypedElement right, ITypedElement origin, IProgressMonitor monitor) {
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
+ Subscriber subscriber = EMFCompareIDEUIPlugin.getDefault().getSubscriberProviderRegistry()
+ .getSubscriber(container, left, right, origin, subMonitor.split(10));
IStorageProviderAccessor storageAccessor = null;
- Subscriber subscriber = getSubscriber(container);
if (subscriber != null) {
storageAccessor = new SubscriberStorageAccessor(subscriber);
}
@@ -268,7 +263,7 @@
final ComparisonScopeBuilder scopeBuilder = new ComparisonScopeBuilder(resolver,
EMFCompareIDEUIPlugin.getDefault().getModelMinimizerRegistry().getCompoundMinimizer(),
storageAccessor);
- return scopeBuilder.build(left, right, origin, monitor);
+ return scopeBuilder.build(left, right, origin, subMonitor.split(90));
}
/**
@@ -299,53 +294,6 @@
}
/**
- * Team left us with absolutely no way to determine whether our supplied input is the result of a
- * synchronization or not.
- * <p>
- * In order to properly resolve the logical model of the resource currently being compared we need to know
- * what "other" resources were part of its logical model, and we need to know the revisions of these
- * resources we are to load. All of this has already been computed by Team, but it would not let us know.
- * This method uses discouraged means to get around this "black box" locking from Team.
- * </p>
- * <p>
- * The basic need here is to retrieve the Subscriber from this point. We have a lot of accessible
- * variables, the two most important being the CompareConfiguration and ICompareInput... I could find no
- * way around the privileged access to the private ModelCompareEditorInput.participant field. There does
- * not seem to be any adapter (or Platform.getAdapterManager().getAdapter(...)) that would allow for this,
- * so I'm taking the long way 'round.
- * </p>
- *
- * @return The subscriber used for this comparison if any could be found, <code>null</code> otherwise.
- */
- @SuppressWarnings("restriction")
- private static Subscriber getSubscriber(ICompareContainer container) {
- if (container instanceof org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput) {
- final org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput modelInput = (org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput)container;
- ISynchronizeParticipant participant = null;
- try {
- final Field field = org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput.class
- .getDeclaredField("participant"); //$NON-NLS-1$
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- field.setAccessible(true);
- return null;
- }
- });
- participant = (ISynchronizeParticipant)field.get(modelInput);
- } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
- // Swallow this, this private field was there at least from 3.5 to 4.3
- }
- if (participant instanceof ModelSynchronizeParticipant
- && ((ModelSynchronizeParticipant)participant)
- .getContext() instanceof SubscriberMergeContext) {
- return ((SubscriberMergeContext)((ModelSynchronizeParticipant)participant).getContext())
- .getSubscriber();
- }
- }
- return null;
- }
-
- /**
* Creates the synchronization model for the given three elements (left, right, and the common ancestor of
* the two). Since this comparison may concern either local or remote resources, all I/O operations should
* go through the given storage accessor.
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/subscriber/SubscriberProviderDescriptor.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/subscriber/SubscriberProviderDescriptor.java
new file mode 100644
index 0000000..e414c34
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/subscriber/SubscriberProviderDescriptor.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2022 EclipseSource 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:
+ * Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.subscriber;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.compare.ide.ui.dependency.IDependencyProvider;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
+import org.eclipse.emf.compare.ide.ui.subscriber.ISubscriberProvider;
+import org.eclipse.emf.compare.ide.ui.subscriber.SubscriberProviderRegistry;
+
+/**
+ * This class is used for information flow between {@link SubscriberProviderRegistryListener} and
+ * {@link SubscriberProviderRegistry} and managing the creation of {@link ISubscriberProvider} instances.
+ *
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ */
+public class SubscriberProviderDescriptor {
+
+ /** Underlying {@link IConfigurationElement} describing this subscriber provider. */
+ private final IConfigurationElement configurationElement;
+
+ /**
+ * Name of the configuration property that can be used to retrieve the qualified class name of this
+ * dependency provider.
+ */
+ private final String attributeClassName;
+
+ /** Don't log the same error multiple times. */
+ private boolean logOnce;
+
+ /**
+ * The cached provider demand created the first time {@link #getSubscriberProvider()} is called.
+ */
+ private ISubscriberProvider provider;
+
+ private int ranking;
+
+ /**
+ * Default constructor.
+ *
+ * @param attributeName
+ * The name of the configuration attribute responsible for the registered
+ * {@link IDependencyProvider}.
+ * @param configurationElement
+ * The {@link IConfigurationElement} containing all relevant extension information.
+ */
+ public SubscriberProviderDescriptor(String attributeName, IConfigurationElement configurationElement,
+ int ranking) {
+ this.attributeClassName = attributeName;
+ this.configurationElement = configurationElement;
+ this.ranking = ranking;
+ }
+
+ public int getRanking() {
+ return ranking;
+ }
+
+ /**
+ * Returns the {@link IDependencyProvider}.
+ *
+ * @return The newly created {@link IDependencyProvider}.
+ */
+ public ISubscriberProvider getSubscriberProvider() {
+ if (provider == null && !logOnce) {
+ try {
+ provider = (ISubscriberProvider)configurationElement
+ .createExecutableExtension(attributeClassName);
+ } catch (CoreException e) {
+ if (!logOnce) {
+ logOnce = true;
+ final String className = configurationElement.getAttribute(attributeClassName);
+ final String message = EMFCompareIDEUIMessages
+ .getString("SubscriberProviderRegistry.invalidSubscriber", className); //$NON-NLS-1$
+ final IStatus status = new Status(IStatus.ERROR,
+ configurationElement.getDeclaringExtension().getContributor().getName(), message,
+ e);
+ EMFCompareIDEUIPlugin.getDefault().getLog().log(status);
+ }
+ }
+ }
+ return provider;
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/subscriber/SubscriberProviderRegistryListener.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/subscriber/SubscriberProviderRegistryListener.java
new file mode 100644
index 0000000..350a2c2
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/subscriber/SubscriberProviderRegistryListener.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2022 EclipseSource 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:
+ * Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.internal.subscriber;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
+import org.eclipse.emf.compare.ide.ui.subscriber.SubscriberProviderRegistry;
+import org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener;
+
+/**
+ * This listener will react to changes against the subscriber provider extension point, allowing us to be in
+ * sync with plugin activation and deactivation.
+ *
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ */
+public class SubscriberProviderRegistryListener extends AbstractRegistryEventListener {
+
+ /**
+ * The name of the provider element.
+ */
+ private static final String PROVIDER_ELEMENT_NAME = "provider"; //$NON-NLS-1$
+
+ /**
+ * The name of the class attribute of the provider element.
+ */
+ private static final String ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$
+
+ /**
+ * The name of the class attribute of the provider element.
+ */
+ private static final String ATTRIBUTE_RANKING = "ranking"; //$NON-NLS-1$
+
+ /**
+ * The registry which will actually hold all information.
+ */
+ private final SubscriberProviderRegistry registry;
+
+ /**
+ * Initialize a registry event listener for our handlers.
+ *
+ * @param pluginID
+ * ID of the plugin contributing the extension point to monitor.
+ * @param extensionPointID
+ * Actual id of the extension point to monitor.
+ * @param log
+ * Log in which errors/warning should be logged.
+ * @param registry
+ * The actual store of handlers this registry will alter.
+ */
+ public SubscriberProviderRegistryListener(String pluginID, String extensionPointID, ILog log,
+ SubscriberProviderRegistry registry) {
+ super(pluginID, extensionPointID, log);
+ this.registry = registry;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean validateExtensionElement(IConfigurationElement element) {
+ if (PROVIDER_ELEMENT_NAME.equals(element.getName())) {
+ final String className = element.getAttribute(ATTRIBUTE_CLASS);
+ return className != null && className.trim().length() > 0;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean addedValid(IConfigurationElement element) {
+ final String className = element.getAttribute(ATTRIBUTE_CLASS);
+ final String rankingStr = element.getAttribute(ATTRIBUTE_RANKING);
+ int ranking = -1;
+ try {
+ ranking = Integer.parseInt(rankingStr);
+ } catch (NumberFormatException e) {
+ log(IStatus.ERROR, element, EMFCompareIDEUIMessages
+ .getString("ModelResolverRegistry.invalidRanking", className, rankingStr)); //$NON-NLS-1$
+ }
+
+ final SubscriberProviderDescriptor descriptor = new SubscriberProviderDescriptor(ATTRIBUTE_CLASS,
+ element, ranking);
+ registry.addProvider(className, descriptor);
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean removedValid(IConfigurationElement element) {
+ final String className = element.getAttribute(ATTRIBUTE_CLASS);
+ registry.removeProvider(className);
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/ISubscriberProvider.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/ISubscriberProvider.java
new file mode 100644
index 0000000..287ebef
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/ISubscriberProvider.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2022 EclipseSource 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:
+ * Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.subscriber;
+
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.subscribers.Subscriber;
+
+/**
+ * Contract for clients of the org.eclipse.emf.ecompare.ide.ui.subscriberProvider extension point.
+ *
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ * @since 4.4.3
+ */
+public interface ISubscriberProvider {
+ /**
+ * Returns the subscriber that provides the synchronization between local resources and remote resources
+ * based on the given comparison input.
+ *
+ * @param container
+ * The compare container input.
+ * @param left
+ * Left of the compared elements.
+ * @param right
+ * Right of the compared elements.
+ * @param origin
+ * Common ancestor of the <code>left</code> and <code>right</code> compared elements.
+ * @param monitor
+ * Monitor to report progress on.
+ * @return The subscriber used for the comparison of the container or <code>null</code> if no subscriber
+ * could be determined.
+ */
+ Subscriber getSubscriber(ICompareContainer container, ITypedElement left, ITypedElement right,
+ ITypedElement origin, IProgressMonitor monitor);
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/SubscriberProviderRegistry.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/SubscriberProviderRegistry.java
new file mode 100644
index 0000000..3545d78
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/SubscriberProviderRegistry.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2022 EclipseSource 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:
+ * Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.subscriber;
+
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.compare.ide.ui.internal.subscriber.SubscriberProviderDescriptor;
+import org.eclipse.team.core.subscribers.Subscriber;
+
+/**
+ * The registry managing the registered subscriber provider extension point information.
+ *
+ * @author Martin Fleck <mfleck@eclipsesource.com>
+ * @since 4.4.3
+ */
+public class SubscriberProviderRegistry {
+
+ /** Keeps track of the extensions providing subscriber providers. */
+ private final Map<String, SubscriberProviderDescriptor> registeredDescriptors;
+
+ /**
+ * Constructs and initialized this registry.
+ */
+ public SubscriberProviderRegistry() {
+ registeredDescriptors = new LinkedHashMap<>();
+ }
+
+ /**
+ * Adds the given {@link SubscriberProviderDescriptor} to this registry, using the given {@code className}
+ * as the identifier.
+ *
+ * @param className
+ * The identifier for the given {@link SubscriberProviderDescriptor}.
+ * @param descriptor
+ * The {@link SubscriberProviderDescriptor} which is to be added to this registry.
+ */
+ public void addProvider(String className, SubscriberProviderDescriptor descriptor) {
+ registeredDescriptors.put(className, descriptor);
+ }
+
+ /**
+ * Removes the {@link SubscriberProviderDescriptor} and its managed {@link ISubscriberProvider} identified
+ * by the given {@code className} from this registry.
+ *
+ * @param className
+ * Identifier of the provider we are to remove from this registry.
+ * @return The removed {@link SubscriberProviderDescriptor}, if any.
+ */
+ public SubscriberProviderDescriptor removeProvider(String className) {
+ return registeredDescriptors.remove(className);
+ }
+
+ /** Clears out all registered providers from this registry. */
+ public void clear() {
+ registeredDescriptors.clear();
+ }
+
+ /**
+ * Returns the subscriber that provides the synchronization between local resources and remote resources
+ * based on the given comparison input.
+ *
+ * @param container
+ * The compare container input.
+ * @param left
+ * Left of the compared elements.
+ * @param right
+ * Right of the compared elements.
+ * @param origin
+ * Common ancestor of the <code>left</code> and <code>right</code> compared elements.
+ * @param monitor
+ * Monitor to report progress on.
+ * @return The subscriber used for the comparison of the container or <code>null</code> if no subscriber
+ * could be determined.
+ */
+ public Subscriber getSubscriber(ICompareContainer container, ITypedElement left, ITypedElement right,
+ ITypedElement origin, IProgressMonitor monitor) {
+ List<SubscriberProviderDescriptor> rankedDescriptors = registeredDescriptors.values().stream()
+ .sorted(Comparator.comparingInt(SubscriberProviderDescriptor::getRanking).reversed())
+ .collect(Collectors.toList());
+ for (SubscriberProviderDescriptor descriptor : rankedDescriptors) {
+ ISubscriberProvider provider = descriptor.getSubscriberProvider();
+ if (provider != null) {
+ Subscriber subscriber = provider.getSubscriber(container, left, right, origin, monitor);
+ if (subscriber != null) {
+ return subscriber;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/TeamSubscriberProvider.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/TeamSubscriberProvider.java
new file mode 100644
index 0000000..c001238
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/subscriber/TeamSubscriberProvider.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2022 EclipseSource 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:
+ * Martin Fleck - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.ide.ui.subscriber;
+
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.eclipse.compare.ICompareContainer;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.subscribers.Subscriber;
+import org.eclipse.team.core.subscribers.SubscriberMergeContext;
+import org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput;
+import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant;
+
+@SuppressWarnings("restriction")
+public class TeamSubscriberProvider implements ISubscriberProvider {
+
+ public Subscriber getSubscriber(ICompareContainer container, ITypedElement left, ITypedElement right,
+ ITypedElement origin, IProgressMonitor monitor) {
+ if (container instanceof ModelCompareEditorInput) {
+ return getSubscriber((ModelCompareEditorInput)container);
+ }
+ return null;
+ }
+
+ /**
+ * Team left us with absolutely no way to determine whether our supplied input is the result of a
+ * synchronization or not.
+ * <p>
+ * In order to properly resolve the logical model of the resource currently being compared we need to know
+ * what "other" resources were part of its logical model, and we need to know the revisions of these
+ * resources we are to load. All of this has already been computed by Team, but it would not let us know.
+ * This method uses discouraged means to get around this "black box" locking from Team.
+ * </p>
+ * <p>
+ * The basic need here is to retrieve the Subscriber from this point. We have a lot of accessible
+ * variables, the two most important being the CompareConfiguration and ICompareInput... I could find no
+ * way around the privileged access to the private ModelCompareEditorInput.participant field. There does
+ * not seem to be any adapter (or Platform.getAdapterManager().getAdapter(...)) that would allow for this,
+ * so I'm taking the long way 'round.
+ * </p>
+ *
+ * @return The subscriber used for this comparison if any could be found, <code>null</code> otherwise.
+ */
+ private Subscriber getSubscriber(ModelCompareEditorInput input) {
+ ModelSynchronizeParticipant participant = getModelSynchronizeParticipant(input);
+ return participant != null && participant.getContext() instanceof SubscriberMergeContext
+ ? ((SubscriberMergeContext)participant.getContext()).getSubscriber()
+ : null;
+ }
+
+ private ModelSynchronizeParticipant getModelSynchronizeParticipant(ModelCompareEditorInput modelInput) {
+ try {
+ final Field field = ModelCompareEditorInput.class.getDeclaredField("participant"); //$NON-NLS-1$
+ AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
+ field.setAccessible(true);
+ return null;
+ });
+ Object participant = field.get(modelInput);
+ return participant instanceof ModelSynchronizeParticipant
+ ? (ModelSynchronizeParticipant)participant
+ : null;
+ } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
+ // Swallow this, this private field was there at least from 3.5 to 4.3
+ }
+ return null;
+ }
+
+}