Bug 577073 URI change detector delegate extension

Change-Id: I011d20c8e752ef51bab6afd8c0c30ecfff908630
Signed-off-by: Andras Janko <andras.janko@incquerylabs.com>
Signed-off-by: Balazs Varnai <balazs_varnai@mentor.com>
diff --git a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegate.java b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegate.java
index 9a5403e..7c8bd8c 100644
--- a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegate.java
+++ b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegate.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2016 itemis and others.

+ * Copyright (c) 2016-2021 itemis, Siemens 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

@@ -9,6 +9,7 @@
  *

  * Contributors:

  *     itemis - Initial API and implementation

+ *     Siemens - [577073] URI change detector delegate extension

  *

  * </copyright>

  */

@@ -19,7 +20,10 @@
 

 import java.util.ArrayList;

 import java.util.Collections;

+import java.util.HashMap;

+import java.util.LinkedList;

 import java.util.List;

+import java.util.Map;

 

 import org.eclipse.core.resources.IFile;

 import org.eclipse.emf.common.notify.Notification;

@@ -28,6 +32,7 @@
 import org.eclipse.emf.ecore.EObject;

 import org.eclipse.emf.ecore.EReference;

 import org.eclipse.emf.ecore.EStructuralFeature;

+import org.eclipse.emf.ecore.resource.Resource;

 import org.eclipse.sphinx.emf.util.EcoreResourceUtil;

 import org.eclipse.sphinx.emf.workspace.internal.referentialintegrity.IntermittentRemoveTracker;

 

@@ -54,52 +59,106 @@
 

 	/*

 	 * @see

-	 * org.eclipse.sphinx.emf.workspace.referencialintegrity.IURIChangeDetectorDelegate#detectChangedURIs(org.eclipse

-	 * .emf .common.notify.Notification)

+	 * org.eclipse.sphinx.emf.workspace.referentialintegrity.IURIChangeDetectorDelegate#detectChangedURIs(java.util.

+	 * List)

+	 */

+	@Override

+	public Map<Resource, List<URIChangeNotification>> detectChangedURIs(List<Notification> notifications) {

+		if (notifications != null) {

+			Map<Resource, List<URIChangeNotification>> resourceToUriChangeNotification = new HashMap<>(notifications.size());

+

+			for (Notification notification : notifications) {

+				Object notifierObj = notification.getNotifier();

+				if (notifierObj instanceof EObject) {

+					EObject notifier = (EObject) notifierObj;

+					Resource resource = notifier.eResource();

+

+					List<URIChangeNotification> uriNotifications = handleNotification(notification, notifier);

+

+					if (uriNotifications != null && !uriNotifications.isEmpty()) {

+						if (resourceToUriChangeNotification.get(resource) == null) {

+							resourceToUriChangeNotification.put(resource, uriNotifications);

+						} else {

+							resourceToUriChangeNotification.get(resource).addAll(uriNotifications);

+						}

+					}

+				}

+			}

+			return resourceToUriChangeNotification;

+		}

+		return Collections.emptyMap();

+	}

+

+	/*

+	 * @see

+	 * org.eclipse.sphinx.emf.workspace.referencialintegrity.IURIChangeDetectorDelegate#detectChangedURIs(org.eclipse.

+	 * emf.common.notify.Notification)

 	 */

 	@Override

 	public List<URIChangeNotification> detectChangedURIs(Notification notification) {

-		List<URIChangeNotification> notifications = new ArrayList<URIChangeNotification>();

-		if (notification.getNotifier() instanceof EObject) {

-			EObject eObject = (EObject) notification.getNotifier();

-			EStructuralFeature feature = (EStructuralFeature) notification.getFeature();

-

-			// Detect object URI changes due to modifications that affect the URIs of the modified object and its

-			// contents

-			if (affectsURIFragmentSegmentOfChangedObject(notification)) {

-				URI newURI = EcoreResourceUtil.getURI(eObject);

-				URI oldURI = replaceLastFragmentSegment(newURI, notification.getNewStringValue(), notification.getOldStringValue());

-				if (oldURI != null && !oldURI.equals(newURI)) {

-					addURIChangeNotification(notifications, eObject, oldURI, newURI);

-				}

-			}

-

-			// Detect object URI changes due to contents being removed and added back elsewhere

-			else if (feature instanceof EReference && ((EReference) feature).isContainment()) {

-				removedContentsTracker.clearObsoleteEntries();

-

-				if (Notification.REMOVE == notification.getEventType()) {

-					URI containerURI = EcoreResourceUtil.getURI(eObject);

-					handleRemovedContent(eObject, containerURI, feature, (EObject) notification.getOldValue());

-				} else if (Notification.ADD == notification.getEventType()) {

-					handleAddedContent(notifications, (EObject) notification.getNewValue());

-				} else if (Notification.REMOVE_MANY == notification.getEventType()) {

-					URI containerURI = EcoreResourceUtil.getURI(eObject);

-					@SuppressWarnings("unchecked")

-					List<EObject> oldValues = (List<EObject>) notification.getOldValue();

-					for (EObject oldValue : oldValues) {

-						handleRemovedContent(eObject, containerURI, feature, oldValue);

-					}

-				} else if (Notification.ADD_MANY == notification.getEventType()) {

-					@SuppressWarnings("unchecked")

-					List<EObject> newValues = (List<EObject>) notification.getNewValue();

-					for (EObject newValue : newValues) {

-						handleAddedContent(notifications, newValue);

-					}

-				}

+		if (notification != null) {

+			Object notifierObj = notification.getNotifier();

+			if (notifierObj instanceof EObject) {

+				return handleNotification(notification, (EObject) notifierObj);

 			}

 		}

-		return notifications;

+		return Collections.emptyList();

+	}

+

+	protected List<URIChangeNotification> handleNotification(Notification notification, EObject notifier) {

+		EStructuralFeature feature = (EStructuralFeature) notification.getFeature();

+

+		// Detect object URI changes due to modifications that affect the URIs of the modified object and its

+		// contents

+		if (affectsURIFragmentSegmentOfChangedObject(notification)) {

+			return handleURIFragmentSegmentChange(notification, notifier);

+		}

+		// Detect object URI changes due to contents being removed and added back elsewhere

+		else if (feature instanceof EReference && ((EReference) feature).isContainment()) {

+			return handleContainmentChange(notification, notifier, feature);

+		}

+

+		return Collections.emptyList();

+	}

+

+	protected List<URIChangeNotification> handleURIFragmentSegmentChange(Notification notification, EObject eObject) {

+		URI newURI = EcoreResourceUtil.getURI(eObject);

+		URI oldURI = replaceLastFragmentSegment(newURI, notification.getNewStringValue(), notification.getOldStringValue());

+		if (oldURI != null && !oldURI.equals(newURI)) {

+			return addURIChangeNotification(eObject, oldURI, newURI);

+		}

+		return Collections.emptyList();

+	}

+

+	protected List<URIChangeNotification> handleContainmentChange(Notification notification, EObject eObject, EStructuralFeature feature) {

+		removedContentsTracker.clearObsoleteEntries();

+

+		if (Notification.REMOVE == notification.getEventType()) {

+			URI containerURI = EcoreResourceUtil.getURI(eObject);

+			if (notification.getOldValue() instanceof EObject) {

+				handleRemovedContent(eObject, containerURI, feature, (EObject) notification.getOldValue());

+			}

+		} else if (Notification.ADD == notification.getEventType()) {

+			return handleAddedContent((EObject) notification.getNewValue());

+		} else if (Notification.REMOVE_MANY == notification.getEventType()) {

+			URI containerURI = EcoreResourceUtil.getURI(eObject);

+			List<?> oldValues = (List<?>) notification.getOldValue();

+			for (Object oldValue : oldValues) {

+				if (oldValue instanceof EObject) {

+					handleRemovedContent(eObject, containerURI, feature, (EObject) oldValue);

+				}

+			}

+		} else if (Notification.ADD_MANY == notification.getEventType()) {

+			List<?> newValues = (List<?>) notification.getNewValue();

+			List<URIChangeNotification> notifications = new ArrayList<>(newValues.size());

+			for (Object newValue : newValues) {

+				if (newValue instanceof EObject) {

+					notifications.addAll(handleAddedContent((EObject) newValue));

+				}

+			}

+			return notifications;

+		}

+		return Collections.emptyList();

 	}

 

 	protected void handleRemovedContent(EObject oldContainer, URI oldContainerURI, EStructuralFeature oldFeature, EObject oldContent) {

@@ -124,17 +183,20 @@
 		}

 	}

 

-	protected void handleAddedContent(List<URIChangeNotification> notifications, EObject newContent) {

+	protected List<URIChangeNotification> handleAddedContent(EObject newContent) {

 		URI oldContentURI = removedContentsTracker.get(newContent);

 		if (oldContentURI != null) {

 			URI newContentURI = EcoreResourceUtil.getURI(newContent);

 			if (!oldContentURI.equals(newContentURI)) {

-				addURIChangeNotification(notifications, newContent, oldContentURI, newContentURI);

+				return addURIChangeNotification(newContent, oldContentURI, newContentURI);

 			}

 		}

+		return Collections.emptyList();

 	}

 

-	protected void addURIChangeNotification(List<URIChangeNotification> notifications, EObject eObject, URI oldURI, URI newURI) {

+	protected List<URIChangeNotification> addURIChangeNotification(EObject eObject, URI oldURI, URI newURI) {

+		List<URIChangeNotification> notifications = new LinkedList<>();

+

 		// Add URI change notification for given EObject

 		notifications.add(new URIChangeNotification(eObject, oldURI));

 

@@ -149,6 +211,8 @@
 				notifications.add(new URIChangeNotification(contentObject, oldContentURI));

 			}

 		}

+

+		return notifications;

 	}

 

 	/*

diff --git a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/IURIChangeDetectorDelegate.java b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/IURIChangeDetectorDelegate.java
index 40fd57a..f6b3544 100644
--- a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/IURIChangeDetectorDelegate.java
+++ b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/IURIChangeDetectorDelegate.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2008-2010 See4sys and others.

+ * Copyright (c) 2008-2021 See4sys, Siemens 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

@@ -9,17 +9,20 @@
  *

  * Contributors:

  *     See4sys - Initial API and implementation

+ *     Siemens - [577073] URI change detector delegate extension

  *

  * </copyright>

  */

 package org.eclipse.sphinx.emf.workspace.referentialintegrity;

 

 import java.util.List;

+import java.util.Map;

 

 import org.eclipse.core.resources.IFile;

 import org.eclipse.emf.common.notify.Notification;

 import org.eclipse.emf.common.util.URI;

 import org.eclipse.emf.ecore.EObject;

+import org.eclipse.emf.ecore.resource.Resource;

 

 /**

  * Interface that determines URIChangeDetectorDelegate API.see also {@link URIChangeDetectorDelegateRegistry}

@@ -27,17 +30,33 @@
 public interface IURIChangeDetectorDelegate {

 

 	/**

-	 * Detects all the model changed {@link URI}s from a given resource set event {@link Notification}

-	 * 

+	 * Detects all the model changed {@link URI}s from the given resource set events {@link Notification}. This method

+	 * deprecates the {@link IURIChangeDetectorDelegate#detectChangedURIs(Notification)}.

+	 *

 	 * @param notification

 	 *            The notification from the resource set event to use for computing the changed {@link URI}s

-	 * @return A map containing new {@link EObject new EObject} , {@link URI old URI} pairs.

+	 * @return A map containing entries of {@link Resource} to new {@link EObject new EObject} and {@link URI old URI}

+	 *         pairs. Null return value is interpreted by the caller as this method is not implemented. Empty map return

+	 *         value shall be used to signal that there were no changed {@link URI}s.

 	 */

+	public Map<Resource, List<URIChangeNotification>> detectChangedURIs(List<Notification> notifications);

+

+	/**

+	 * Detects all the model changed {@link URI}s from a given resource set event {@link Notification}.

+	 *

+	 * @deprecated Use {@link IURIChangeDetectorDelegate#detectChangedURIs(List)} instead. However, if that method

+	 *             returns null value, this method is called to detect {@link URI} changes. This behavior is kept until

+	 *             this method is completely removed from the API.

+	 * @param notification

+	 *            The notification from the resource set event to use for computing the changed {@link URI}s

+	 * @return A list containing new {@link EObject new EObject} , {@link URI old URI} pairs.

+	 */

+	@Deprecated

 	public List<URIChangeNotification> detectChangedURIs(Notification notification);

 

 	/**

 	 * Detects all the model changed {@link URI}s from a given {@link IFile file} moved event .

-	 * 

+	 *

 	 * @param oldFile

 	 *            The {@link IFile file} before modification.

 	 * @param newFile

@@ -45,5 +64,4 @@
 	 * @return A map containing {@link EObject new EObject} , {@link URI old URI} pairs.

 	 */

 	public List<URIChangeNotification> detectChangedURIs(IFile oldFile, IFile newFile);

-

 }

diff --git a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetector.java b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetector.java
index 7f523c1..e169df4 100644
--- a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetector.java
+++ b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetector.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2008-2013 See4sys, BMW Car IT, itemis and others.

+ * Copyright (c) 2008-2021 See4sys, BMW Car IT, itemis, Siemens 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

@@ -11,12 +11,16 @@
  *     See4sys - Initial API and implementation

  *     BMW Car IT - Avoid usage of Object.finalize

  *     itemis - [409014] Listener URIChangeDetector registered for all transactional editing domains

+ *     Siemens - [577073] URI change detector delegate extension

  *

  * </copyright>

  */

 package org.eclipse.sphinx.emf.workspace.referentialintegrity;

 

+import java.util.ArrayList;

+import java.util.HashMap;

 import java.util.List;

+import java.util.Map;

 

 import org.eclipse.core.resources.IFile;

 import org.eclipse.core.resources.IResourceChangeEvent;

@@ -71,27 +75,52 @@
 	}

 

 	/*

-	 * @see

-	 * org.eclipse.emf.transaction.ResourceSetListener#resourceSetChanged(org.eclipse.emf.transaction.ResourceSetChangeEvent

-	 * )

+	 * @see org.eclipse.emf.transaction.ResourceSetListener#resourceSetChanged(org.eclipse.emf.transaction.

+	 * ResourceSetChangeEvent )

 	 */

 	@Override

 	public void resourceSetChanged(ResourceSetChangeEvent event) {

 		List<?> notifications = event.getNotifications();

+

+		// Split the notifications based on their delegate handler into smaller lists

+		Map<IURIChangeDetectorDelegate, List<Notification>> delegateToNotifications = new HashMap<>();

 		for (Object o : notifications) {

 			if (o instanceof Notification) {

 				Notification notification = (Notification) o;

 				if (notification.getNotifier() instanceof EObject) {

-					EObject newEObject = (EObject) notification.getNotifier();

-					Resource resource = newEObject.eResource();

+					Resource resource = ((EObject) notification.getNotifier()).eResource();

 					IURIChangeDetectorDelegate delegate = URIChangeDetectorDelegateRegistry.INSTANCE.getDetectorDelegate(resource);

+

 					if (delegate != null) {

+						delegateToNotifications.computeIfAbsent(delegate, d -> new ArrayList<Notification>()).add(notification);

+					}

+				}

+			}

+		}

+

+		for (IURIChangeDetectorDelegate delegate : delegateToNotifications.keySet()) {

+

+			List<Notification> delegateNotifications = delegateToNotifications.get(delegate);

+			Map<Resource, List<URIChangeNotification>> resourceToUriNotifications = delegate.detectChangedURIs(delegateNotifications);

+

+			if (resourceToUriNotifications == null) {

+				/** assume that the new API is not implemented, call the deprecated API */

+				for (Notification notification : delegateNotifications) {

+					if (notification.getNotifier() instanceof EObject) {

+						EObject newEObject = (EObject) notification.getNotifier();

+						Resource resource = newEObject.eResource();

+

+						@SuppressWarnings("deprecation")

 						List<URIChangeNotification> uriNotifications = delegate.detectChangedURIs(notification);

-						if (!uriNotifications.isEmpty()) {

+						if (uriNotifications != null && !uriNotifications.isEmpty()) {

 							fireURIChanged(createURIChangeEvent(resource, uriNotifications));

 						}

 					}

 				}

+			} else if (!resourceToUriNotifications.isEmpty()) {

+				for (Resource resource : resourceToUriNotifications.keySet()) {

+					fireURIChanged(createURIChangeEvent(resource, resourceToUriNotifications.get(resource)));

+				}

 			}

 		}

 	}

diff --git a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetectorDelegateRegistry.java b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetectorDelegateRegistry.java
index 7ce770b..599a795 100644
--- a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetectorDelegateRegistry.java
+++ b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeDetectorDelegateRegistry.java
@@ -1,23 +1,24 @@
 /**

  * <copyright>

- * 

- * Copyright (c) 2008-2010 See4sys and others.

+ *

+ * Copyright (c) 2008-2021 See4sys, Siemens 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: 

+ *

+ * Contributors:

  *     See4sys - Initial API and implementation

- * 

+ *     Siemens - [577073] URI change detector delegate extension

+ *

  * </copyright>

  */

 package org.eclipse.sphinx.emf.workspace.referentialintegrity;

 

-import java.util.HashMap;

 import java.util.HashSet;

 import java.util.Map;

 import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

 

 import org.eclipse.core.runtime.Assert;

 import org.eclipse.core.runtime.IConfigurationElement;

@@ -45,9 +46,9 @@
 	private static final String ATTR_OVERRIDE = "override"; //$NON-NLS-1$

 	private static final String ATTR_RESOURCE_TYPE = "resourceType";//$NON-NLS-1$

 

-	public static URIChangeDetectorDelegateRegistry INSTANCE = new URIChangeDetectorDelegateRegistry();

+	public static final URIChangeDetectorDelegateRegistry INSTANCE = new URIChangeDetectorDelegateRegistry();

 

-	private Map<Class<? extends Resource>, IURIChangeDetectorDelegate> fContributedURIChangeDetectorDelegates = new HashMap<Class<? extends Resource>, IURIChangeDetectorDelegate>();

+	private final Map<Class<? extends Resource>, IURIChangeDetectorDelegate> fContributedURIChangeDetectorDelegates = new ConcurrentHashMap<Class<? extends Resource>, IURIChangeDetectorDelegate>();

 

 	private URIChangeDetectorDelegateRegistry() {

 		readContributedURIChangeDetectors();

@@ -56,7 +57,7 @@
 	/**

 	 * Returns the contributed {@link IURIChangeDetectorDelegate URIChangeDetectorDelegate} for the provided

 	 * {@link Resource resource} if such one exists.

-	 * 

+	 *

 	 * @param resource

 	 *            the {@link Resource resource} to retrieve {@link IURIChangeDetectorDelegate URIChangeDetectorDelegate}

 	 *            for.

@@ -86,7 +87,7 @@
 	/**

 	 * Returns the contributed {@link IURIChangeDetectorDelegate URIChangeDetectorDelegate} for the provided

 	 * {@link Class resource Type} if such one exists.

-	 * 

+	 *

 	 * @param resourceType

 	 *            the {@link Class resource Type} to retrieve {@link IURIChangeDetectorDelegate

 	 *            URIChangeDetectorDelegate} for.

@@ -188,10 +189,8 @@
 					if (!overriddenIds.contains(overriddenURIChangeDetectorDelegateId)) {

 						overriddenIds.add(overriddenURIChangeDetectorDelegateId);

 					} else {

-						PlatformLogUtil.logAsWarning(

-								Activator.getPlugin(),

-								new RuntimeException(NLS.bind(Messages.warning_multipleOverridesForSameURIChangeDetectorDelegate,

-										overriddenURIChangeDetectorDelegateId)));

+						PlatformLogUtil.logAsWarning(Activator.getPlugin(), new RuntimeException(

+								NLS.bind(Messages.warning_multipleOverridesForSameURIChangeDetectorDelegate, overriddenURIChangeDetectorDelegateId)));

 					}

 				}

 			}

diff --git a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeListenerRegistry.java b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeListenerRegistry.java
index fc588ee..0d54b52 100644
--- a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeListenerRegistry.java
+++ b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/URIChangeListenerRegistry.java
@@ -1,22 +1,24 @@
 /**

  * <copyright>

- * 

- * Copyright (c) 2008-2012 See4sys, BMW Car IT and others.

+ *

+ * Copyright (c) 2008-2021 See4sys, BMW Car IT, Siemens 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: 

+ *

+ * Contributors:

  *     See4sys - Initial API and implementation

  *     BMW Car IT - Avoid usage of Object.finalize

- * 

+ *     Siemens - [577073] URI change detector delegate extension

+ *

  * </copyright>

  */

 package org.eclipse.sphinx.emf.workspace.referentialintegrity;

 

 import java.util.HashSet;

 import java.util.Set;

+import java.util.concurrent.ConcurrentHashMap;

 

 import org.eclipse.core.runtime.Assert;

 import org.eclipse.core.runtime.IConfigurationElement;

@@ -29,12 +31,12 @@
 import org.eclipse.sphinx.platform.util.PlatformLogUtil;

 

 /**

- * 

+ *

  */

 public class URIChangeListenerRegistry {

 	public static URIChangeListenerRegistry INSTANCE = new URIChangeListenerRegistry();

 

-	private Set<IURIChangeListener> fURIChangeListeners = new HashSet<IURIChangeListener>();

+	private Set<IURIChangeListener> fURIChangeListeners = ConcurrentHashMap.newKeySet();

 

 	private static final String EXTP_URI_CHANGE_LISTENERS = "org.eclipse.sphinx.emf.workspace.uriChangeListeners"; //$NON-NLS-1$

 

@@ -124,10 +126,8 @@
 					if (!overriddenIds.contains(overriddenURIChangeDetectorDelegateId)) {

 						overriddenIds.add(overriddenURIChangeDetectorDelegateId);

 					} else {

-						PlatformLogUtil.logAsWarning(

-								Activator.getPlugin(),

-								new RuntimeException(NLS.bind(Messages.warning_multipleOverridesForSameURIChangeListener,

-										overriddenURIChangeDetectorDelegateId)));

+						PlatformLogUtil.logAsWarning(Activator.getPlugin(), new RuntimeException(

+								NLS.bind(Messages.warning_multipleOverridesForSameURIChangeListener, overriddenURIChangeDetectorDelegateId)));

 					}

 				}

 			}

diff --git a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/XMIURIChangeDetectorDelegate.java b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/XMIURIChangeDetectorDelegate.java
index d4e0630..9003854 100644
--- a/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/XMIURIChangeDetectorDelegate.java
+++ b/plugins/org.eclipse.sphinx.emf.workspace/src/org/eclipse/sphinx/emf/workspace/referentialintegrity/XMIURIChangeDetectorDelegate.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2008-2013 See4sys, itemis and others.

+ * Copyright (c) 2008-2013 See4sys, itemis, Siemens 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 @@
  * Contributors:

  *     See4sys - Initial API and implementation

  *     itemis - [418005] Add support for model files with multiple root elements

+ *     Siemens - [577073] URI change detector delegate extension

  *

  * </copyright>

  */

@@ -17,7 +18,9 @@
 

 import java.util.ArrayList;

 import java.util.Collections;

+import java.util.HashMap;

 import java.util.List;

+import java.util.Map;

 

 import org.eclipse.core.resources.IFile;

 import org.eclipse.emf.common.notify.Notification;

@@ -39,20 +42,36 @@
 

 	/*

 	 * @see

+	 * org.eclipse.sphinx.emf.workspace.referentialintegrity.IURIChangeDetectorDelegate#detectChangedURIs(java.util.

+	 * List)

+	 */

+	@Override

+	public Map<Resource, List<URIChangeNotification>> detectChangedURIs(List<Notification> notifications) {

+

+		Map<Resource, List<URIChangeNotification>> resourceToUriChangeNotification = new HashMap<>();

+

+		for (Notification notification : notifications) {

+			Object notifier = notification.getNotifier();

+			if (notifier instanceof EObject) {

+				EObject eObject = (EObject) notifier;

+				Resource resource = ((EObject) notifier).eResource();

+

+				resourceToUriChangeNotification.computeIfAbsent(resource, k -> new ArrayList<>());

+				resourceToUriChangeNotification.get(resource).add(new URIChangeNotification(eObject, EcoreResourceUtil.getURI(eObject)));

+			}

+		}

+

+		return resourceToUriChangeNotification;

+	}

+

+	/*

+	 * @see

 	 * org.eclipse.sphinx.emf.workspace.referencialintegrity.IURIChangeDetectorDelegate#detectChangedURIs(org.eclipse

 	 * .emf .common.notify.Notification)

 	 */

 	@Override

 	public List<URIChangeNotification> detectChangedURIs(Notification notification) {

-		List<URIChangeNotification> uriChangeNotifications = new ArrayList<URIChangeNotification>();

-

-		Object notifier = notification.getNotifier();

-		if (notifier instanceof EObject) {

-			EObject eObject = (EObject) notifier;

-			uriChangeNotifications.add(new URIChangeNotification(eObject, EcoreResourceUtil.getURI(eObject)));

-		}

-

-		return uriChangeNotifications;

+		return Collections.emptyList(); // NOOP implementation for the deprecated API

 	}

 

 	/*

diff --git a/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegateTest.java b/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegateTest.java
index ac2eff9..ac2bbfc 100644
--- a/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegateTest.java
+++ b/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/AbstractHierarchicalFragmentURIChangeDetectorDelegateTest.java
@@ -1,7 +1,7 @@
 /**

  * <copyright>

  *

- * Copyright (c) 2016 itemis and others.

+ * Copyright (c) 2016-2021 itemis, Siemens 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

@@ -9,6 +9,7 @@
  *

  * Contributors:

  *     itemis - Initial API and implementation

+ *     Siemens - [577073] URI change detector delegate extension

  *

  * </copyright>

  */

@@ -17,12 +18,17 @@
 import static org.junit.Assert.assertEquals;

 import static org.junit.Assert.assertNotNull;

 import static org.junit.Assert.assertSame;

+import static org.junit.Assert.assertTrue;

 

-import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.HashMap;

 import java.util.List;

+import java.util.Map;

 

 import org.eclipse.emf.common.notify.Notification;

 import org.eclipse.emf.common.util.URI;

+import org.eclipse.emf.ecore.resource.Resource;

 import org.eclipse.emf.ecore.util.EContentAdapter;

 import org.eclipse.sphinx.emf.workspace.internal.referentialintegrity.IntermittentRemoveTracker;

 import org.eclipse.sphinx.emf.workspace.referentialintegrity.IURIChangeDetectorDelegate;

@@ -51,7 +57,7 @@
 	private static class TestURIChangeDetectorAdapter extends EContentAdapter {

 

 		private IURIChangeDetectorDelegate delegate;

-		private List<URIChangeNotification> changedURIs = new ArrayList<URIChangeNotification>();

+		private Map<Resource, List<URIChangeNotification>> changedURIs = new HashMap<>();

 

 		public TestURIChangeDetectorAdapter(IURIChangeDetectorDelegate delegate) {

 			this.delegate = delegate;

@@ -59,12 +65,25 @@
 

 		@Override

 		public void notifyChanged(Notification notification) {

-			changedURIs.addAll(delegate.detectChangedURIs(notification));

+			Map<Resource, List<URIChangeNotification>> changedURIsResult = delegate.detectChangedURIs(Collections.singletonList(notification));

+

+			for (Resource resource : changedURIsResult.keySet()) {

+				if (changedURIs.containsKey(resource)) {

+					changedURIs.get(resource).addAll(changedURIsResult.get(resource));

+				} else {

+					changedURIs.put(resource, changedURIsResult.get(resource));

+				}

+			}

+

 		}

 

-		public List<URIChangeNotification> getChangedURIs() {

+		public Map<Resource, List<URIChangeNotification>> getChangedURIs() {

 			return changedURIs;

 		}

+

+		public long getChangedURISize() {

+			return getChangedURIs().values().stream().map(Collection::size).reduce(0, Integer::sum);

+		}

 	}

 

 	private Hummingbird20TestModel model;

@@ -81,10 +100,12 @@
 	public void testDetectChangedURIsNotification_singleLeafObjectChanged() {

 		model.parameterValue111.setName("parameterValue111_changed");

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(1, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(1, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.parameterValue111, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue111"), uriChangeNotification.getOldURI());

@@ -97,17 +118,19 @@
 		model.parameterValue111.setName("parameterValue111_changed");

 		model.parameterValue112.setName("parameterValue112_changed");

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(2, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(2, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.parameterValue111, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue111"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue111_changed"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(1);

+		uriChangeNotification = changedURIs.get(model.resource1).get(1);

 		assertSame(model.parameterValue112, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue112"), uriChangeNotification.getOldURI());

@@ -119,31 +142,33 @@
 	public void testDetectChangedURIsNotification_containerObjectChanged() {

 		model.component11.setName("component11_changed");

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(4, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(4, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.component11, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component11_changed"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(1);

+		uriChangeNotification = changedURIs.get(model.resource1).get(1);

 		assertSame(model.component11ToComponent22Connection, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/component11ToComponent22Connection"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component11_changed/component11ToComponent22Connection"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(2);

+		uriChangeNotification = changedURIs.get(model.resource1).get(2);

 		assertSame(model.parameterValue111, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue111"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component11_changed/parameterValue111"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(3);

+		uriChangeNotification = changedURIs.get(model.resource1).get(3);

 		assertSame(model.parameterValue112, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue112"), uriChangeNotification.getOldURI());

@@ -156,10 +181,12 @@
 		model.component22.getParameterValues().remove(model.parameterValue221);

 		model.component11.getParameterValues().add(model.parameterValue221);

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(1, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(1, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.parameterValue221, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22/parameterValue221"), uriChangeNotification.getOldURI());

@@ -174,17 +201,19 @@
 		model.component22.getParameterValues().add(model.parameterValue111);

 		model.component22.getParameterValues().add(model.parameterValue112);

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(2, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(2, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource2));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource2).get(0);

 		assertSame(model.parameterValue111, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue111"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource2#//component22/parameterValue111"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(1);

+		uriChangeNotification = changedURIs.get(model.resource2).get(1);

 		assertSame(model.parameterValue112, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue112"), uriChangeNotification.getOldURI());

@@ -197,24 +226,26 @@
 		model.application2.getComponents().remove(model.component22);

 		model.application1.getComponents().add(model.component22);

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(3, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(3, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.component22, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component22"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(1);

+		uriChangeNotification = changedURIs.get(model.resource1).get(1);

 		assertSame(model.component22ToComponent11Connection, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22/component22ToComponent11Connection"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component22/component22ToComponent11Connection"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(2);

+		uriChangeNotification = changedURIs.get(model.resource1).get(2);

 		assertSame(model.parameterValue221, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22/parameterValue221"), uriChangeNotification.getOldURI());

@@ -226,10 +257,12 @@
 	public void testDetectChangedURIsNotification_singleLeafObjectMoved() {

 		model.component11.getParameterValues().add(model.parameterValue221);

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(1, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(1, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.parameterValue221, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22/parameterValue221"), uriChangeNotification.getOldURI());

@@ -242,17 +275,19 @@
 		model.component22.getParameterValues().add(model.parameterValue111);

 		model.component22.getParameterValues().add(model.parameterValue112);

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(2, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(2, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource2));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource2).get(0);

 		assertSame(model.parameterValue111, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue111"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource2#//component22/parameterValue111"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(1);

+		uriChangeNotification = changedURIs.get(model.resource2).get(1);

 		assertSame(model.parameterValue112, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource1#//component11/parameterValue112"), uriChangeNotification.getOldURI());

@@ -264,24 +299,26 @@
 	public void testDetectChangedURIsNotification_containerObjectMoved() {

 		model.application1.getComponents().add(model.component22);

 

-		List<URIChangeNotification> changedURIs = uriChangeDectector.getChangedURIs();

-		assertEquals(3, changedURIs.size());

+		Map<Resource, List<URIChangeNotification>> changedURIs = uriChangeDectector.getChangedURIs();

+		assertEquals(3, uriChangeDectector.getChangedURISize());

 

-		URIChangeNotification uriChangeNotification = changedURIs.get(0);

+		assertTrue(changedURIs.containsKey(model.resource1));

+		assertEquals(1, changedURIs.keySet().size());

+		URIChangeNotification uriChangeNotification = changedURIs.get(model.resource1).get(0);

 		assertSame(model.component22, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component22"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(1);

+		uriChangeNotification = changedURIs.get(model.resource1).get(1);

 		assertSame(model.component22ToComponent11Connection, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22/component22ToComponent11Connection"), uriChangeNotification.getOldURI());

 		assertNotNull(uriChangeNotification.getNewURI());

 		assertEquals(URI.createURI("resource1#//component22/component22ToComponent11Connection"), uriChangeNotification.getNewURI());

 

-		uriChangeNotification = changedURIs.get(2);

+		uriChangeNotification = changedURIs.get(model.resource1).get(2);

 		assertSame(model.parameterValue221, uriChangeNotification.getNewEObject());

 		assertNotNull(uriChangeNotification.getOldURI());

 		assertEquals(URI.createURI("resource2#//component22/parameterValue221"), uriChangeNotification.getOldURI());

diff --git a/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/UriChangeDetectorTest.java b/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/UriChangeDetectorTest.java
new file mode 100644
index 0000000..881caf3
--- /dev/null
+++ b/tests/org.eclipse.sphinx.tests.emf.workspace/src/org/eclipse/sphinx/tests/emf/workspace/referentialintegrity/UriChangeDetectorTest.java
@@ -0,0 +1,275 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 2021 Siemens 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:
+ *     Siemens - [577073] Initial API and implementation
+ *
+ * </copyright>
+ */
+package org.eclipse.sphinx.tests.emf.workspace.referentialintegrity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.InternalEObject;
+import org.eclipse.emf.ecore.impl.ENotificationImpl;
+import org.eclipse.emf.ecore.impl.EObjectImpl;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
+import org.eclipse.emf.transaction.ResourceSetChangeEvent;
+import org.eclipse.emf.transaction.impl.TransactionalCommandStackImpl;
+import org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.IURIChangeDetectorDelegate;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.IURIChangeListener;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.URIChangeDetector;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.URIChangeDetectorDelegateRegistry;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.URIChangeEvent;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.URIChangeListenerRegistry;
+import org.eclipse.sphinx.emf.workspace.referentialintegrity.URIChangeNotification;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class UriChangeDetectorTest {
+
+	private static class TestEOjbect extends EObjectImpl {
+	}
+
+	private static class TestResource1 extends ResourceImpl {
+	}
+
+	private static class TestResource2 extends ResourceImpl {
+	}
+
+	private static class AbstractTestURIChangeDetectorDelegate implements IURIChangeDetectorDelegate {
+
+		List<Notification> notifications = null;
+		Notification notification = null;
+		IFile oldFile = null;
+		IFile newFile = null;
+
+		Map<Resource, List<URIChangeNotification>> returnValue1 = null;
+		List<URIChangeNotification> returnValue2 = null;
+		List<URIChangeNotification> returnValue3 = null;
+
+		@Override
+		public Map<Resource, List<URIChangeNotification>> detectChangedURIs(List<Notification> notifications) {
+			this.notifications = notifications;
+			return returnValue1;
+		}
+
+		@Override
+		public List<URIChangeNotification> detectChangedURIs(Notification notification) {
+			this.notification = notification;
+			return returnValue2;
+		}
+
+		@Override
+		public List<URIChangeNotification> detectChangedURIs(IFile oldFile, IFile newFile) {
+			this.oldFile = oldFile;
+			this.newFile = newFile;
+			return returnValue3;
+		}
+	}
+
+	private static class TestURIChangeDetectorDelegate1 extends AbstractTestURIChangeDetectorDelegate {
+	}
+
+	private static class TestURIChangeDetectorDelegate2 extends AbstractTestURIChangeDetectorDelegate {
+	}
+
+	private static class TestURIChangeListener implements IURIChangeListener {
+		final List<URIChangeEvent> events = new ArrayList<>();
+
+		@Override
+		public void uriChanged(URIChangeEvent event) {
+			events.add(event);
+		}
+	}
+
+	private static final URIChangeDetector uriChangeDetector = new URIChangeDetector();
+	private static final TestURIChangeDetectorDelegate1 delegate1 = new TestURIChangeDetectorDelegate1();
+	private static final TestURIChangeDetectorDelegate2 delegate2 = new TestURIChangeDetectorDelegate2();
+	private static final TestURIChangeListener listener = new TestURIChangeListener();
+
+	@BeforeClass
+	public static void init() {
+		URIChangeDetectorDelegateRegistry.INSTANCE.addDelegate(TestResource1.class, delegate1);
+		URIChangeDetectorDelegateRegistry.INSTANCE.addDelegate(TestResource2.class, delegate2);
+		URIChangeListenerRegistry.INSTANCE.addListener(listener);
+	}
+
+	@AfterClass
+	public static void clean() {
+		URIChangeDetectorDelegateRegistry.INSTANCE.removeDelegate(delegate1);
+		URIChangeDetectorDelegateRegistry.INSTANCE.removeDelegate(delegate2);
+		URIChangeListenerRegistry.INSTANCE.removeListener(listener);
+	}
+
+	@Before
+	public void reset() {
+		delegate1.notifications = null;
+		delegate1.notification = null;
+		delegate1.newFile = null;
+		delegate1.oldFile = null;
+		delegate1.returnValue1 = null;
+		delegate1.returnValue2 = null;
+		delegate1.returnValue3 = null;
+		delegate2.notifications = null;
+		delegate2.notification = null;
+		delegate2.newFile = null;
+		delegate2.oldFile = null;
+		delegate2.returnValue1 = null;
+		delegate2.returnValue2 = null;
+		delegate2.returnValue3 = null;
+		listener.events.clear();
+	}
+
+	@Test
+	public void testResourceSetChangedSimple() {
+		TestResource1 testResource1 = new TestResource1();
+		testResource1.getContents().add(new TestEOjbect());
+
+		List<Notification> notifications = new ArrayList<>();
+		notifications.add(createNotification(testResource1));
+
+		delegate1.returnValue1 = createMockedDelegateReturnValue1(testResource1);
+
+		uriChangeDetector.resourceSetChanged(createResourceSetChangedEvent(notifications));
+
+		assertEquals(notifications, delegate1.notifications);
+		assertEquals(1, listener.events.size());
+
+		// deprecated API not called
+		assertNull(delegate1.notification);
+
+		assertSame(testResource1, listener.events.get(0).getSource());
+		assertSame(delegate1.returnValue1.get(testResource1), listener.events.get(0).getNotifications());
+	}
+
+	@Test
+	public void testResourceSetChangedMultipleResource() {
+		TestResource1 testResource1 = new TestResource1();
+		testResource1.getContents().add(new TestEOjbect());
+		TestResource2 testResource2 = new TestResource2();
+		testResource2.getContents().add(new TestEOjbect());
+
+		List<Notification> notifications = new ArrayList<>();
+		notifications.add(createNotification(testResource1));
+		notifications.add(createNotification(testResource2));
+
+		delegate1.returnValue1 = createMockedDelegateReturnValue1(testResource1);
+		delegate2.returnValue1 = createMockedDelegateReturnValue1(testResource2);
+
+		uriChangeDetector.resourceSetChanged(createResourceSetChangedEvent(notifications));
+
+		assertEquals(1, delegate1.notifications.size());
+		assertEquals(notifications.get(0), delegate1.notifications.get(0));
+
+		assertEquals(1, delegate2.notifications.size());
+		assertEquals(notifications.get(1), delegate2.notifications.get(0));
+
+		// deprecated API not called
+		assertNull(delegate1.notification);
+		assertNull(delegate2.notification);
+
+		assertEquals(2, listener.events.size());
+
+		for (URIChangeEvent event : listener.events) {
+			if (testResource1 == event.getSource()) {
+				assertSame(testResource1, event.getSource());
+				assertSame(delegate1.returnValue1.get(testResource1), event.getNotifications());
+			} else {
+				assertSame(testResource2, event.getSource());
+				assertSame(delegate2.returnValue1.get(testResource2), event.getNotifications());
+			}
+		}
+	}
+
+	@Test
+	public void testResourceSetChangedDeprecated() {
+		TestResource1 testResource1 = new TestResource1();
+		testResource1.getContents().add(new TestEOjbect());
+		TestResource2 testResource2 = new TestResource2();
+		testResource2.getContents().add(new TestEOjbect());
+
+		List<Notification> notifications = new ArrayList<>();
+		notifications.add(createNotification(testResource1));
+		notifications.add(createNotification(testResource2));
+
+		// Signal that the new API is not implemented
+		delegate1.returnValue1 = null;
+		delegate2.returnValue1 = null;
+
+		// deprecated API
+		delegate1.returnValue2 = createMockedDelegateReturnValue2();
+		delegate2.returnValue2 = createMockedDelegateReturnValue2();
+
+		uriChangeDetector.resourceSetChanged(createResourceSetChangedEvent(notifications));
+
+		// non deprecated API called
+		assertEquals(1, delegate1.notifications.size());
+		assertEquals(notifications.get(0), delegate1.notifications.get(0));
+
+		assertEquals(1, delegate2.notifications.size());
+		assertEquals(notifications.get(1), delegate2.notifications.get(0));
+
+		// deprecated API called
+		assertNotNull(delegate1.notification);
+		assertEquals(notifications.get(0), delegate1.notifications.get(0));
+
+		assertNotNull(delegate2.notification);
+		assertEquals(notifications.get(1), delegate2.notifications.get(0));
+
+		assertEquals(2, listener.events.size());
+
+		for (URIChangeEvent event : listener.events) {
+			if (testResource1 == event.getSource()) {
+				assertSame(testResource1, event.getSource());
+				assertSame(delegate1.returnValue2, event.getNotifications());
+			} else {
+				assertSame(testResource2, event.getSource());
+				assertSame(delegate2.returnValue2, event.getNotifications());
+			}
+		}
+	}
+
+	private Notification createNotification(ResourceImpl testResource) {
+		return new ENotificationImpl((InternalEObject) testResource.getContents().get(0), Notification.ADD, null, null, null);
+	}
+
+	private ResourceSetChangeEvent createResourceSetChangedEvent(List<Notification> notifications) {
+		return new ResourceSetChangeEvent(new TransactionalEditingDomainImpl(null, new TransactionalCommandStackImpl()), null, notifications);
+	}
+
+	private Map<Resource, List<URIChangeNotification>> createMockedDelegateReturnValue1(ResourceImpl testResource) {
+		Map<Resource, List<URIChangeNotification>> returnValue = new HashMap<>();
+		List<URIChangeNotification> uriNotifications = new ArrayList<URIChangeNotification>();
+		uriNotifications.add(new URIChangeNotification(new TestEOjbect(), URI.createURI("test"))); //$NON-NLS-1$
+		returnValue.put(testResource, uriNotifications);
+		return returnValue;
+	}
+
+	private List<URIChangeNotification> createMockedDelegateReturnValue2() {
+		List<URIChangeNotification> uriNotifications = new ArrayList<URIChangeNotification>();
+		uriNotifications.add(new URIChangeNotification(new TestEOjbect(), URI.createURI("test"))); //$NON-NLS-1$
+		return uriNotifications;
+	}
+}