blob: 532a9594301a98d403a43c419fd25a5e686267dc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 Willink Transformations 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:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvtm2qvts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.utilities.NameUtil.ToStringComparator;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.TreeIterable;
import org.eclipse.qvtd.pivot.qvtcore.Assignment;
import org.eclipse.qvtd.pivot.qvtcore.CorePattern;
import org.eclipse.qvtd.pivot.qvtcore.NavigationAssignment;
import org.eclipse.qvtd.pivot.qvtcore.RealizedVariable;
import org.eclipse.qvtd.pivot.qvtcore.VariableAssignment;
import org.eclipse.qvtd.pivot.qvtcore.utilities.QVTcoreUtil;
/**
* AssignmentSorter supports ordering the incoming Assignments so that they can be converted to QVTs form
* directly; reads before assigns ensuring that computational chains are not polluted by GREENs as a
* consequence of assignment of intermediate computations.
*/
public class AssignmentSorter
{
private static @Nullable Variable getTargetVariable(@NonNull NavigationAssignment navigationAssignment) {
OCLExpression slotExpression = navigationAssignment.getSlotExpression();
if (slotExpression instanceof VariableExp) {
VariableExp variableExp = (VariableExp)slotExpression;
Variable targetVariable = (Variable) variableExp.getReferredVariable();
assert targetVariable != null;
return targetVariable;
}
else {
return null; // FIXME Adolfo's folded middle uses complex casts lhs'
}
}
/**
* Nested Map for each known NavigationAssignment from each RealizedVariable to its Property to the NavigationAssignment.
* FIXME Not RealizedVariable because of Adolfo's folded middle
*/
protected final @NonNull Map<@NonNull Variable, @NonNull Map<@NonNull Property, @NonNull NavigationAssignment>> realizedVariable2property2navigationAssignment = new HashMap<>();
/**
* Nested Map for each known VariableAssignment from each Variable to the VariableAssignment.
*/
protected final @NonNull Map<@NonNull Variable, @NonNull VariableAssignment> variable2variableAssignment = new HashMap<>();
/**
* FIXME List of too-complex assignments that OCL2QVTm produces; just tack them on at the end.
*/
protected final @NonNull List<@NonNull Assignment> otherAssignments = new ArrayList<>();
/**
* Asd all assignments to the Set of assignments to be sorted.
*/
public void addAll(@NonNull Iterable<@NonNull Assignment> assignments) {
for (@NonNull Assignment assignment : assignments) {
if (assignment instanceof VariableAssignment) {
VariableAssignment variableAssignment = (VariableAssignment) assignment;
Variable targetVariable = variableAssignment.getTargetVariable();
if (targetVariable != null) {
VariableAssignment oldAssignment = variable2variableAssignment.put(targetVariable, variableAssignment);
assert oldAssignment == null;
}
}
else if (assignment instanceof NavigationAssignment) {
NavigationAssignment navigationAssignment = (NavigationAssignment) assignment;
Variable targetVariable = getTargetVariable(navigationAssignment);
if (targetVariable != null) {
Map<@NonNull Property, @NonNull NavigationAssignment> property2navigationAssignable = realizedVariable2property2navigationAssignment.get(targetVariable);
if (property2navigationAssignable == null) {
property2navigationAssignable = new HashMap<>();
realizedVariable2property2navigationAssignment.put(targetVariable, property2navigationAssignable);
}
Property targetProperty = QVTcoreUtil.getTargetProperty(navigationAssignment);
NavigationAssignment oldAssignment = property2navigationAssignable.put(targetProperty, navigationAssignment);
assert oldAssignment == null;
}
else {
otherAssignments.add(assignment);
}
}
else {
throw new IllegalStateException("Unknown Assignment class " + assignment.getClass().getName());
}
}
}
/**
* Return a Map from each target assignment to the source assignments that are used to compute the value of the target.
*/
protected @NonNull Map<@NonNull Assignment, Set<@NonNull Assignment>> gatherSourceAssignables(@NonNull Iterable<@NonNull Assignment> assignments) {
Map<@NonNull Assignment, Set<@NonNull Assignment>> targetAssignment2sourceAssignments = new HashMap<>();
for (@NonNull Assignment assignment : assignments) {
Set<@NonNull Assignment> assignmentReferences = new HashSet<>();
OCLExpression value = assignment.getValue();
assert value != null;
for (@NonNull EObject eObject : new TreeIterable(value, true)) {
if (eObject instanceof NavigationCallExp) {
NavigationCallExp navigationCallExp = (NavigationCallExp)eObject;
OCLExpression sourceExpression = navigationCallExp.getOwnedSource();
if (sourceExpression instanceof VariableExp) {
VariableDeclaration referredVariable = ((VariableExp)sourceExpression).getReferredVariable();
if (referredVariable instanceof RealizedVariable) {
Map<@NonNull Property, @NonNull NavigationAssignment> property2navigationAssignment = realizedVariable2property2navigationAssignment.get(referredVariable);
if (property2navigationAssignment != null) {
Property property = PivotUtil.getReferredProperty(navigationCallExp);
NavigationAssignment navigationAssignment = property2navigationAssignment.get(property);
if (navigationAssignment != null) {
assignmentReferences.add(navigationAssignment);
}
}
}
}
}
else if (eObject instanceof VariableExp) {
VariableExp variableExp = (VariableExp)eObject;
VariableDeclaration referredVariable = variableExp.getReferredVariable();
VariableAssignment variableAssignment = variable2variableAssignment.get(referredVariable);
if (variableAssignment != null) {
assignmentReferences.add(variableAssignment);
}
}
}
targetAssignment2sourceAssignments.put(assignment, assignmentReferences);
}
return targetAssignment2sourceAssignments;
}
protected void gatherSourceAssignables(@NonNull Map<@NonNull Assignment, Set<@NonNull Assignment>> targetAssignment2sourceAssignments, @NonNull Iterable<@NonNull ? extends Assignment> assignments) {
for (@NonNull Assignment assignment : assignments) {
Set<@NonNull Assignment> assignmentReferences = new HashSet<>();
OCLExpression value = assignment.getValue();
assert value != null;
for (@NonNull EObject eObject : new TreeIterable(value, true)) {
if (eObject instanceof NavigationCallExp) {
NavigationCallExp navigationCallExp = (NavigationCallExp)eObject;
OCLExpression sourceExpression = navigationCallExp.getOwnedSource();
if (sourceExpression instanceof VariableExp) {
VariableDeclaration referredVariable = ((VariableExp)sourceExpression).getReferredVariable();
if (referredVariable.eContainer() instanceof CorePattern) {
Map<@NonNull Property, @NonNull NavigationAssignment> property2navigationAssignment = realizedVariable2property2navigationAssignment.get(referredVariable);
if (property2navigationAssignment != null) {
Property property = PivotUtil.getReferredProperty(navigationCallExp);
NavigationAssignment navigationAssignment = property2navigationAssignment.get(property);
if (navigationAssignment != null) {
assignmentReferences.add(navigationAssignment);
}
}
}
}
}
else if (eObject instanceof VariableExp) {
VariableExp variableExp = (VariableExp)eObject;
VariableDeclaration referredVariable = variableExp.getReferredVariable();
VariableAssignment variableAssignment = variable2variableAssignment.get(referredVariable);
if (variableAssignment != null) {
assignmentReferences.add(variableAssignment);
}
}
}
targetAssignment2sourceAssignments.put(assignment, assignmentReferences);
}
}
/**
* Return the assignments in a less dependent-first stable order.
*
* Throws IllegalStateException if there is a cyclic dependency.
*/
public @NonNull Iterable<@NonNull Assignment> getSortedAssignments() {
Map<@NonNull Assignment, Set<@NonNull Assignment>> targetAssignment2sourceAssignments = new HashMap<>();
gatherSourceAssignables(targetAssignment2sourceAssignments, variable2variableAssignment.values());
for (@NonNull Map<@NonNull Property, @NonNull NavigationAssignment> property2navigationAssignment : realizedVariable2property2navigationAssignment.values()) {
gatherSourceAssignables(targetAssignment2sourceAssignments, property2navigationAssignment.values());
}
List<@NonNull Assignment> sortedAssignments = new ArrayList<>();
Set<@NonNull Assignment> targetAssignments = new HashSet<>(targetAssignment2sourceAssignments.keySet());
while (targetAssignments.size() > 0) {
List<@NonNull Assignment> resolvedAssignments = new ArrayList<>();
for (@NonNull Assignment targetAssignment : targetAssignments) {
Set<@NonNull Assignment> sourceAssignments = targetAssignment2sourceAssignments.get(targetAssignment);
assert sourceAssignments != null;
Set<@NonNull Assignment> unresolvedAssignments = new HashSet<>(sourceAssignments);
unresolvedAssignments.removeAll(sortedAssignments);
if (unresolvedAssignments.isEmpty()) {
resolvedAssignments.add(targetAssignment);
}
}
if (resolvedAssignments.isEmpty()) {
throw new IllegalStateException("Cyclic assignment dependency " + targetAssignments);
}
Collections.sort(resolvedAssignments, ToStringComparator.INSTANCE);
sortedAssignments.addAll(resolvedAssignments);
targetAssignments.removeAll(resolvedAssignments);
}
sortedAssignments.addAll(otherAssignments);
return sortedAssignments;
}
}