blob: 49908c5bfddd463771cbbf3d3a6db6f53148cc74 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2021 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - initial API and implementation
* E.D.Willink (CEA List) - Bug 424057 - UML 2.5 CG
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.ecore.es2as;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.util.EcoreSwitch;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Annotation;
import org.eclipse.ocl.pivot.AnyType;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.Constraint;
import org.eclipse.ocl.pivot.DataType;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.PivotFactory;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Stereotype;
import org.eclipse.ocl.pivot.TemplateParameter;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.internal.complete.StandardLibraryInternal;
import org.eclipse.ocl.pivot.internal.library.JavaCompareToOperation;
import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager;
import org.eclipse.ocl.pivot.internal.utilities.OppositePropertyDetails;
import org.eclipse.ocl.pivot.internal.utilities.PivotConstantsInternal;
import org.eclipse.ocl.pivot.library.LibraryConstants;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.IntegerValue;
import org.eclipse.ocl.pivot.values.NumberValue;
import org.eclipse.ocl.pivot.values.UnlimitedNaturalValue;
/**
* Resolve the references in the Pivot tree wrt the Ecore tree.
*
* caseXXXX returns:
* null to delegate to the super-caseXXX; never happens
* 'this' or an invalid-type/property to fail requiring a retry later when some dependency might have been resolved
* non-null, typically the pivot element for success
*/
public class Ecore2ASReferenceSwitch extends EcoreSwitch<Object>
{
@Deprecated /* @deprected moved to PropertyDetails */
public static final String PROPERTY_OPPOSITE_ROLE_NAME_KEY = OppositePropertyDetails.PROPERTY_OPPOSITE_ROLE_NAME_KEY;
@Deprecated /* @deprected moved to PropertyDetails */
public static final Object PROPERTY_OPPOSITE_ROLE_UNIQUE_KEY = OppositePropertyDetails.PROPERTY_OPPOSITE_ROLE_UNIQUE_KEY;
@Deprecated /* @deprected moved to PropertyDetails */
public static final Object PROPERTY_OPPOSITE_ROLE_ORDERED_KEY = OppositePropertyDetails.PROPERTY_OPPOSITE_ROLE_ORDERED_KEY;
@Deprecated /* @deprected moved to PropertyDetails */
public static final Object PROPERTY_OPPOSITE_ROLE_LOWER_KEY = OppositePropertyDetails.PROPERTY_OPPOSITE_ROLE_LOWER_KEY;
@Deprecated /* @deprected moved to PropertyDetails */
public static final Object PROPERTY_OPPOSITE_ROLE_UPPER_KEY = OppositePropertyDetails.PROPERTY_OPPOSITE_ROLE_UPPER_KEY;
protected final @NonNull Ecore2AS converter;
protected final @NonNull PivotMetamodelManager metamodelManager;
protected final @NonNull StandardLibraryInternal standardLibrary;
private final @NonNull Property oclInvalidProperty;
public Ecore2ASReferenceSwitch(@NonNull Ecore2AS converter) {
this.converter = converter;
this.metamodelManager = converter.getMetamodelManager();
this.standardLibrary = metamodelManager.getStandardLibrary();
this.oclInvalidProperty = standardLibrary.getOclInvalidProperty();
}
@Override
public Object caseEAnnotation(EAnnotation eAnnotation) {
assert eAnnotation != null;
Annotation pivotElement = converter.getCreated(Annotation.class, eAnnotation);
if (pivotElement == null) {
return this;
}
doSwitchAll(Element.class, ClassUtil.<Element>nullFree(pivotElement.getReferences()), eAnnotation.getReferences());
return pivotElement;
}
@Override
public Object caseEClass(EClass eClass) {
assert eClass != null;
org.eclipse.ocl.pivot.Class pivotElement = converter.getCreated(org.eclipse.ocl.pivot.Class.class, eClass);
if (pivotElement == null) {
return this;
}
doSwitchAll(org.eclipse.ocl.pivot.Class.class, ClassUtil.<org.eclipse.ocl.pivot.Class>nullFree(pivotElement.getSuperClasses()), eClass.getEGenericSuperTypes());
if (pivotElement.getSuperClasses().isEmpty() && !(pivotElement instanceof AnyType)) {
org.eclipse.ocl.pivot.Class superType = pivotElement instanceof Stereotype ? standardLibrary.getOclStereotypeType() : standardLibrary.getOclElementType();
pivotElement.getSuperClasses().add(superType);
}
return pivotElement;
}
@Override
public Object caseEDataType(EDataType eDataType) {
assert eDataType != null;
DataType pivotElement = converter.getCreated(DataType.class, eDataType);
if (pivotElement == null) {
return this;
}
doCompareToOperation(pivotElement, eDataType);
return pivotElement;
}
@Override
public Object caseEEnum(EEnum eEnum) {
return null; // Drop through to caseEDataType
}
@Override
public Object caseEOperation(EOperation eOperation) {
assert eOperation != null;
if (converter.isInvariant(eOperation)) {
Constraint asConstraint = converter.getCreated(Constraint.class, eOperation);
if (asConstraint == null) {
return this;
}
EAnnotation redefinesAnnotation = eOperation.getEAnnotation(PivotConstantsInternal.REDEFINES_ANNOTATION_SOURCE);
if (redefinesAnnotation != null) {
for (EObject eReference : redefinesAnnotation.getReferences()) {
if ((eReference != null) && checkProxy(eReference)) {
NamedElement redefinedConstraint = converter.getCreated(NamedElement.class, eReference);
if (redefinedConstraint instanceof Constraint) {
asConstraint.getRedefinedConstraints().add((Constraint)redefinedConstraint);
}
}
}
}
return asConstraint;
}
else {
TypedElement pivotElement = caseETypedElement(eOperation);
// if (pivotElement == oclInvalidProperty) {
if (!(pivotElement instanceof Operation)) {
return this;
}
Operation asOperation = (Operation)pivotElement;
EAnnotation redefinesAnnotation = eOperation.getEAnnotation(PivotConstantsInternal.REDEFINES_ANNOTATION_SOURCE);
if (redefinesAnnotation != null) {
for (EObject eReference : redefinesAnnotation.getReferences()) {
if ((eReference != null) && checkProxy(eReference)) {
NamedElement redefinedOperation = converter.getCreated(NamedElement.class, eReference);
if (redefinedOperation instanceof Operation) {
asOperation.getRedefinedOperations().add((Operation)redefinedOperation);
}
}
}
}
doSwitchAll(Type.class, ClassUtil.<Type>nullFree(asOperation.getRaisedExceptions()), eOperation.getEGenericExceptions());
return asOperation;
}
}
@Override
public Object caseEReference(EReference eReference) {
assert eReference != null;
// Property pivotElement = converter.getCreated(Property.class, eObject);
Property asProperty = caseEStructuralFeature(eReference);
if (asProperty == oclInvalidProperty) {
return this;
}
doSwitchAll(Property.class, ClassUtil.<Property>nullFree(asProperty.getKeys()), eReference.getEKeys());
Property oppositeProperty = null;
EReference eOpposite = eReference.getEOpposite();
if (eOpposite != null) {
oppositeProperty = converter.getCreated(Property.class, eOpposite);
asProperty.setOpposite(oppositeProperty);
}
else {
OppositePropertyDetails oppositePropertyDetails = OppositePropertyDetails.createFromEReference(eReference);
if (oppositePropertyDetails != null) {
metamodelManager.createImplicitOppositeProperty(asProperty, oppositePropertyDetails.getName(),
oppositePropertyDetails.isOrdered(), oppositePropertyDetails.isUnique(),
oppositePropertyDetails.getLower(), oppositePropertyDetails.getUpper());
}
else {
asProperty.setOpposite(null);
}
}
// else if (eObject.eContainer() instanceof EClass) { // Skip annotation references
// metamodelManager.installPropertyDeclaration(pivotElement);
// }
return asProperty;
}
@Override
public @NonNull Property caseEStructuralFeature(EStructuralFeature eStructuralFeature) {
assert eStructuralFeature != null;
TypedElement pivotElement = caseETypedElement(eStructuralFeature);
if (pivotElement == oclInvalidProperty) {
return oclInvalidProperty;
}
Property asProperty = (Property)pivotElement;
EAnnotation redefinesAnnotation = eStructuralFeature.getEAnnotation(PivotConstantsInternal.REDEFINES_ANNOTATION_SOURCE);
if (redefinesAnnotation != null) {
for (EObject eReference : redefinesAnnotation.getReferences()) {
if ((eReference != null) && checkProxy(eReference)) {
Property redefinedProperty = converter.getCreated(Property.class, eReference);
asProperty.getRedefinedProperties().add(redefinedProperty);
}
}
}
EObject eContainer = eStructuralFeature.eContainer();
if (eContainer instanceof EAnnotation) {
EAnnotation duplicatesAnnotation = (EAnnotation) eContainer;
if (PivotConstantsInternal.DUPLICATES_ANNOTATION_SOURCE.equals(duplicatesAnnotation.getSource())) {
EAnnotation eAnnotation = duplicatesAnnotation.getEAnnotation(eStructuralFeature.getName());
if (eAnnotation != null) {
String newLowerBound = null;
Boolean newOrdered = null;
Boolean newUnique = null;
String newUpperBound = null;
Type newType = null;
boolean changedType = false;
EMap<String, String> details = eAnnotation.getDetails();
for (String key : details.keySet()) {
Object value = details.get(key);
if (value != null) {
if ("lowerBound".equals(key)) {
newLowerBound = value.toString();
changedType = true;
}
else if ("ordered".equals(key)) {
newOrdered = Boolean.valueOf(value.toString());
changedType = true;
}
else if ("unique".equals(key)) {
newUnique = Boolean.valueOf(value.toString());
changedType = true;
}
else if ("upperBound".equals(key)) {
newUpperBound = value.toString();
changedType = true;
}
else if ("eType".equals(key)) {
String[] path = value.toString().split("::");
EObject eRoot = EcoreUtil.getRootContainer(eStructuralFeature);
int iSize = path.length;
if ((iSize >= 2) && (eRoot instanceof EPackage)) {
EPackage ePackage = (EPackage)eRoot;
if (path[0].equals(ePackage.getName())) {
for (int i = 1; (ePackage != null) && (i < iSize-1); i++) {
ePackage = NameUtil.getENamedElement(ePackage.getESubpackages(), path[i]);
}
if (ePackage != null) {
EClassifier eClassifier = NameUtil.getENamedElement(ePackage.getEClassifiers(), path[iSize-1]);
if (eClassifier != null) {
newType = converter.getASType(eClassifier);
changedType = true;
}
}
}
}
}
}
}
if (changedType) {
IntegerValue oldLowerValue;
boolean oldOrdered;
boolean oldUnique;
UnlimitedNaturalValue oldUpperValue;
Type oldType = asProperty.getType();
if (oldType instanceof CollectionType) {
CollectionType oldCollectionType = (CollectionType)oldType;
oldType = oldCollectionType.getElementType();
oldLowerValue = oldCollectionType.getLowerValue();
oldOrdered = oldCollectionType.isOrdered();
oldUnique = oldCollectionType.isUnique();
oldUpperValue = oldCollectionType.getUpperValue();
}
else {
oldLowerValue = asProperty.isIsRequired() ? ValueUtil.ONE_VALUE : ValueUtil.ZERO_VALUE;
oldOrdered = false;
oldUnique = false;
oldUpperValue = ValueUtil.UNLIMITED_ONE_VALUE;
}
boolean isOrdered = newOrdered != null ? newOrdered.booleanValue() : oldOrdered;
IntegerValue lowerValue = newLowerBound != null ? ValueUtil.integerValueOf(newLowerBound) : oldLowerValue;
boolean isUnique = newUnique != null ? newUnique.booleanValue() : oldUnique;
UnlimitedNaturalValue upperValue = newUpperBound != null ? ValueUtil.unlimitedNaturalValueOf(newUpperBound) : oldUpperValue;
Type type = newType != null ? newType : oldType;
boolean isRequired;
Type pivotType;
if (type != null) {
pivotType = type;
if (((NumberValue)upperValue).equals(ValueUtil.ONE_VALUE)) {
isRequired = lowerValue.equals(ValueUtil.ONE_VALUE);
}
else {
isRequired = true;
pivotType = metamodelManager.getCollectionType(isOrdered, isUnique, pivotType, false, lowerValue, upperValue);
}
}
else {
isRequired = false;
pivotType = standardLibrary.getOclVoidType();
}
asProperty.setType(pivotType);
asProperty.setIsRequired(isRequired);
}
}
}
}
// Object boxedDefaultValue = null;
String defaultValueLiteral = null;
if (eStructuralFeature.eIsSet(EcorePackage.Literals.ESTRUCTURAL_FEATURE__DEFAULT_VALUE_LITERAL)) {
defaultValueLiteral = eStructuralFeature.getDefaultValueLiteral();
/* EClassifier eType = eObject.getEType();
if (eType instanceof EEnum) {
EEnum eEnum = (EEnum)eType;
EEnumLiteral unboxedValue = eEnum.getEEnumLiteral(defaultValueLiteral);
if (unboxedValue != null) {
boxedDefaultValue = metamodelManager.getPivotOfEcore(EnumerationLiteral.class, unboxedValue);
}
else {
// converter.addError("Unknown enumeration literal");
}
}
else if (eType instanceof EDataType) {
EDataType eDataType = (EDataType)eType;
EPackage ePackage = eDataType.getEPackage();
if (PivotPackage.eNS_URI.equals(ePackage.getNsURI()) && !(ePackage instanceof PivotPackage)) { // Occurs when ConstraintMerger using dynamic Pivot
ePackage = PivotPackage.eINSTANCE;
eDataType = (EDataType) ePackage.getEClassifier(eDataType.getName());
}
EFactory eFactoryInstance = ePackage.getEFactoryInstance();
boxedDefaultValue = eFactoryInstance.createFromString(eDataType, defaultValueLiteral);
}
else {
URI uri = URI.createURI(defaultValueLiteral);
EObject defaultEObject = metamodelManager.getExternalResourceSet().getEObject(uri, false);
if (defaultEObject instanceof Element) {
boxedDefaultValue = defaultEObject;
}
else {
boxedDefaultValue = defaultEObject;
}
} */
}
asProperty.setDefaultValueString(defaultValueLiteral);
return asProperty;
}
@Override
public @NonNull TypedElement caseETypedElement(ETypedElement eTypedElement) {
assert eTypedElement != null;
TypedElement pivotElement = converter.getCreated(TypedElement.class, eTypedElement);
if (pivotElement == null) {
return oclInvalidProperty;
}
boolean isRequired;
Type pivotType;
EGenericType eType = eTypedElement.getEGenericType();
if (eType != null) {
EClassifier eClassifier = eType.getEClassifier();
int lower = eTypedElement.getLowerBound();
int upper = eTypedElement.getUpperBound();
if ((lower == 0) && (upper == -1) && converter.isEcoreOnlyEntryClass(eClassifier)) {
pivotType = converter.getCreated(Type.class, eType);
assert converter.isEntryClass(eClassifier);
assert pivotType == null;
assert eClassifier != null;
isRequired = true;
pivotType = getEcoreOnlyEntryClassMapType((EClass)eClassifier);
}
else {
pivotType = converter.getASType(eType);
assert pivotType != null;
if (upper == 1) {
if (lower == 0) {
if (converter.cannotBeOptional(eTypedElement)) {
lower = 1;
Ecore2AS.NOT_OPTIONAL.println(NameUtil.qualifiedNameFor(eTypedElement) + " converted to not-optional");
}
else {
if (eClassifier instanceof EDataType) {
Class<?> instanceClass = ((EDataType)eClassifier).getInstanceClass();
if ((instanceClass == Boolean.class) && (pivotType.getESObject() == EcorePackage.Literals.EBOOLEAN_OBJECT)) {
pivotType = standardLibrary.getBooleanType(); // Correct Ecore's BooleanObject but not UML's BooleanObject
}
}
}
}
isRequired = lower == 1;
}
else {
isRequired = true;
if (converter.isEntryClass(eClassifier)) {
Iterable<@NonNull Property> ownedProperties = PivotUtil.getOwnedProperties((org.eclipse.ocl.pivot.Class)pivotType);
Property keyProperty = ClassUtil.nonNullState(NameUtil.getNameable(ownedProperties, "key"));
Property valueProperty = ClassUtil.nonNullState(NameUtil.getNameable(ownedProperties, "value"));
if (keyProperty.getType() == null) {
return oclInvalidProperty; // Retry later once type defined
}
if (valueProperty.getType() == null) {
return oclInvalidProperty; // Retry later once type defined
}
pivotType = metamodelManager.getMapType((org.eclipse.ocl.pivot.Class)pivotType);
}
else {
boolean isNullFree = Ecore2AS.isNullFree(eTypedElement);
boolean isOrdered = eTypedElement.isOrdered();
boolean isUnique = eTypedElement.isUnique();
IntegerValue lowerValue = ValueUtil.integerValueOf(lower);
UnlimitedNaturalValue upperValue = upper != -1 ? ValueUtil.unlimitedNaturalValueOf(upper) : ValueUtil.UNLIMITED_VALUE;
pivotType = metamodelManager.getCollectionType(isOrdered, isUnique, pivotType, isNullFree, lowerValue, upperValue);
}
}
}
}
else {
isRequired = false;
pivotType = standardLibrary.getOclVoidType();
}
pivotElement.setType(pivotType);
pivotElement.setIsRequired(isRequired);
return pivotElement;
}
@Override
public Object caseETypeParameter(ETypeParameter eTypeParameter) {
assert eTypeParameter != null;
TemplateParameter pivotElement = converter.getCreated(TemplateParameter.class, eTypeParameter);
if (pivotElement == null) {
return this;
}
doSwitchAll(org.eclipse.ocl.pivot.Class.class, ClassUtil.<org.eclipse.ocl.pivot.Class>nullFree(pivotElement.getConstrainingClasses()), eTypeParameter.getEBounds());
return pivotElement;
}
protected boolean checkProxy(@NonNull EObject eReference) { // BUG 457206 MARTE has unresolveable proxies
if (!eReference.eIsProxy()) {
return true;
}
converter.error("Unresolved proxy: " + EcoreUtil.getURI(eReference));
return false;
}
/**
* @since 1.17
*/
protected void doCompareToOperation(@NonNull DataType pivotElement, @NonNull EDataType eDataType) {
Class<?> instanceClass = eDataType.getInstanceClass();
if (instanceClass != null) {
try {
Method declaredMethod = instanceClass.getDeclaredMethod("compareTo", instanceClass);
if (declaredMethod != null) {
Operation operation = PivotFactory.eINSTANCE.createOperation();
operation.setName(LibraryConstants.COMPARE_TO);
operation.setImplementation(new JavaCompareToOperation(declaredMethod));
Parameter parameter = PivotFactory.eINSTANCE.createParameter();
parameter.setName("that");
parameter.setType(standardLibrary.getOclSelfType());
operation.getOwnedParameters().add(parameter);
operation.setType(standardLibrary.getIntegerType());
pivotElement.getOwnedOperations().add(operation);
pivotElement.getSuperClasses().add(standardLibrary.getOclComparableType());
}
} catch (Exception e) {
}
}
}
public Object doInPackageSwitch(EObject eObject) {
int classifierID = eObject.eClass().getClassifierID();
return doSwitch(classifierID, eObject);
}
public <T extends Element> void doSwitchAll(Class<T> pivotClass, Collection<T> pivotElements, List<? extends EObject> eObjects) {
@SuppressWarnings("null") @NonNull Class<T> pivotClass2 = pivotClass;
for (EObject eObject : eObjects) {
if ((eObject != null) && checkProxy(eObject)) {
T pivotElement = converter.getASElement(pivotClass2, eObject);
if (pivotElement != null) {
pivotElements.add(pivotElement);
}
}
}
}
/**
* @since 1.7
*/
protected @Nullable Type getEcoreOnlyEntryClassMapType(@NonNull EClass eClass) {
EStructuralFeature keyFeature = eClass.getEStructuralFeature("key");
EStructuralFeature valueFeature = eClass.getEStructuralFeature("value");
if (keyFeature == null) {
converter.error("Missing 'key' feature for map '" + eClass.getName() + "");
}
else if (valueFeature == null) {
converter.error("Missing 'value' feature for map '" + eClass.getName() + "");
}
else {
EGenericType keyGenericType = keyFeature.getEGenericType();
EGenericType valueGenericType = valueFeature.getEGenericType();
if (keyGenericType == null) {
converter.error("No 'key' EGenericType for map '" + eClass.getName() + "");
}
else if (valueGenericType == null) {
converter.error("No 'value' EGenericType for map '" + eClass.getName() + "");
}
else {
Map<@NonNull String, @NonNull Type> resolvedSpecializations = new HashMap<>();
Type keyType = converter.resolveType(resolvedSpecializations, keyGenericType);
Type valueType = converter.resolveType(resolvedSpecializations, valueGenericType);
if ((keyType != null) && (valueType != null)) {
boolean keysAreNullFree = keyFeature.isRequired();
boolean valuesAreNullFree = valueFeature.isRequired();
org.eclipse.ocl.pivot.Class mapMetatype = standardLibrary.getMapType();
return metamodelManager.getCompleteEnvironment().getMapType(mapMetatype, keyType, keysAreNullFree, valueType, valuesAreNullFree);
}
}
}
return null;
}
}