[509680] EcoreUtil lacks of deleteAll and removeAll methods, causing
poor performance
diff --git a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreUtil.java b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreUtil.java
index 613f05b..f148f27 100644
--- a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreUtil.java
+++ b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreUtil.java
@@ -19,6 +19,7 @@
 import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -3389,7 +3390,7 @@
    * as well as from any other feature that references it 
    * within the enclosing resource set, resource, or root object.
    * If recursive true, contained children of the object that are in the same resource 
-   * are similarly removed from any features that references them.
+   * are similarly removed from any features that reference them.
    * @param eObject the object to delete.
    * @param recursive whether references to contained children should also be removed.
    * @since 2.4
@@ -3410,6 +3411,7 @@
         if (childEObject.eDirectResource() != null)
         {
           crossResourceEObjects.add(childEObject);
+          j.prune();
         }
         else
         {
@@ -3462,6 +3464,201 @@
   }
 
   /**
+   * Returns the root object;
+   * it may be either 
+   * the {@link #getRootContainer(EObject, boolean) root container}
+   * if that doesn't have a {@link EObject#eResource() containing} resource,
+   * the containing resource of the root container if that doesn't have a {@link Resource#getResourceSet() containing} resource set,
+   * or the resource set of the root container's resource.
+   *
+   * @param resolve whether to resolve container proxies.
+   * @return the root object.
+   * @since 2.13
+   */
+  public static Notifier getRoot(EObject eObject, boolean resolve)
+  {
+    Resource resource = eObject.eResource();
+    if (resource == null)
+    {
+      return EcoreUtil.getRootContainer(eObject, resolve);
+    }
+    else
+    {
+      ResourceSet resourceSet = resource.getResourceSet();
+      return (resourceSet != null) ? resourceSet : resource;
+    }
+  }
+
+
+  /**
+   * Deletes each object from its {@link EObject#eResource containing} resource 
+   * and/or its {@link EObject#eContainer containing} object
+   * as well as from any other feature that references it 
+   * within the enclosing resource set, resource, or root object of any of the objects.
+   * If recursive true, contained children of the object that are in the same resource 
+   * are similarly removed from any features that reference them.
+   * @param eObjects the objects to delete.
+   * @see #delete(EObject, boolean)
+   * @param recursive whether references to contained children should also be removed.
+   * @since 2.13
+   */
+  public static void deleteAll(Collection<? extends EObject> eObjects, boolean recursive)
+  {
+    // Get objects to remove and all their descendants.
+    Set<Notifier> roots = new HashSet<Notifier>();
+    Set<EObject> eAllObjects = new HashSet<EObject>(eObjects);
+    Set<EObject> crossResourceEObjects = new HashSet<EObject>();
+    for (EObject eObject : eObjects)
+    {
+      roots.add(getRoot(eObject, true));
+
+      if (recursive)
+      {
+        for (TreeIterator<EObject> j = eObject.eAllContents(); j.hasNext();)
+        {
+          InternalEObject childEObject = (InternalEObject)j.next();
+          if (childEObject.eDirectResource() != null)
+          {
+            crossResourceEObjects.add(childEObject);
+            j.prune();
+          }
+          else
+          {
+            eAllObjects.add(childEObject);
+          }
+        }
+      }
+    }
+
+    // Find usages.
+    Map<EObject, Collection<EStructuralFeature.Setting>> usages = UsageCrossReferencer.findAll(eAllObjects, roots);
+
+    // Remove all usages.
+    for (Map.Entry<EObject, Collection<EStructuralFeature.Setting>> entry : usages.entrySet())
+    {
+      EObject deletedEObject = entry.getKey();
+      Collection<EStructuralFeature.Setting> settings = entry.getValue();
+      for (EStructuralFeature.Setting setting : settings)
+      {
+        if (!eAllObjects.contains(setting.getEObject()) && setting.getEStructuralFeature().isChangeable())
+        {
+          EcoreUtil.remove(setting, deletedEObject);
+        }
+      }
+    }
+
+    // Remove all objects.
+    removeAll(eObjects);
+
+    // Disconnect all cross resource objects.
+    for (EObject crossResourceEObject : crossResourceEObjects)
+    {
+      EcoreUtil.remove(crossResourceEObject.eContainer(), crossResourceEObject.eContainmentFeature(), crossResourceEObject);
+    }
+  }
+
+  /**
+   * Removes each object from its {@link EObject#eResource containing}
+   * resource and/or its {@link EObject#eContainer containing} object.
+   * 
+   * @param eObjects the objects to remove.
+   * @see EcoreUtil#remove(EObject)
+   * @since 2.13
+   */
+  public static void removeAll(Collection<? extends EObject> eObjects)
+  {
+    // First collect all siblings based on their containing EStructuralFeature.Setting so that each setting can be modified with a single removeAll.
+    // Use IdentityHashMap as its performance is much better than plain HashMap.
+    //
+    Map<EStructuralFeature.Setting, Collection<EObject>> settings = new IdentityHashMap<EStructuralFeature.Setting, Collection<EObject>>();
+    Map<Resource, Collection<EObject>> resources = new HashMap<Resource, Collection<EObject>>();
+    for (EObject eObject : eObjects)
+    {
+      InternalEObject internalEObject = (InternalEObject)eObject;
+      InternalEObject container = internalEObject.eInternalContainer();
+      if (container != null)
+      {
+        EStructuralFeature.Setting setting = container.eSetting(eObject.eContainingFeature());
+        Collection<EObject> values = settings.get(setting);
+        if (values == null)
+        {
+          values = new ArrayList<EObject>();
+          settings.put(setting, values);
+        }
+        values.add(eObject);
+      }
+
+      Resource resource = internalEObject.eDirectResource();
+      if (resource != null)
+      {
+        Collection<EObject> values = resources.get(resource);
+        if (values == null)
+        {
+          values = new ArrayList<EObject>();
+          resources.put(resource, values);
+        }
+        values.add(eObject);
+      }
+    }
+
+    // Remove the siblings from their settings using removeAll to improve performance.
+    //
+    for (Map.Entry<EStructuralFeature.Setting, Collection<EObject>> entry : settings.entrySet())
+    {
+      removeAll(entry.getKey(), entry.getValue());
+    }
+
+    // Remove objects from their resources.
+    //
+    for (Map.Entry<Resource, Collection<EObject>> entry : resources.entrySet())
+    {
+      entry.getKey().getContents().removeAll(entry.getValue());
+    }
+  }
+
+  /**
+   * Removes the values from the feature of the object.
+   * 
+   * @param eObject the object holding the value.
+   * @param eStructuralFeature the feature of the object holding the value.
+   * @param value the value to remove.
+   * @see EcoreUtil#remove(EObject, EStructuralFeature, Object)
+   * @since 2.13
+   */
+  public static void removeAll(EObject eObject, EStructuralFeature eStructuralFeature, Collection<?> values)
+  {
+    if (FeatureMapUtil.isMany(eObject, eStructuralFeature))
+    {
+      ((List<?>)eObject.eGet(eStructuralFeature)).removeAll(values);
+    }
+    else
+    {
+      // The feature is assumed to hold the value.
+      eObject.eUnset(eStructuralFeature);
+    }
+  }
+
+  /**
+   * Removes the values from the setting.
+   * 
+   * @param setting the setting holding the value.
+   * @param values the values to remove.
+   * @since 2.13
+   */
+  public static void removeAll(EStructuralFeature.Setting setting, Collection<?> values)
+  {
+    if (FeatureMapUtil.isMany(setting.getEObject(), setting.getEStructuralFeature()))
+    {
+      ((List<?>)setting.get(false)).removeAll(values);
+    }
+    else
+    {
+      // The feature is assumed to hold the value.
+      setting.unset();
+    }
+  }
+
+  /**
    * Creates an instance of the class.
    * @param eClass the class to instantiate.
    * @return an instance of the class.
diff --git a/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF b/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF
index 21d1fb5..a1e544a 100644
--- a/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.emf.test.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.emf.test.core;singleton:=true
-Bundle-Version: 2.12.0.qualifier
+Bundle-Version: 2.13.0.qualifier
 Bundle-ClassPath: test.core.jar
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/EcoreUtilStaticMethodsTest.java b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/EcoreUtilStaticMethodsTest.java
index 389feff..dda0e43 100644
--- a/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/EcoreUtilStaticMethodsTest.java
+++ b/tests/org.eclipse.emf.test.core/src/org/eclipse/emf/test/core/ecore/EcoreUtilStaticMethodsTest.java
@@ -240,4 +240,64 @@
     assertTrue(EcoreUtil.isAncestor(resourceSet, ePackage));
     assertFalse(EcoreUtil.isAncestor(otherResourceSet, ePackage));
   }
+  
+  @Test
+  public void testDelete()
+  {
+    ResourceSet resourceSet = new ResourceSetImpl();
+    Resource resource = new ResourceImpl();
+    resourceSet.getResources().add(resource);
+    EPackage ePackage = EcoreFactory.eINSTANCE.createEPackage();
+    resource.getContents().add(ePackage);
+    
+    Resource otherResource = new ResourceImpl();
+    resourceSet.getResources().add(otherResource);
+    EPackage eSubPackage = EcoreFactory.eINSTANCE.createEPackage();
+    otherResource.getContents().add(eSubPackage);
+    
+    EClass eClass = EcoreFactory.eINSTANCE.createEClass();
+    eSubPackage.getEClassifiers().add(eClass);
+    
+    EClass otherEClass = EcoreFactory.eINSTANCE.createEClass();
+    EReference eReference = EcoreFactory.eINSTANCE.createEReference();
+    otherEClass.getEStructuralFeatures().add(eReference);
+    eReference.setEType(eClass);
+    otherResource.getContents().add(otherEClass);
+    
+    ePackage.getESubpackages().add(eSubPackage);
+    
+    EcoreUtil.delete(ePackage, true);
+    
+    assertTrue("Recursive delete doesn't delete cross resource contained children", eReference.getEType() == eClass);
+  }
+  
+  @Test
+  public void testDeleteAll()
+  {
+    ResourceSet resourceSet = new ResourceSetImpl();
+    Resource resource = new ResourceImpl();
+    resourceSet.getResources().add(resource);
+    EPackage ePackage = EcoreFactory.eINSTANCE.createEPackage();
+    resource.getContents().add(ePackage);
+    
+    Resource otherResource = new ResourceImpl();
+    resourceSet.getResources().add(otherResource);
+    EPackage eSubPackage = EcoreFactory.eINSTANCE.createEPackage();
+    otherResource.getContents().add(eSubPackage);
+    
+    EClass eClass = EcoreFactory.eINSTANCE.createEClass();
+    eSubPackage.getEClassifiers().add(eClass);
+    
+    EClass otherEClass = EcoreFactory.eINSTANCE.createEClass();
+    EReference eReference = EcoreFactory.eINSTANCE.createEReference();
+    otherEClass.getEStructuralFeatures().add(eReference);
+    eReference.setEType(eClass);
+    otherResource.getContents().add(otherEClass);
+    
+    ePackage.getESubpackages().add(eSubPackage);
+    
+    EcoreUtil.deleteAll(Collections.singleton(ePackage), true);
+    
+    assertTrue("Recursive delete doesn't delete cross resource contained children", eReference.getEType() == eClass);
+  }
 }