blob: 307fd689ba3b10c9ae306dbc8f16b31a97e074e5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 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.qvtd.pivot.qvtimperative.evaluation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.NavigationCallExp;
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.PrimitiveType;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.PropertyCallExp;
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.OperationId;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.library.LibraryFeature;
import org.eclipse.ocl.pivot.library.oclany.OclElementOclContainerProperty;
import org.eclipse.ocl.pivot.utilities.TreeIterable;
import org.eclipse.ocl.pivot.utilities.UniqueList;
import org.eclipse.qvtd.pivot.qvtimperative.AppendParameter;
import org.eclipse.qvtd.pivot.qvtimperative.EntryPoint;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.SetStatement;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
/**
* EntryPointAnalysis provides the pre-execution analysis of a particular transformation entry-point.
*/
public class EntryPointAnalysis
{
protected final @NonNull EntryPointsAnalysis entryPointsAnalysis;
protected final @NonNull EntryPoint entryPoint;
protected final @NonNull UniqueList<@NonNull Mapping> mappings = new UniqueList<>();
/**
* Set of all types for which allInstances() is invoked.
*/
private final @NonNull Set<@NonNull CompleteClass> allInstancesCompleteClasses = new HashSet<>();
/**
* Map from oppositePropertyCallExp to the cache index identifying the relevant un-navigable lookup cache.
*/
private final @NonNull Map<@NonNull OppositePropertyCallExp, @NonNull Integer> oppositePropertyCallExp2cacheIndex = new HashMap<>();
/**
* Mappings that have an isPolled input.
*/
private final @NonNull Set<@NonNull Mapping> hazardousMappings = new HashSet<>();
/**
* Map from mapping to the properties that it may access.
*/
private final @NonNull Map<@NonNull Mapping, @NonNull Set<@NonNull NavigationCallExp>> mapping2property = new HashMap<>();
/**
* Map from mapping to the properties that it may assign.
*/
private final @NonNull Map<@NonNull Mapping, @NonNull Set<@NonNull SetStatement>> mapping2propertyAssignments = new HashMap<>();
/**
* Map from operation to the properties that it may access.
*/
private final @NonNull Map<@NonNull Operation, @NonNull Set<@NonNull NavigationCallExp>> operation2property = new HashMap<>();
/**
* The SetStatements to each Property.
*/
private final @NonNull Map<@NonNull Property, @NonNull Set<@NonNull SetStatement>> property2propertyAssignments = new HashMap<>();
/**
* Map of all oclContainer() accesses.
*/
private final @NonNull Map<@NonNull Type, @NonNull List<@NonNull Type>> parentClass2childClasses = new HashMap<>();
public EntryPointAnalysis(@NonNull EntryPointsAnalysis entryPointsAnalysis, @NonNull EntryPoint entryPoint) {
this.entryPointsAnalysis = entryPointsAnalysis;
this.entryPoint = entryPoint;
mappings.add(entryPoint);
}
private void addAllInstancesClass(@NonNull TypedElement asExpression) {
Type asType = asExpression instanceof OCLExpression ? ((OCLExpression)asExpression).getTypeValue() : null;
if (asType == null) {
asType = asExpression.getType();
}
if (asType instanceof org.eclipse.ocl.pivot.Class) {
assert !(asType instanceof PrimitiveType);
assert !(asType instanceof CollectionType);
CompleteClass completeClass = entryPointsAnalysis.environmentFactory.getCompleteModel().getCompleteClass(asType);
allInstancesCompleteClasses.add(completeClass);
}
}
public boolean addMapping(@NonNull Mapping iMapping) {
return mappings.add(iMapping);
}
public void analyze() {
//
// First pass
// - identify all allInstances() source types
// - identify all SetStatements
// - identify all assigned PropertyCallExp and allocate a cacheIndex
//
List<@NonNull SetStatement> setStatements = new ArrayList<>();
Type oclElementType = entryPointsAnalysis.environmentFactory.getStandardLibrary().getOclElementType();
Type modelType = entryPointsAnalysis.environmentFactory.getStandardLibrary().getLibraryType("Model");
OperationId allInstancesOperationId = oclElementType.getTypeId().getOperationId(0, "allInstances", IdManager.getParametersId());
OperationId objectsOfKindOperationId = modelType.getTypeId().getOperationId(1, "objectsOfKind", IdManager.getParametersId(TypeId.T_1));
OperationId objectsOfTypeOperationId = modelType.getTypeId().getOperationId(1, "objectsOfType", IdManager.getParametersId(TypeId.T_1));
for (@NonNull Mapping iMapping : getMappings()) {
for (EObject eObject : new TreeIterable(iMapping, true)) {
if (eObject instanceof Mapping) {
Mapping mapping = (Mapping)eObject;
analyzeMappingPropertyAccesses(mapping);
analyzeMappingSetStatements(mapping);
if (QVTimperativeUtil.isObserver(mapping)) {
hazardousMappings.add(mapping);
}
}
else if (eObject instanceof OppositePropertyCallExp) {
OppositePropertyCallExp oppositePropertyCallExp = (OppositePropertyCallExp)eObject;
Property navigableProperty = oppositePropertyCallExp.getReferredProperty();
if ((navigableProperty != null) && !navigableProperty.isIsComposite()) {
int cacheIndex = entryPointsAnalysis.allocateCacheIndex(oppositePropertyCallExp.getOwnedSource(), navigableProperty);
oppositePropertyCallExp2cacheIndex.put(oppositePropertyCallExp, cacheIndex);
}
}
else if (eObject instanceof SetStatement) {
setStatements.add((SetStatement)eObject);
}
else if (eObject instanceof AppendParameter) {
Mapping mapping = QVTimperativeUtil.getContainingMapping(eObject);
if (entryPoint == mapping) {
addAllInstancesClass((AppendParameter)eObject);
}
}
else if (eObject instanceof OperationCallExp) {
OperationCallExp operationCallExp = (OperationCallExp)eObject;
Operation referredOperation = operationCallExp.getReferredOperation();
if (referredOperation != null) {
OperationId operationId = referredOperation.getOperationId();
if (operationId == allInstancesOperationId) {
OCLExpression source = operationCallExp.getOwnedSource();
if (source != null) {
addAllInstancesClass(source);
}
}
else if ((operationId == objectsOfKindOperationId) || (operationId == objectsOfTypeOperationId)) {
OCLExpression argument = operationCallExp.getOwnedArguments().get(0);
if (argument != null) {
addAllInstancesClass(argument);
}
}
}
}
/* else if (eObject instanceof OppositePropertyCallExp) {
OppositePropertyCallExp oppositePropertyCallExp = (OppositePropertyCallExp)eObject;
Property referredProperty = oppositePropertyCallExp.getReferredProperty();
if (referredProperty != null) {
getOppositeCacheIndex(referredProperty);
}
} */
}
//
// Second pass
// - install cacheIndex allocated to MiddlePropertyCallExp in each MiddleSetStatement
//
entryPointsAnalysis.analyzeStatements(setStatements);
//
analyzeProperties();
}
}
private @NonNull Set<@NonNull NavigationCallExp> analyzeMappingPropertyAccesses(@NonNull Mapping mapping) {
Set<@NonNull NavigationCallExp> accessedProperties = mapping2property.get(mapping);
if (accessedProperties != null) {
return accessedProperties;
}
accessedProperties = new HashSet<>();
mapping2property.put(mapping, accessedProperties);
analyzeTree(accessedProperties, mapping.eAllContents());
return accessedProperties;
}
private @NonNull Set<@NonNull SetStatement> analyzeMappingSetStatements(@NonNull Mapping mapping) {
Set<@NonNull SetStatement> assignedProperties = mapping2propertyAssignments.get(mapping);
if (assignedProperties == null) {
assignedProperties = new HashSet<>();
mapping2propertyAssignments.put(mapping, assignedProperties);
}
for (TreeIterator<EObject> treeIterator = mapping.eAllContents(); treeIterator.hasNext(); ) {
EObject eObject = treeIterator.next();
if (eObject instanceof SetStatement) {
assignedProperties.add((SetStatement) eObject);
}
}
return assignedProperties;
}
private @NonNull Set<@NonNull NavigationCallExp> analyzeOperation(@NonNull Operation operation) {
Set<@NonNull NavigationCallExp> operationProperties = operation2property.get(operation);
if (operationProperties != null) {
return operationProperties;
}
operationProperties = new HashSet<>();
operation2property.put(operation, operationProperties);
analyzeTree(operationProperties, operation.eAllContents());
return operationProperties;
}
private void analyzeProperties() {
for (@NonNull Set<@NonNull SetStatement> propertyAssignments : mapping2propertyAssignments.values()) {
for (@NonNull SetStatement propertyAssignment : propertyAssignments) {
Property property = propertyAssignment.getTargetProperty();
assert property != null;
Set<@NonNull SetStatement> assignments = property2propertyAssignments.get(property);
if (assignments == null) {
assignments = new HashSet<>();
property2propertyAssignments.put(property, assignments);
}
assignments.add(propertyAssignment);
}
}
}
protected void analyzeTree(@NonNull Set<NavigationCallExp> properties, /*@NonNull*/ TreeIterator<EObject> treeIterator) {
while (treeIterator.hasNext()) {
EObject eObject = treeIterator.next();
if (eObject instanceof OperationCallExp) {
Operation referredOperation = ((OperationCallExp)eObject).getReferredOperation();
if (referredOperation != null) {
properties.addAll(analyzeOperation(referredOperation));
}
}
else if (eObject instanceof PropertyCallExp) {
PropertyCallExp asPropertyCallExp = (PropertyCallExp)eObject;
Property referredProperty = asPropertyCallExp.getReferredProperty();
if (referredProperty != null) {
LibraryFeature implementation = referredProperty.getImplementation();
if (implementation instanceof OclElementOclContainerProperty) {
Type childType = asPropertyCallExp.getOwnedSource().getType();
assert childType != null;
Type parentType = asPropertyCallExp.getType();
assert parentType != null;
List<@NonNull Type> childClasses = parentClass2childClasses .get(parentType);
if (childClasses == null) {
childClasses = new ArrayList<>();
parentClass2childClasses.put(parentType, childClasses);
}
if (!childClasses.contains(childType)) {
childClasses.add(childType);
}
}
else {
properties.add(asPropertyCallExp);
}
}
}
else if (eObject instanceof OppositePropertyCallExp) {
OppositePropertyCallExp asOppositePropertyCallExp = (OppositePropertyCallExp)eObject;
Property referredOppositeProperty = asOppositePropertyCallExp.getReferredProperty();
if (referredOppositeProperty != null) {
Property referredProperty = referredOppositeProperty.getOpposite();
if (referredProperty != null) {
properties.add(asOppositePropertyCallExp);
}
}
}
}
}
public @NonNull Set<@NonNull CompleteClass> getAllInstancesCompleteClasses() {
return allInstancesCompleteClasses;
}
public @Nullable Integer getCacheIndex(@NonNull OppositePropertyCallExp oppositePropertyCallExp) {
return oppositePropertyCallExp2cacheIndex.get(oppositePropertyCallExp);
}
public @NonNull EntryPoint getEntryPoint() {
return entryPoint;
}
public @NonNull EntryPointsAnalysis getEntryPointsAnalysis() {
return entryPointsAnalysis;
}
// public @NonNull Set<@NonNull Mapping> getHazardousMappings() {
// return hazardousMappings;
// }
// public @NonNull Map<@NonNull Mapping, @NonNull Set<@NonNull NavigationCallExp>> getMapping2Property() {
// return mapping2property;
// }
// public @NonNull Map<Mapping, @NonNull Set<@NonNull SetStatement>> getMapping2SetStatements() {
// return mapping2propertyAssignments;
// }
public @NonNull List<@NonNull Mapping> getMappings() {
return mappings;
}
public boolean isHazardous(@NonNull Mapping mapping) {
return hazardousMappings.contains(mapping);
}
}