[529226] Integrate latest EMF inverse xref optimization

This backports in all custom Sirius cross-reference adapters the
latest version of the algorithmic improvement made in EMF in the
context of bug #471106. 

See
http://git.eclipse.org/c/emf/org.eclipse.emf.git/commit/?id=0448d995991f08fd5764f1efe134f3a5395593cc

Bug: 529226
Change-Id: I240e12c9381d1ef06ff9d1ef2f3207718ba29729
Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
diff --git a/plugins/org.eclipse.sirius.common/META-INF/MANIFEST.MF b/plugins/org.eclipse.sirius.common/META-INF/MANIFEST.MF
index e5f3fff..927536a 100644
--- a/plugins/org.eclipse.sirius.common/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.sirius.common/META-INF/MANIFEST.MF
@@ -19,11 +19,12 @@
  org.eclipse.sirius.common.tools.api.query;version="2.0.4",
  org.eclipse.sirius.common.tools.api.resource;version="2.1.0",
  org.eclipse.sirius.common.tools.api.util;version="3.1.0",
- org.eclipse.sirius.common.tools.internal.assist;x-internal:=true;version="2.0.4",
- org.eclipse.sirius.common.tools.internal.ecore;x-internal:=true;version="2.0.4",
- org.eclipse.sirius.common.tools.internal.editing;x-internal:=true;version="2.0.4",
- org.eclipse.sirius.common.tools.internal.interpreter;x-internal:=true;version="3.1.0",
- org.eclipse.sirius.common.tools.internal.resource;x-internal:=true;version="2.0.4"
+ org.eclipse.sirius.common.tools.internal.assist;version="2.0.4";x-internal:=true,
+ org.eclipse.sirius.common.tools.internal.ecore;version="2.0.4";x-internal:=true,
+ org.eclipse.sirius.common.tools.internal.editing;version="2.0.4";x-internal:=true,
+ org.eclipse.sirius.common.tools.internal.interpreter;version="3.1.0";x-internal:=true,
+ org.eclipse.sirius.common.tools.internal.resource;version="2.0.4";x-internal:=true,
+ org.eclipse.sirius.common.tools.internal.util;version="5.1.1";x-internal:=true
 Bundle-Activator: org.eclipse.sirius.common.tools.DslCommonPlugin$Implementation
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/ECrossReferenceAdapterWithUnproxyCapability.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/ECrossReferenceAdapterWithUnproxyCapability.java
index 4f00832..c29cc62 100644
--- a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/ECrossReferenceAdapterWithUnproxyCapability.java
+++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/ECrossReferenceAdapterWithUnproxyCapability.java
@@ -15,7 +15,6 @@
 import java.util.List;
 import java.util.Map;
 
-import org.eclipse.emf.common.util.BasicEList;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
@@ -23,21 +22,21 @@
 import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
 import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.sirius.common.tools.internal.util.FastInverseCrossReferencesList;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
 /**
- * {@link ECrossReferenceAdapter} that provides the capability to resolve all
- * proxy cross reference to a given resource.
+ * {@link ECrossReferenceAdapter} that provides the capability to resolve all proxy cross reference to a given resource.
  * 
  * @noextend This class is not intended to be subclassed by clients.
  * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
  */
 public class ECrossReferenceAdapterWithUnproxyCapability extends SiriusCrossReferenceAdapterImpl {
+
     /**
-     * InverseCrossReferencer to allow access to {@link #removeProxies(URI)} in
-     * '@link InternalCrossReferencer}.
+     * InverseCrossReferencer to allow access to {@link #removeProxies(URI)} in '@link InternalCrossReferencer}.
      * 
      * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
      */
@@ -50,10 +49,9 @@
         }
 
         /**
-         * Check if the proxyMap is null or not. Since change in
-         * ECrossReferenceAdapter (bugzilla 400891), the proxyMap is no longer
-         * used if the resolve() method returns true. In this case, we must
-         * iterate on all crossReferences to retrieve corresponding proxies.
+         * Check if the proxyMap is null or not. Since change in ECrossReferenceAdapter (bugzilla 400891), the proxyMap
+         * is no longer used if the resolve() method returns true. In this case, we must iterate on all crossReferences
+         * to retrieve corresponding proxies.
          * 
          * @return true if the proxy map is null.
          */
@@ -62,13 +60,11 @@
         }
 
         /**
-         * Get all proxy {@link EObject EObjects} that have URI corresponding to
-         * the <code>resourceURI</code>. Warning: this map is computed at each
-         * call and can be costly.
+         * Get all proxy {@link EObject EObjects} that have URI corresponding to the <code>resourceURI</code>. Warning:
+         * this map is computed at each call and can be costly.
          * 
          * @param resourceURI
-         *            The URI of the resource for which we want to get the proxy
-         *            EObjects.
+         *            The URI of the resource for which we want to get the proxy EObjects.
          * @return map of proxies with the URI as key.
          */
         public Map<URI, List<EObject>> getProxiesOf(URI resourceURI) {
@@ -94,31 +90,7 @@
 
         @Override
         protected Collection<EStructuralFeature.Setting> newCollection() {
-            return new BasicEList<EStructuralFeature.Setting>() {
-                private static final long serialVersionUID = 1L;
-
-                @Override
-                protected Object[] newData(int capacity) {
-                    return new EStructuralFeature.Setting[capacity];
-                }
-
-                @Override
-                public boolean add(EStructuralFeature.Setting setting) {
-                    if (!isSettingTargets || ECrossReferenceAdapterWithUnproxyCapability.this.resolve()) {
-                        EObject eObject = setting.getEObject();
-                        EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
-                        EStructuralFeature.Setting[] settingData = (EStructuralFeature.Setting[]) data;
-                        for (int i = 0; i < size; ++i) {
-                            EStructuralFeature.Setting containedSetting = settingData[i];
-                            if (containedSetting.getEObject() == eObject && containedSetting.getEStructuralFeature() == eStructuralFeature) {
-                                return false;
-                            }
-                        }
-                    }
-                    addUnique(setting);
-                    return true;
-                }
-            };
+            return new FastInverseCrossReferencesList(() -> !ECrossReferenceAdapterWithUnproxyCapability.this.settingTargets || ECrossReferenceAdapterWithUnproxyCapability.this.resolve());
         }
     }
 
@@ -128,15 +100,13 @@
     }
 
     /**
-     * Look at all EObjects of the specified resource and resolve proxy cross
-     * reference that reference these EObjects.<BR>
-     * A part of {@link #resolveAll(EObject)} has been duplicated to avoid the
-     * time consumption of accessing to resourceURI for each objects of the same
-     * resource.
+     * Look at all EObjects of the specified resource and resolve proxy cross reference that reference these
+     * EObjects.<BR>
+     * A part of {@link #resolveAll(EObject)} has been duplicated to avoid the time consumption of accessing to
+     * resourceURI for each objects of the same resource.
      * 
      * @param resource
-     *            Each cross reference pointing to a proxy of this
-     *            <code>resource</code> will be resolved.
+     *            Each cross reference pointing to a proxy of this <code>resource</code> will be resolved.
      */
     public void resolveProxyCrossReferences(Resource resource) {
         if (resource != null) {
diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/SiriusCrossReferenceAdapterImpl.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/SiriusCrossReferenceAdapterImpl.java
index 46f67e0..ecff1a8 100644
--- a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/SiriusCrossReferenceAdapterImpl.java
+++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/util/SiriusCrossReferenceAdapterImpl.java
@@ -16,11 +16,10 @@
 
 import org.eclipse.emf.common.notify.Adapter;
 import org.eclipse.emf.common.notify.Notifier;
-import org.eclipse.emf.common.util.BasicEList;
-import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import org.eclipse.sirius.common.tools.internal.util.FastInverseCrossReferencesList;
 
 /**
  * Specific {@link ECrossReferenceAdapter} which resolve proxy ability can be disabled. All
@@ -74,33 +73,8 @@
         return new InverseCrossReferencer() {
             private static final long serialVersionUID = 1L;
 
-            @Override
             protected Collection<EStructuralFeature.Setting> newCollection() {
-                return new BasicEList<EStructuralFeature.Setting>() {
-                    private static final long serialVersionUID = 1L;
-
-                    @Override
-                    protected Object[] newData(int capacity) {
-                        return new EStructuralFeature.Setting[capacity];
-                    }
-
-                    @Override
-                    public boolean add(EStructuralFeature.Setting setting) {
-                        if (!isSettingTargets || SiriusCrossReferenceAdapterImpl.this.resolve()) {
-                            EObject eObject = setting.getEObject();
-                            EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
-                            EStructuralFeature.Setting[] settingData = (EStructuralFeature.Setting[]) data;
-                            for (int i = 0; i < size; ++i) {
-                                EStructuralFeature.Setting containedSetting = settingData[i];
-                                if (containedSetting.getEObject() == eObject && containedSetting.getEStructuralFeature() == eStructuralFeature) {
-                                    return false;
-                                }
-                            }
-                        }
-                        addUnique(setting);
-                        return true;
-                    }
-                };
+                return new FastInverseCrossReferencesList(() -> !SiriusCrossReferenceAdapterImpl.this.settingTargets || SiriusCrossReferenceAdapterImpl.this.resolve());
             }
         };
     }
diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/util/FastInverseCrossReferencesList.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/util/FastInverseCrossReferencesList.java
new file mode 100644
index 0000000..487f5dc
--- /dev/null
+++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/util/FastInverseCrossReferencesList.java
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2005-2017 IBM Corporation, CEA, 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: 
+ *   IBM - Initial API and implementation
+ *   Christian W. Damus (CEA) - bug 433027
+ *   Obeo - extracted internal BasicEList subclass
+ *******************************************************************************/
+package org.eclipse.sirius.common.tools.internal.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BooleanSupplier;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+// CHECKSTYLE:OFF
+/**
+ * A {@link BasicEList} optimized for use in inverse cross-referencers. It switches from a plain list to a map for
+ * storing inverse references when the number of settings increases beyond a threshold. Using a map allows for very fast
+ * containment testing, which can bring spectacular performance improvements when cross-referencing models where some
+ * elements are referenced a lot.
+ * <p>
+ * Extracted from org.eclipse.emf.ecore.util.ECrossReferenceAdapter.InverseCrossReferencer.newCollection() added in EMF
+ * 2.13 by http://git.eclipse.org/c/emf/org.eclipse.emf.git/commit/?id=0448d995991f08fd5764f1efe134f3a5395593cc.
+ */
+public final class FastInverseCrossReferencesList extends BasicEList<EStructuralFeature.Setting> {
+    private static final long serialVersionUID = 1L;
+
+    private static final int THRESHOLD = 100;
+
+    private Map<EObject, Object> map;
+
+    private BooleanSupplier checkUnique;
+
+    public FastInverseCrossReferencesList(BooleanSupplier checkUnique) {
+        this.checkUnique = checkUnique;
+    }
+
+    @Override
+    protected Object[] newData(int capacity) {
+        return new EStructuralFeature.Setting[capacity];
+    }
+
+    @Override
+    protected void didAdd(int index, EStructuralFeature.Setting setting) {
+        if (map != null) {
+            EObject eObject = setting.getEObject();
+            EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
+            Object object = map.get(eObject);
+            if (object == null) {
+                map.put(eObject, eStructuralFeature);
+            } else if (object instanceof Object[]) {
+                Object[] oldFeatures = (Object[]) object;
+                Object[] newFeatures = new Object[oldFeatures.length + 1];
+                System.arraycopy(oldFeatures, 0, newFeatures, 0, oldFeatures.length);
+                newFeatures[oldFeatures.length] = eStructuralFeature;
+                map.put(eObject, newFeatures);
+            } else {
+                Object[] newFeatures = new Object[2];
+                newFeatures[0] = object;
+                newFeatures[1] = eStructuralFeature;
+                map.put(eObject, newFeatures);
+            }
+        }
+    }
+
+    @Override
+    protected void didRemove(int index, EStructuralFeature.Setting setting) {
+        if (map != null) {
+            if (size < THRESHOLD / 2) {
+                map = null;
+            } else {
+                EObject eObject = setting.getEObject();
+                Object object = map.get(eObject);
+                if (object instanceof Object[]) {
+                    Object[] oldFeatures = (Object[]) object;
+                    EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
+                    if (oldFeatures.length == 2) {
+                        map.put(eObject, oldFeatures[0] == eStructuralFeature ? oldFeatures[1] : oldFeatures[0]);
+                    } else {
+                        Object[] newFeatures = new Object[oldFeatures.length - 1];
+                        for (int i = 0; i < oldFeatures.length; ++i) {
+                            Object oldFeature = oldFeatures[i];
+                            if (oldFeature == eStructuralFeature) {
+                                System.arraycopy(oldFeatures, i + 1, newFeatures, i, oldFeatures.length - i - 1);
+                                break;
+                            } else {
+                                newFeatures[i] = oldFeatures[i];
+                            }
+                        }
+                        map.put(eObject, newFeatures);
+                    }
+                } else {
+                    map.remove(eObject);
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean add(EStructuralFeature.Setting setting) {
+        if (size > 0 && checkUnique.getAsBoolean()) {
+            EObject eObject = setting.getEObject();
+            if (size > THRESHOLD) {
+                if (map == null) {
+                    map = new HashMap<EObject, Object>();
+                    EStructuralFeature.Setting[] settingData = (EStructuralFeature.Setting[]) data;
+                    for (int i = 0; i < size; ++i) {
+                        didAdd(i, settingData[i]);
+                    }
+                }
+
+                Object object = map.get(eObject);
+                if (object != null) {
+                    EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
+                    if (object == eStructuralFeature) {
+                        return false;
+                    } else if (object instanceof Object[]) {
+                        Object[] features = (Object[]) object;
+                        for (int i = 0; i < features.length; ++i) {
+                            if (features[i] == eStructuralFeature) {
+                                return false;
+                            }
+                        }
+                    }
+                }
+            } else {
+                EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
+                EStructuralFeature.Setting[] settingData = (EStructuralFeature.Setting[]) data;
+                for (int i = 0; i < size; ++i) {
+                    EStructuralFeature.Setting containedSetting = settingData[i];
+                    if (containedSetting.getEObject() == eObject && containedSetting.getEStructuralFeature() == eStructuralFeature) {
+                        return false;
+                    }
+                }
+            }
+        }
+        addUnique(setting);
+        return true;
+    }
+}
+// CHECKSTYLE:ON
diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/perf/common/Session1MillionTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/perf/common/Session1MillionTests.java
index 48ba244..319f58c 100644
--- a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/perf/common/Session1MillionTests.java
+++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/perf/common/Session1MillionTests.java
@@ -44,9 +44,9 @@
     /**
      * limit is set empirically.
      */
-    private static final long MAX_TIME_TO_OPEN_SECONDS = 110;
+    private static final long MAX_TIME_TO_OPEN_SECONDS = 15;
 
-    private static final long MAX_TIME_TO_CLOSE_SECONDS = 10;
+    private static final long MAX_TIME_TO_CLOSE_SECONDS = 5;
 
     private static final int NUMBER_Of_ELEMENTS = 966220;
 
diff --git a/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/LocalResourceCollector.java b/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/LocalResourceCollector.java
index c938de2..5445b87 100644
--- a/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/LocalResourceCollector.java
+++ b/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/LocalResourceCollector.java
@@ -17,7 +17,6 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 
-import org.eclipse.emf.common.util.BasicEList;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.emf.ecore.EStructuralFeature;
@@ -28,6 +27,7 @@
 import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.sirius.business.api.query.ResourceQuery;
 import org.eclipse.sirius.common.tools.api.util.SiriusCrossReferenceAdapterImpl;
+import org.eclipse.sirius.common.tools.internal.util.FastInverseCrossReferencesList;
 
 /**
  * A {@link IResourceCollector} for local {@link Resource}.
@@ -231,33 +231,8 @@
 
         @Override
         protected Collection<EStructuralFeature.Setting> newCollection() {
-            return new BasicEList<EStructuralFeature.Setting>() {
-                private static final long serialVersionUID = 1L;
-
-                @Override
-                protected Object[] newData(int capacity) {
-                    return new EStructuralFeature.Setting[capacity];
-                }
-
-                @Override
-                public boolean add(EStructuralFeature.Setting setting) {
-                    if (!isSettingTargets || LocalResourceCollector.this.resolve()) {
-                        EObject eObject = setting.getEObject();
-                        EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
-                        EStructuralFeature.Setting[] settingData = (EStructuralFeature.Setting[]) data;
-                        for (int i = 0; i < size; ++i) {
-                            EStructuralFeature.Setting containedSetting = settingData[i];
-                            if (containedSetting.getEObject() == eObject && containedSetting.getEStructuralFeature() == eStructuralFeature) {
-                                return false;
-                            }
-                        }
-                    }
-                    addUnique(setting);
-                    return true;
-                }
-            };
+            return new FastInverseCrossReferencesList(() -> !LocalResourceCollector.this.settingTargets || LocalResourceCollector.this.resolve());
         }
-
     }
 
 }