[516524] Use more efficient feature look-up in ReferenceUtil

ReferenceUtil could make use of an EClass.getFeatureID(feature) which
returns -1 if the feature doesn't belong to the class. Only in that case
do we need to try to look up the feature by name. Once we've computed
the feature ID, we can use it directly to efficiently access the
generated reflective methods.

Bug: 516524
Change-Id: I6ff031ebc02dd10bf3b0d88c00f03f15c25cfb61
Signed-off-by: Philip Langer <planger@eclipsesource.com>
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/MatchUtilFeatureContainsTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/MatchUtilFeatureContainsTest.java
index f46559d..a905457 100644
--- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/MatchUtilFeatureContainsTest.java
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/MatchUtilFeatureContainsTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 EclipseSource Muenchen GmbH.
+ * Copyright (c) 2015, 2017 EclipseSource Muenchen GmbH 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,6 +7,7 @@
  * 
  * Contributors:
  *     Michael Borkowski - initial tests
+ *     Philip Langer - bug 516524
  *******************************************************************************/
 package org.eclipse.emf.compare.tests.utils;
 
@@ -31,11 +32,13 @@
 @SuppressWarnings({"boxing", "nls" })
 public class MatchUtilFeatureContainsTest {
 
+	private static final int MOCK_FEATURE_ID = 1;
+
 	List<EObject> featureList;
 
 	InternalEObject value;
 
-	EObject container;
+	InternalEObject container;
 
 	EClass eClass;
 
@@ -45,17 +48,20 @@
 	public void setUp() {
 		EPackage ePackage = mock(EPackage.class);
 
-		container = mock(EObject.class);
+		container = mock(InternalEObject.class);
+
 		feature = mock(EStructuralFeature.class);
+		when(feature.isMany()).thenReturn(true);
 
 		eClass = mock(EClass.class);
 		when(eClass.getEPackage()).thenReturn(ePackage);
+		when(eClass.getFeatureID(feature)).thenReturn(MOCK_FEATURE_ID);
 
 		when(container.eClass()).thenReturn(eClass);
 		when(feature.getEContainingClass()).thenReturn(eClass);
 
 		featureList = new LinkedList<EObject>();
-		when(container.eGet(feature, false)).thenReturn(featureList);
+		when(container.eGet(MOCK_FEATURE_ID, false, true)).thenReturn(featureList);
 
 		value = mockInternalObject();
 		featureList.add(value);
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/ReferenceUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/ReferenceUtil.java
index 7fe6bd1..488b724 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/ReferenceUtil.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/ReferenceUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo.
+ * Copyright (c) 2012, 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,6 +7,7 @@
  * 
  * Contributors:
  *     Obeo - initial API and implementation
+ *     Philip Langer - bug 516524
  *******************************************************************************/
 package org.eclipse.emf.compare.utils;
 
@@ -21,6 +22,7 @@
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.EcorePackage;
+import org.eclipse.emf.ecore.InternalEObject;
 import org.eclipse.emf.ecore.util.EcoreUtil;
 import org.eclipse.emf.ecore.util.ExtendedMetaData;
 import org.eclipse.emf.ecore.util.FeatureMapUtil;
@@ -87,14 +89,8 @@
 	 * @return The value of the given {@code feature} for the given {@code object}.
 	 */
 	public static Object safeEGet(EObject object, EStructuralFeature feature) {
-		final EClass clazz = object.eClass();
-		// TODO profile. This "if" might be counter productive : accessing both packages is probably as long
-		// as a direct lookup to the clazz.eGetEStructuralFeature...
-		if (clazz.getEPackage() == feature.getEContainingClass().getEPackage()) {
-			return object.eGet(feature, false);
-		}
-		// Assumes that the containing package is the same, let it fail otherwise
-		return object.eGet(clazz.getEStructuralFeature(feature.getName()), false);
+		final int featureID = getFeatureID(feature, object.eClass());
+		return ((InternalEObject)object).eGet(featureID, false, true);
 	}
 
 	/**
@@ -109,14 +105,8 @@
 	 * @return whether the {@code feature} for the given {@code object} is set.
 	 */
 	public static boolean safeEIsSet(EObject object, EStructuralFeature feature) {
-		final EClass clazz = object.eClass();
-		// TODO profile. This "if" might be counter productive : accessing both packages is probably as long
-		// as a direct lookup to the clazz.eGetEStructuralFeature...
-		if (clazz.getEPackage() == feature.getEContainingClass().getEPackage()) {
-			return object.eIsSet(feature);
-		}
-		// Assumes that the containing package is the same, let it fail otherwise
-		return object.eIsSet(clazz.getEStructuralFeature(feature.getName()));
+		int featureID = getFeatureID(feature, object.eClass());
+		return ((InternalEObject)object).eIsSet(featureID);
 	}
 
 	/**
@@ -132,15 +122,33 @@
 	 *            The value to set, can be <code>null</code>.
 	 */
 	public static void safeESet(EObject object, EStructuralFeature feature, Object newValue) {
-		final EClass clazz = object.eClass();
-		// TODO profile. This "if" might be counter productive : accessing both packages is probably as long
-		// as a direct lookup to the clazz.eGetEStructuralFeature...
-		if (clazz.getEPackage() == feature.getEContainingClass().getEPackage()) {
-			object.eSet(feature, newValue);
-		} else {
-			// Assumes that the containing package is the same, let it fail otherwise
-			object.eSet(clazz.getEStructuralFeature(feature.getName()), newValue);
+		int featureID = getFeatureID(feature, object.eClass());
+		((InternalEObject)object).eSet(featureID, newValue);
+	}
+
+	/**
+	 * Returns the ID of the given <code>feature</code> relative to the given <code>eClass</code>.
+	 * <p>
+	 * If the feature ID could not be found in <code>eClass</code> directly, this method will try find a
+	 * feature in <code>eClass</code> with the same name as the given <code>feature</code> and return its
+	 * feature ID. Otherwise, this method returns -1. , or -1 if the feature is not in this class.
+	 * </p>
+	 * 
+	 * @param feature
+	 *            The feature.
+	 * @param eClass
+	 *            The class.
+	 * @return The ID of the <code>feature</code> relative to <code>class</code>, or -1 if the feature or an
+	 *         equally named feature is not in <code>clazz</code>.
+	 */
+	private static int getFeatureID(EStructuralFeature feature, final EClass eClass) {
+		int featureID = eClass.getFeatureID(feature);
+		if (featureID == -1) {
+			// We may have a different but equivalent EClass, so try find the feature with the same name and
+			// compute the feature ID for that.
+			featureID = eClass.getFeatureID(eClass.getEStructuralFeature(feature.getName()));
 		}
+		return featureID;
 	}
 
 	/**