blob: 54184d1ecec064f5beab60af5b23002e4b104e5f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 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
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.manager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CallExp;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.Feature;
import org.eclipse.ocl.pivot.IterateExp;
import org.eclipse.ocl.pivot.Iteration;
import org.eclipse.ocl.pivot.IteratorExp;
import org.eclipse.ocl.pivot.LambdaType;
import org.eclipse.ocl.pivot.MapType;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.PrimitiveType;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.PropertyCallExp;
import org.eclipse.ocl.pivot.SelfType;
import org.eclipse.ocl.pivot.TemplateBinding;
import org.eclipse.ocl.pivot.TemplateParameter;
import org.eclipse.ocl.pivot.TemplateParameterSubstitution;
import org.eclipse.ocl.pivot.TemplateSignature;
import org.eclipse.ocl.pivot.TemplateableElement;
import org.eclipse.ocl.pivot.TupleType;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.ids.IdManager;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.ids.TemplateParameterId;
import org.eclipse.ocl.pivot.ids.TuplePartId;
import org.eclipse.ocl.pivot.ids.TupleTypeId;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal.EnvironmentFactoryInternalExtension;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.library.LibraryIterationOrOperation;
import org.eclipse.ocl.pivot.resource.ASResource;
import org.eclipse.ocl.pivot.util.AbstractExtendingVisitor;
import org.eclipse.ocl.pivot.util.Visitable;
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.values.TemplateParameterSubstitutions;
/**
* A TemplateParameterSubstitutionVisitor traverses a CallExp to identify the formal/actual TemplateParameterSubstitutions
* associated with that CallExp. This creates a mapping from each formal template parameter to a list of actual types that
* correspond. This mapping can then be used to create new specializations.
* <p>
* The visitor should be constructed with a MetamodelManager in case any synthetic types need contructing, and the identity
* of the self type incase one of the substitutions uses OclSelf.
*/
public class TemplateParameterSubstitutionVisitor extends AbstractExtendingVisitor<Object, Map<Integer, Type>> implements TemplateParameterSubstitutions
{
public static @NonNull TemplateParameterSubstitutions createBindings(@NonNull EnvironmentFactoryInternal environmentFactory, @NonNull Type formalType, @NonNull Type actualType) {
TemplateParameterSubstitutionVisitor visitor = createVisitor(actualType, environmentFactory, null, null);
visitor.analyzeType(formalType, actualType);
return visitor;
}
public static @NonNull TemplateParameterSubstitutions createBindings(@NonNull EnvironmentFactoryInternal environmentFactory, @Nullable Type sourceType, @Nullable Type sourceTypeValue, @NonNull Operation candidateOperation) {
TemplateParameterSubstitutionVisitor visitor = createVisitor(candidateOperation, environmentFactory, sourceType, sourceTypeValue);
visitor.analyzeType(candidateOperation.getOwningClass(), sourceType);
for (EObject eObject = candidateOperation; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof TemplateableElement) {
TemplateSignature templateSignature = ((TemplateableElement)eObject).getOwnedSignature();
if (templateSignature != null) {
List<TemplateParameter> templateParameters = templateSignature.getOwnedParameters();
int iSize = templateParameters.size();
if (iSize > 0) {
return visitor;
}
}
}
}
return TemplateParameterSubstitutions.EMPTY;
}
protected static @NonNull TemplateParameterSubstitutionVisitor createVisitor(@NonNull EObject eObject, @NonNull EnvironmentFactoryInternal environmentFactory, @Nullable Type selfType, @Nullable Type selfTypeValue) {
Resource resource = eObject.eResource();
if (environmentFactory instanceof EnvironmentFactoryInternalExtension) {
return ((EnvironmentFactoryInternalExtension)environmentFactory).createTemplateParameterSubstitutionVisitor(selfType, selfTypeValue);
}
else if (resource instanceof ASResource) { // This used to be thefirst choice; now it should never happen
return ((ASResource)resource).getASResourceFactory().createTemplateParameterSubstitutionVisitor(environmentFactory, selfType, selfTypeValue);
}
else { // This too should never happen
return new TemplateParameterSubstitutionVisitor(environmentFactory, selfType, selfTypeValue);
}
}
/**
* Return the specialized form of type analyzing expr to determine the formal to actual parameter mappings under the
* supervision of a metamodelManager and using selfType as the value of OclSelf.
*/
public static @NonNull Type specializeType(@NonNull Type type, @NonNull CallExp callExp, @NonNull EnvironmentFactoryInternal environmentFactory, @Nullable Type selfType, @Nullable Type selfTypeValue) {
TemplateParameterSubstitutionVisitor visitor = createVisitor(callExp, environmentFactory, selfType, selfTypeValue);
visitor.exclude(callExp);
visitor.visit(callExp);
return visitor.specializeType(type);
}
private final @NonNull EnvironmentFactoryInternal environmentFactory;
private final @Nullable Type selfType;
private final @Nullable Type selfTypeValue;
private @Nullable TypedElement excludedTarget = null;
/**
* Internal variable used to pass the actual corresponding to the visited formal.
*/
private Element actual;
public TemplateParameterSubstitutionVisitor(@NonNull EnvironmentFactoryInternal environmentFactory, @Nullable Type selfType, @Nullable Type selfTypeValue) {
super(new HashMap<Integer, Type>());
this.environmentFactory = environmentFactory;
this.selfType = selfType;
this.selfTypeValue = selfTypeValue;
}
protected void analyzeFeature(@Nullable Feature formalFeature, @Nullable TypedElement actualElement) {
if ((formalFeature != null) && (actualElement != null)) {
Type formalType = formalFeature.getOwningClass();
Type actualType = actualElement.getType();
analyzeType(formalType, actualType);
}
}
protected void analyzeType(@Nullable Type newFormal, @Nullable Element newActual) {
if ((newFormal != null) && (newActual != null)) {
Element oldActual = actual;
try {
actual = newActual;
newFormal.accept(this);
} finally {
actual = oldActual;
}
}
}
protected void analyzeTypedElement(@Nullable TypedElement newFormal, @Nullable TypedElement newActual) {
if ((newFormal != null) && (newActual != null) && (newActual != excludedTarget)) {
Element oldActual = actual;
try {
actual = newActual;
newFormal.accept(this);
} finally {
actual = oldActual;
}
}
}
protected void analyzeTypedElements(@NonNull List<? extends TypedElement> formalElements, @Nullable List<? extends TypedElement> actualElements) {
if (actualElements != null) {
int iMax = Math.min(formalElements.size(), actualElements.size());
for (int i = 0; i < iMax; i++) {
TypedElement formalElement = formalElements.get(i);
TypedElement actualElement = actualElements.get(i);
analyzeTypedElement(formalElement, actualElement);
}
}
}
protected void analyzeTypes(@NonNull List<? extends Type> formalElements, @NonNull List<? extends Type> actualElements) {
int iMax = Math.min(formalElements.size(), actualElements.size());
for (int i = 0; i < iMax; i++) {
analyzeType(formalElements.get(i), actualElements.get(i));
}
}
/**
* Exclude the current typed element which may have a stale type.
*/
private void exclude(@NonNull TypedElement typedElement) {
assert excludedTarget == null;
excludedTarget = typedElement;
}
@Override
public @Nullable Type get(@Nullable TemplateParameter templateParameter) {
if (templateParameter == null) {
return null;
}
return context.get(templateParameter.getTemplateParameterId().getIndex());
}
@Deprecated /* @deprecated all functionality moved to LibraryOperation */
protected @Nullable TemplateParameterSubstitutionHelper getHelper(@NonNull Operation operation) {
return null;
}
protected @NonNull TupleType getSpecializedTupleType(@NonNull TupleType type) {
PivotMetamodelManager metamodelManager = environmentFactory.getMetamodelManager();
TupleType specializedTupleType = type;
Map<String, Type> resolutions = null;
List<Property> parts = specializedTupleType.getOwnedProperties();
for (Property part : parts) {
if (part != null) {
Type propertyType = PivotUtilInternal.getType(part);
Type resolvedPropertyType = specializeType(propertyType);
if (resolvedPropertyType != propertyType) {
if (resolutions == null) {
resolutions = new HashMap<String, Type>();
}
resolutions.put(NameUtil.getSafeName(part), resolvedPropertyType);
}
}
}
if (resolutions != null) {
List<@NonNull TuplePartId> partIds = new ArrayList<@NonNull TuplePartId>(parts.size());
for (int i = 0; i < parts.size(); i++) {
@SuppressWarnings("null") @NonNull Property part = parts.get(i);
String partName = NameUtil.getSafeName(part);
Type resolvedPropertyType = resolutions.get(partName);
TypeId partTypeId = resolvedPropertyType != null ? resolvedPropertyType.getTypeId() : part.getTypeId();
TuplePartId tuplePartId = IdManager.getTuplePartId(i, partName, partTypeId);
partIds.add(tuplePartId);
}
TupleTypeId tupleTypeId = IdManager.getTupleTypeId(ClassUtil.nonNullModel(type.getName()), partIds);
specializedTupleType = metamodelManager.getCompleteModel().getTupleManager().getTupleType(metamodelManager.getEnvironmentFactory().getIdResolver(), tupleTypeId);
return specializedTupleType;
}
else {
Map<@NonNull String, @NonNull Type> partMap = new HashMap<>();
for (TypedElement part : type.getOwnedProperties()) {
Type type1 = part.getType();
if (type1 != null) {
Type type2 = metamodelManager.getPrimaryType(type1);
Type type3 = specializeType(type2);
partMap.put(PivotUtil.getName(part), type3);
}
}
return metamodelManager.getCompleteModel().getTupleManager().getTupleType(NameUtil.getSafeName(type), partMap);
}
}
@Override
public boolean isEmpty() {
return context.isEmpty();
}
public void put(int templateParameterIndex, @Nullable Type actualType) {
context.put(templateParameterIndex, actualType);
}
@Override
public @NonNull Type put(@NonNull TemplateParameter formalTemplateParameter, @NonNull Type actualType) {
TemplateParameterId elementId = formalTemplateParameter.getTemplateParameterId();
int index = elementId.getIndex();
Type oldType = context.get(index);
if (oldType != null) {
IdResolver idResolver = environmentFactory.getIdResolver();
Type commonType = oldType.getCommonType(idResolver, actualType);
Type bestType = environmentFactory.getMetamodelManager().getPrimaryType(commonType);
if (bestType != oldType) {
context.put(index, bestType);
}
return bestType;
}
else {
context.put(index, actualType);
return actualType;
}
}
public @NonNull Type specializeType(@NonNull Type type) {
PivotMetamodelManager metamodelManager = environmentFactory.getMetamodelManager();
TemplateParameter asTemplateParameter = type.isTemplateParameter();
if (asTemplateParameter != null) {
int index = asTemplateParameter.getTemplateParameterId().getIndex();
Type actualType = context.get(index);
return actualType != null ? actualType : type;
}
if (type instanceof SelfType) {
return ClassUtil.nonNullState(selfTypeValue != null ? selfTypeValue : selfType != null ? selfType : type);
}
else if (type instanceof CollectionType) {
CollectionType collectionType = (CollectionType)type;
Type elementType = ClassUtil.nonNullModel(collectionType.getElementType());
Type specializedElementType = specializeType(elementType);
CollectionType unspecializedCollectionType = PivotUtil.getUnspecializedTemplateableElement(collectionType);
return metamodelManager.getCompleteEnvironment().getCollectionType(unspecializedCollectionType, specializedElementType, collectionType.isIsNullFree(), collectionType.getLowerValue(), collectionType.getUpperValue());
}
else if (type instanceof TupleType) {
return getSpecializedTupleType((TupleType) type);
}
else if (type instanceof LambdaType) {
LambdaType lambdaType = (LambdaType)type;
String typeName = ClassUtil.nonNullModel(lambdaType.getName());
Type specializedContextType = specializeType(ClassUtil.nonNullModel(lambdaType.getContextType()));
List<@NonNull Type> specializedParameterTypes = new ArrayList<@NonNull Type>();
for (Type parameterType : lambdaType.getParameterType()) {
if (parameterType != null) {
specializedParameterTypes.add(specializeType(parameterType));
}
}
Type specializedResultType = specializeType(ClassUtil.nonNullModel(lambdaType.getResultType()));
return metamodelManager.getCompleteModel().getLambdaType(typeName, specializedContextType, specializedParameterTypes, specializedResultType);
}
else {
//
// Get the bindings of the type.
//
org.eclipse.ocl.pivot.Class partiallySpecializedType = (org.eclipse.ocl.pivot.Class)type;
org.eclipse.ocl.pivot.Class unspecializedType = PivotUtil.getUnspecializedTemplateableElement(partiallySpecializedType);
List<TemplateBinding> ownedTemplateBindings = partiallySpecializedType.getOwnedBindings();
if (ownedTemplateBindings.size() > 0) {
// boolean hasActual = false;
List<@NonNull Type> templateArguments = new ArrayList<@NonNull Type>();
for (TemplateBinding ownedTemplateBinding : ownedTemplateBindings) {
for (TemplateParameterSubstitution ownedTemplateParameterSubstitution : ownedTemplateBinding.getOwnedSubstitutions()) {
Type actualType = ownedTemplateParameterSubstitution.getActual();
if (actualType != null) {
actualType = specializeType(actualType);
// hasActual = true;
}
else {
TemplateParameter formalParameter = ownedTemplateParameterSubstitution.getFormal();
if (formalParameter != null) {
actualType = PivotUtil.basicGetLowerBound(formalParameter);
}
}
if (actualType == null) {
actualType = metamodelManager.getStandardLibrary().getOclAnyType();
}
templateArguments.add(actualType);
}
}
// if (hasActual) {
return metamodelManager.getLibraryType(unspecializedType, templateArguments);
// }
// else {
// return unspecializedType;
// }
}
TemplateSignature ownedTemplateSignature = partiallySpecializedType.getOwnedSignature();
if (ownedTemplateSignature != null) {
List<@NonNull Type> templateArguments = new ArrayList<@NonNull Type>();
for (@SuppressWarnings("null")@NonNull TemplateParameter ownedTemplateParameter : ownedTemplateSignature.getOwnedParameters()) {
Type actualType = specializeType(ownedTemplateParameter);
templateArguments.add(actualType);
}
return metamodelManager.getLibraryType(unspecializedType, templateArguments);
}
}
return type;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("{");
boolean isFirst = true;
List<Integer> keys = new ArrayList<Integer>(context.keySet());
Collections.sort(keys);
for (Integer index : keys) {
if (!isFirst) {
s.append("\n");
}
s.append(index + " => " + context.get(index));
isFirst = false;
}
s.append("}");
return s.toString();
}
@Override
public String visiting(@NonNull Visitable visitable) {
throw new UnsupportedOperationException("Unsupported " + getClass().getSimpleName() + " " + visitable.getClass().getSimpleName());
}
@Override
public @Nullable Object visitClass(org.eclipse.ocl.pivot.@NonNull Class object) {
for (TemplateBinding templateBinding : object.getOwnedBindings()) {
for (TemplateParameterSubstitution templateParameterSubstitution : templateBinding.getOwnedSubstitutions()) {
safeVisit(templateParameterSubstitution.getActual());
}
}
return null;
}
@Override
public @Nullable Object visitCollectionType(@NonNull CollectionType object) {
if (actual instanceof CollectionType) {
Type formalElementType = object.getElementType();
Type actualElementType = ((CollectionType)actual).getElementType();
analyzeType(formalElementType, actualElementType);
}
return null;
}
@Override
public @Nullable Object visitIterateExp(@NonNull IterateExp object) {
Iteration referredIteration = object.getReferredIteration();
analyzeTypedElement(referredIteration, object);
analyzeFeature(referredIteration, object.getOwnedSource());
analyzeTypedElements(referredIteration.getOwnedIterators(), object.getOwnedIterators());
// analyzeTypedElements(referredIteration.getOwnedCoIterators(), object.getOwnedCoIterators());
analyzeTypedElements(referredIteration.getOwnedAccumulators(), Collections.singletonList(object.getOwnedResult()));
analyzeTypedElements(referredIteration.getOwnedParameters(), Collections.singletonList(object.getOwnedBody()));
return null;
}
@Override
public @Nullable Object visitIteratorExp(@NonNull IteratorExp object) {
Iteration referredIteration = object.getReferredIteration();
analyzeTypedElement(referredIteration, object);
analyzeFeature(referredIteration, object.getOwnedSource());
analyzeTypedElements(referredIteration.getOwnedIterators(), object.getOwnedIterators());
// analyzeTypedElements(referredIteration.getOwnedCoIterators(), object.getOwnedCoIterators());
List<Parameter> formalElements = referredIteration.getOwnedParameters();
if (formalElements.size() > 0) {
OCLExpression actualElement = object.getOwnedBody();
Type actualType = actualElement.getType();
LibraryIterationOrOperation implementation = (LibraryIterationOrOperation) referredIteration.getImplementation();
if (implementation != null) { // Library classes have implementations, Complete OCL classes may be recursive
actualType = implementation.resolveBodyType(environmentFactory, object, actualType);
}
analyzeType(formalElements.get(0).getType(), actualType);
}
return null;
}
@Override
public @Nullable Object visitLambdaType(@NonNull LambdaType object) {
if (actual instanceof LambdaType) {
LambdaType actualLambdaType = (LambdaType)actual;
analyzeType(object.getContextType(), actualLambdaType.getContextType());
analyzeType(object.getResultType(), actualLambdaType.getResultType());
analyzeTypes(object.getParameterType(), actualLambdaType.getParameterType());
}
else {
analyzeType(object.getResultType(), actual);
}
return null;
}
@Override
public @Nullable Object visitMapType(@NonNull MapType object) {
if (actual instanceof MapType) {
Type formalKeyType = object.getKeyType();
Type formalValueType = object.getValueType();
MapType mapType = (MapType)actual;
Type actualKeyType = mapType.getKeyType();
Type actualValueType = mapType.getValueType();
analyzeType(formalKeyType, actualKeyType);
analyzeType(formalValueType, actualValueType);
}
return null;
}
@Override
public @Nullable Object visitOperationCallExp(@NonNull OperationCallExp object) {
Operation referredOperation = object.getReferredOperation();
// visit(referredOperation, object);
analyzeTypedElement(referredOperation, object);
OCLExpression source = object.getOwnedSource();
if (source != null) {
analyzeType(referredOperation.getOwningClass(), source.getType());
}
analyzeTypedElements(referredOperation.getOwnedParameters(), object.getOwnedArguments());
//
// FIXME More general processing for T2 < T1
//
LibraryIterationOrOperation implementation = (LibraryIterationOrOperation)referredOperation.getImplementation();
if (implementation != null) { // Library classes have implementations, Complete OCL classes may be recursive
implementation.resolveUnmodeledTemplateParameterSubstitutions(this, object);
}
return null;
}
@Override
public @Nullable Object visitOppositePropertyCallExp(@NonNull OppositePropertyCallExp object) {
Property referredOppositeProperty = object.getReferredProperty();
if (referredOppositeProperty != null) {
Property referredProperty = referredOppositeProperty.getOpposite();
if (referredProperty != null) {
analyzeFeature(referredProperty, object.getOwnedSource());
analyzeTypedElement(referredProperty, object);
}
}
return null;
}
@Override
public @Nullable Object visitParameter(@NonNull Parameter object) {
if ((object.isIsTypeof()) && actual instanceof OCLExpression) {
analyzeType(object.getType(), ((OCLExpression)actual).getTypeValue());
}
else {
super.visitParameter(object);
}
return null;
}
@Override
public @Nullable Object visitPrimitiveType(@NonNull PrimitiveType object) {
return null;
}
@Override
public @Nullable Object visitPropertyCallExp(@NonNull PropertyCallExp object) {
Property referredProperty = object.getReferredProperty();
if (referredProperty != null) {
OCLExpression source = object.getOwnedSource();
if (source != null) {
Type sourceType = source.getType();
analyzeType(referredProperty.getOwningClass(), sourceType);
}
analyzeTypedElement(referredProperty, object);
}
return null;
}
@Override
public @Nullable Object visitSelfType(@NonNull SelfType object) {
analyzeType(/*selfIsTypeof ? metamodelManager.getClassType() :*/ selfType, actual);
return null;
}
@Override
public @Nullable Object visitTemplateParameter(@NonNull TemplateParameter object) {
if (actual instanceof Type) {
put(object, (Type)actual);
}
return null;
}
@Override
public @Nullable Object visitTupleType(@NonNull TupleType object) {
if (actual instanceof TupleType) {
analyzeTypedElements(object.getOwnedProperties(), ((TupleType)actual).getOwnedProperties());
}
return null;
}
@Override
public @Nullable Object visitTypedElement(@NonNull TypedElement object) {
if (actual instanceof TypedElement) {
analyzeType(object.getType(), ((TypedElement)actual).getType());
}
return null;
}
}