PapyrusDiagramPostComparison performs poorly

It has a performance of O(n^3). It's better to set all getRequires() at
once, at avoid uniqueness testing for very large lists. It's also
important to turn off notification because each addAll otherwise also
has O(n^2) performance from merging all the inverse notifications.

Change-Id: Ifd24d5eb71dcd5c00cb40bfbb153fe1e4ec3525e
Signed-off-by: Philip Langer <planger@eclipsesource.com>
diff --git a/plugins/compare/bundles/org.eclipse.papyrus.compare.diagram/src/org/eclipse/papyrus/compare/diagram/internal/PapyrusDiagramPostComparison.java b/plugins/compare/bundles/org.eclipse.papyrus.compare.diagram/src/org/eclipse/papyrus/compare/diagram/internal/PapyrusDiagramPostComparison.java
index 0e92250..d54db11 100644
--- a/plugins/compare/bundles/org.eclipse.papyrus.compare.diagram/src/org/eclipse/papyrus/compare/diagram/internal/PapyrusDiagramPostComparison.java
+++ b/plugins/compare/bundles/org.eclipse.papyrus.compare.diagram/src/org/eclipse/papyrus/compare/diagram/internal/PapyrusDiagramPostComparison.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Obeo.
+ * Copyright (c) 2015, 2017 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
@@ -7,14 +7,19 @@
  * 
  * Contributors:
  *     Obeo - initial API and implementation
+ *     Philip Langer - performance improvements
  *******************************************************************************/
 package org.eclipse.papyrus.compare.diagram.internal;
 
+import com.google.common.collect.LinkedHashMultimap;
+
+import java.util.List;
 import java.util.Set;
 
 import org.eclipse.emf.common.notify.Adapter;
 import org.eclipse.emf.compare.Comparison;
 import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceSource;
 import org.eclipse.emf.compare.merge.ResourceChangeAdapter;
 import org.eclipse.emf.compare.merge.ResourceChangeAdapter.IResourceChangeParticipant;
 import org.eclipse.emf.ecore.util.EcoreUtil;
@@ -90,12 +95,32 @@
 		for (Object key : indexer.getEquivalentDiffsKeySet()) {
 			Set<Diff> diffs = indexer.getEquivalentDiffs(key);
 			if (diffs.size() > 1) {
-				for (Diff diff : diffs) {
-					for (Diff other : diffs) {
-						if (other != diff && other.getSource() == diff.getSource()) {
-							diff.getRequires().add(other);
+				// Keep a map of equivalent diffs based on having the same source.
+				LinkedHashMultimap<DifferenceSource, Diff> sourceDiffs = LinkedHashMultimap.create();
+				try {
+					for (Diff diff : diffs) {
+						sourceDiffs.put(diff.getSource(), diff);
+						// Turn off notification delivery because we can end up populating very large
+						// relations; processing all those notifications is unnecessary at this point in time,
+						// because the reference involved is bi-directional, cross referencers don't need to
+						// monitor it.
+						diff.eSetDeliver(false);
+					}
+
+					for (DifferenceSource differenceSource : sourceDiffs.keySet()) {
+						Set<Diff> equivalentDiffs = sourceDiffs.get(differenceSource);
+						for (Diff diff : equivalentDiffs) {
+							List<Diff> requires = diff.getRequires();
+							requires.addAll(equivalentDiffs);
+							// Remove the circular requirement.
+							requires.remove(diff);
 						}
 					}
+				} finally {
+					for (Diff diff : diffs) {
+						// Turn notification delivery back on after updating the requires references.
+						diff.eSetDeliver(true);
+					}
 				}
 			}
 		}