blob: f0ba77a9fc1f781dbec44557b091e81e156f7423 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2013 Timing-Architects Embedded Systems GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
* *******************************************************************************
*/
package org.eclipse.app4mc.amalthea.model.provider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.edit.command.CommandParameter;
public class EcoreGenericsHelper {
// Suppress default constructor
private EcoreGenericsHelper() {
throw new IllegalStateException("Utility class");
}
/**
* Finds the EClassifier objects for the generic parameters of the class of 'object', depending on the position of
* 'object' in the model. This function only checks the parent elements of the EMF tree to find the expected
* EClassifier for a generic parameter. So references to the object (that are not containment reference) with
* generic parameters are ignored.
*
* @param object
* @return A map where the generic parameters are mapped to the expected type, the map can be incomplete or null if
* there are no generic parameters or no values for the parameters.
*/
public static Map<ETypeParameter, EClassifier> generateGenericParameterMap(final Object object) {
// create generic parameter map
Map<ETypeParameter, EClassifier> result = null;
if (object instanceof EObject) {
final EObject eObject = (EObject) object;
final EClass eClass = eObject.eClass();
// generic type parameters of the class
final List<ETypeParameter> typeParameters = eClass.getETypeParameters();
if (typeParameters != null && !typeParameters.isEmpty()) {
// generic type arguments for the generic type parameters of the
// class, got from the containing feature
final EStructuralFeature containingFeature = eObject.eContainingFeature();
final EGenericType genericType = containingFeature.getEGenericType();
final List<EGenericType> genericTypeArguments = genericType.getETypeArguments();
if (genericTypeArguments != null && !genericTypeArguments.isEmpty()) {
// map name of type parameters to type arguments (by index)
result = new HashMap<>();
final int size = Math.min(typeParameters.size(), genericTypeArguments.size());
for (int i = 0; i < size; i++) {
// local class generic type parameter
final ETypeParameter typeParameter = typeParameters.get(i);
// type of parent feature
final EGenericType type = genericTypeArguments.get(i);
EClassifier typeClassifier = type.getEClassifier();
if (typeClassifier == null) {
final EObject eContainer = eObject.eContainer();
final ETypeParameter parentTypeParameter = type.getETypeParameter();
if (eContainer != null && parentTypeParameter != null) {
final Map<ETypeParameter, EClassifier> parentGenericParameterMap = generateGenericParameterMap(
eContainer);
if (parentGenericParameterMap != null) {
typeClassifier = parentGenericParameterMap.get(parentTypeParameter);
}
}
}
if (typeClassifier != null) {
result.put(typeParameter, typeClassifier);
}
}
}
}
}
return result;
}
/**
* Corrects the child descriptors of an EMF-generated item provider. It removes all child descriptors
* that are not allowed because of the restriction of a generic type.
*
* @param object
* @param newChildDescriptors
* The list of child descriptors to modify
*/
public static void correctChildDescriptorListForGenericTypes(final Object object,
final Collection<Object> newChildDescriptors) {
final Map<ETypeParameter, EClassifier> genericParameterMap = generateGenericParameterMap(object);
if (genericParameterMap != null) {
correctChildDescriptorListForGenericTypes(genericParameterMap, newChildDescriptors);
}
}
/**
* Corrects the child descriptors of an EMF-generated item provider. It removes all child descriptors
* that are not allowed because of the restriction of a generic type.
*
* @param genericParameterMap
* The parameter map that defines which EClassifier is expected for which generic type
* @param newChildDescriptors
* The list of child descriptors to modify
*/
public static void correctChildDescriptorListForGenericTypes(
final Map<ETypeParameter, EClassifier> genericParameterMap, final Collection<Object> newChildDescriptors) {
final List<Object> childDescriptorsToRemove = new ArrayList<>();
// check child descriptors
for (final Object cd : newChildDescriptors) {
if (cd instanceof CommandParameter) {
final CommandParameter cp = (CommandParameter) cd;
if (cp.getFeature() instanceof EReference) {
final EReference reference = (EReference) cp.getFeature();
final EGenericType genericType = reference.getEGenericType();
final ETypeParameter typeParameter = genericType.getETypeParameter();
if (typeParameter == null) {
continue;
}
final EClassifier expectedTypeOfValue = genericParameterMap.get(typeParameter);
final Object value = cp.getValue();
if (expectedTypeOfValue != null && !expectedTypeOfValue.isInstance(value)) {
childDescriptorsToRemove.add(cd);
}
}
}
}
newChildDescriptors.removeAll(childDescriptorsToRemove);
}
}