blob: 52b26c80e0952ab589bb3b67f148d003a93bf41c [file] [log] [blame]
/*******************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
* Philip Langer - bug 516524
*******************************************************************************/
package org.eclipse.emf.compare.utils;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
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;
import org.eclipse.emf.ecore.util.InternalEList;
/**
* This utility class holds methods that will be used by the diff and merge processes. TODO: Maybe useless.
*
* @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
*/
public final class ReferenceUtil {
/**
* Utility classes don't need a default constructor.
*/
private ReferenceUtil() {
// Hides default constructor
}
/**
* This utility simply allows us to retrieve the value of a given feature as a List.
*
* @param object
* The object for which feature we need a value.
* @param feature
* The actual feature of which we need the value.
* @return The value of the given <code>feature</code> for the given <code>object</code> as a list. An
* empty list if this object has no value for that feature or if the object is <code>null</code>.
*/
@SuppressWarnings("unchecked")
public static List<Object> getAsList(EObject object, EStructuralFeature feature) {
if (object != null && feature != null) {
Object value = safeEGet(object, feature);
final List<Object> asList;
if (feature == EcorePackage.Literals.ECLASS__ESUPER_TYPES
|| feature == EcorePackage.Literals.EOPERATION__EEXCEPTIONS) {
// workaround 394286. Use the normal list, resolution is not much of a problem on these.
asList = (List<Object>)value;
} else if (value instanceof InternalEList<?>) {
// EMF ignores the "resolve" flag for containment lists...
asList = ((InternalEList<Object>)value).basicList();
} else if (value instanceof List) {
asList = (List<Object>)value;
} else if (value instanceof Iterable) {
asList = ImmutableList.copyOf((Iterable<Object>)value);
} else if (value != null) {
asList = ImmutableList.of(value);
} else {
asList = Collections.emptyList();
}
return asList;
}
return Collections.emptyList();
}
/**
* This utility simply allows us to retrieve the value of a given feature as a List.
* <p>
* <b>Note</b> that contrary to {@link #getAsList(EObject, EStructuralFeature)}, this will allow proxy
* resolution.
* </p>
*
* @param object
* The object for which feature we need a value.
* @param feature
* The actual feature of which we need the value.
* @return The value of the given <code>feature</code> for the given <code>object</code> as a list. An
* empty list if this object has no value for that feature or if the object is <code>null</code>.
*/
@SuppressWarnings("unchecked")
public static List<Object> getAsListResolving(EObject object, EStructuralFeature feature) {
if (object != null && feature != null) {
Object value = safeResolvingEGet(object, feature);
final List<Object> asList;
if (feature == EcorePackage.Literals.ECLASS__ESUPER_TYPES
|| feature == EcorePackage.Literals.EOPERATION__EEXCEPTIONS) {
// workaround 394286. Use the normal list, resolution is not much of a problem on these.
asList = (List<Object>)value;
} else if (value instanceof List) {
asList = (List<Object>)value;
} else if (value instanceof Iterable) {
asList = ImmutableList.copyOf((Iterable<Object>)value);
} else if (value != null) {
asList = ImmutableList.of(value);
} else {
asList = Collections.emptyList();
}
return asList;
}
return Collections.emptyList();
}
/**
* In case of dynamic EObjects, the EClasses of both sides might be different, making "eget" fail in
* "unknown feature". We assume that even if the EClasses are distinct instances, they are the same
* nonetheless, and thus we can use the feature name in order to retrieve the feature's value.
*
* @param object
* The object for which feature we need a value, must not be <code>null</code>.
* @param feature
* The actual feature of which we need the value, must not be <code>null</code>.
* @return The value of the given {@code feature} for the given {@code object}.
*/
public static Object safeEGet(EObject object, EStructuralFeature feature) {
final int featureID = getFeatureID(feature, object.eClass());
return ((InternalEObject)object).eGet(featureID, false, true);
}
/**
* In case of dynamic EObjects, the EClasses of both sides might be different, making "eget" fail in
* "unknown feature". We assume that even if the EClasses are distinct instances, they are the same
* nonetheless, and thus we can use the feature name in order to retrieve the feature's value.
* <p>
* <b>Note</b> that contrary to {@link #safeEGet(EObject, EStructuralFeature)}, this will allow proxy
* resolution.
* </p>
*
* @param object
* The object for which feature we need a value, must not be <code>null</code>.
* @param feature
* The actual feature of which we need the value, must not be <code>null</code>.
* @return The value of the given {@code feature} for the given {@code object}.
*/
public static Object safeResolvingEGet(EObject object, EStructuralFeature feature) {
final int featureID = getFeatureID(feature, object.eClass());
return ((InternalEObject)object).eGet(featureID, true, true);
}
/**
* In case of dynamic EObjects, the EClasses of both sides might be different, making "isset" fail in
* "unknown feature". We assume that even if the EClasses are distinct instances, they are the same
* nonetheless, and thus we can use the feature name in order to retrieve the feature's value.
*
* @param object
* The object for which feature we need a value, must not be <code>null</code>.
* @param feature
* The actual feature of which we need the value, must not be <code>null</code>.
* @return whether the {@code feature} for the given {@code object} is set.
*/
public static boolean safeEIsSet(EObject object, EStructuralFeature feature) {
int featureID = getFeatureID(feature, object.eClass());
return ((InternalEObject)object).eIsSet(featureID);
}
/**
* In case of dynamic EObjects, the EClasses of both sides might be different, making "isset" fail in
* "unknown feature". We assume that even if the EClasses are distinct instances, they are the same
* nonetheless, and thus we can use the feature name in order to retrieve the feature's value.
*
* @param object
* The object for which feature we'll set the value, must not be <code>null</code>.
* @param feature
* The actual feature of which we'll set the value, must not be <code>null</code>.
* @param newValue
* The value to set, can be <code>null</code>.
*/
public static void safeESet(EObject object, EStructuralFeature feature, Object 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;
}
/**
* Checks if the given reference is a FeatureMap-derived feature.
*
* @param reference
* the given EReference.
* @return true if the given reference is a FeatureMap-derived feature, false otherwise.
* @since 3.2
*/
public static boolean isFeatureMapDerivedFeature(EReference reference) {
if (reference.isDerived() && reference.isTransient() && reference.isVolatile()) {
String annotation = EcoreUtil.getAnnotation(reference, ExtendedMetaData.ANNOTATION_URI, "group"); //$NON-NLS-1$
if (annotation != null) {
if (annotation.startsWith("#")) { //$NON-NLS-1$
annotation = annotation.substring(1); // deletes the '#' character
}
EClass container = reference.getEContainingClass();
for (EAttribute content : container.getEAttributes()) {
if (FeatureMapUtil.isFeatureMap(content)
&& annotation.toLowerCase().startsWith(content.getName().toLowerCase())) {
return true;
}
}
}
}
return false;
}
}