blob: 251707e70f673c86cacedeea0f28b06a19e58ef1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2020 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.compiler.internal.qvts2qvti;
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.CollectionType;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.NullLiteralExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.StandardLibrary;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.VariableExp;
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.TreeIterable;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.trace.Element2MiddleProperty;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.trace.Rule2TraceGroup;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.RegionAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.checks.CheckedCondition;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.checks.CheckedConditionAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.FallibilityAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.MappingPartitionAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionedTransformationAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.TransformationPartitioner;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.utilities.ReachabilityForest;
import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil;
import org.eclipse.qvtd.pivot.qvtbase.Function;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder.GraphElement;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtimperative.AddStatement;
import org.eclipse.qvtd.pivot.qvtimperative.CheckStatement;
import org.eclipse.qvtd.pivot.qvtimperative.ConnectionVariable;
import org.eclipse.qvtd.pivot.qvtimperative.DeclareStatement;
import org.eclipse.qvtd.pivot.qvtimperative.GuardParameter;
import org.eclipse.qvtd.pivot.qvtimperative.LoopVariable;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.MappingParameter;
import org.eclipse.qvtd.pivot.qvtimperative.MappingParameterBinding;
import org.eclipse.qvtd.pivot.qvtimperative.MappingStatement;
import org.eclipse.qvtd.pivot.qvtimperative.NewStatement;
import org.eclipse.qvtd.pivot.qvtimperative.NewStatementPart;
import org.eclipse.qvtd.pivot.qvtimperative.ObservableStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SetStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SimpleParameter;
import org.eclipse.qvtd.pivot.qvtimperative.SpeculateStatement;
import org.eclipse.qvtd.pivot.qvtimperative.Statement;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeHelper;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
import org.eclipse.qvtd.pivot.qvtrelation.utilities.QVTrelationUtil;
import org.eclipse.qvtd.pivot.qvtschedule.BooleanLiteralNode;
import org.eclipse.qvtd.pivot.qvtschedule.ClassDatum;
import org.eclipse.qvtd.pivot.qvtschedule.Connection;
import org.eclipse.qvtd.pivot.qvtschedule.Edge;
import org.eclipse.qvtd.pivot.qvtschedule.ExpressionEdge;
import org.eclipse.qvtd.pivot.qvtschedule.KeyPartEdge;
import org.eclipse.qvtd.pivot.qvtschedule.KeyedValueNode;
import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge;
import org.eclipse.qvtd.pivot.qvtschedule.NavigationEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.NodeConnection;
import org.eclipse.qvtd.pivot.qvtschedule.Partition;
import org.eclipse.qvtd.pivot.qvtschedule.PredicateEdge;
import org.eclipse.qvtd.pivot.qvtschedule.PropertyDatum;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import org.eclipse.qvtd.pivot.qvtschedule.Role;
import org.eclipse.qvtd.pivot.qvtschedule.SuccessEdge;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import org.eclipse.qvtd.runtime.utilities.QVTruntimeLibraryHelper;
import org.eclipse.qvtd.runtime.utilities.QVTruntimeUtil;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class BasicPartition2Mapping extends AbstractPartition2Mapping
{
/**
* The OldEdgeSchedule analyses the old (constant, loaded, speculation, predicated) nodes and edges
* to build a linear edge traversal schedule in which unconditional checks that may fail appear
* as early as possible.
*/
private class OldEdgeSchedule
{
/**
* The linear execution schedule for edges.
*/
private @NonNull List<@NonNull Edge> edgeSchedule = new ArrayList<>();
/**
* The edges (and their opposites) in edgeSchedule.
*/
private final @NonNull Set<@NonNull Edge> scheduledEdges = new HashSet<>();
/**
* The nodes at the ends of the edges in edgeSchedule.
*/
private final @NonNull Set<@NonNull Node> scheduledNodes;
public OldEdgeSchedule() {
this.scheduledNodes = Sets.newHashSet(QVTscheduleUtil.getHeadNodes(partition)); // ?? leaf constants
}
private void addEdge(@NonNull Edge edge) {
assert !edge.isConditional();
assert partition.getRole(edge) != null;
if (scheduledEdges.add(edge)) {
NavigationEdge oppositeEdge = null;
if (edge instanceof NavigationEdge) {
oppositeEdge = ((NavigationEdge)edge).getOppositeEdge();
if (oppositeEdge != null) {
boolean wasAdded = scheduledEdges.add(oppositeEdge);
assert wasAdded;
}
}
Node sourceNode = QVTscheduleUtil.getSourceNode(edge);
Role sourceNodeRole = partition.getRole(sourceNode);
if ((sourceNodeRole != null) && !sourceNode.isDependency()) {
Node targetNode = QVTscheduleUtil.getTargetNode(edge);
Role targetNodeRole = partition.getRole(targetNode);
if ((targetNodeRole != null) && !targetNode.isDependency()) {
// Integer sourceCost = reachabilityForest.getCost(sourceNode);
// Integer targetCost = reachabilityForest.getCost(targetNode);
// assert (sourceCost != null) && (targetCost != null);// && ((targetCost <= 0) || (sourceCost <= targetCost));
addNode(sourceNode);
addNode(targetNode);
edgeSchedule.add(edge);
}
}
}
}
private void addNode(@NonNull Node targetNode) {
assert partition.getRole(targetNode) != null;
if (scheduledNodes.add(targetNode)) {
// Integer targetCost = reachabilityForest.getCost(targetNode);
Iterable<@NonNull Edge> reachingEdges = reachabilityForest.getReachingEdges(targetNode);
int size = Iterables.size(reachingEdges);
if (size > 0) {
if (size == 1) {
Edge edge = reachingEdges.iterator().next();
assert !edge.isPartial();
if (!edge.isConditional()) {
addEdge(edge);
}
}
else {
List<@NonNull Node> sourceNodes = new ArrayList<>();
for (Edge edge : reachingEdges) { // FIXME lowest cost first
assert !edge.isPartial();
if (!edge.isConditional()) {
sourceNodes.add(QVTscheduleUtil.getSourceNode(edge));
}
}
if (sourceNodes.size() > 1) {
Collections.sort(sourceNodes, reachabilityForest.getNodeCostComparator());
}
for (Node sourceNode : sourceNodes) {
addNode(sourceNode);
}
for (Edge edge : reachingEdges) {
assert !edge.isPartial();
if (!edge.isConditional()) {
addEdge(edge);
}
}
}
}
}
}
public @NonNull Iterable<@NonNull CheckedCondition> analyze() {
//
// A dispatch input node must be checked to confirm the correct derivation.
//
Node dispatchNode = regionAnalysis.basicGetDispatchNode();
if (dispatchNode != null) {
for (@NonNull Edge edge : QVTscheduleUtil.getOutgoingEdges(dispatchNode)) {
Role edgeRole = partition.getRole(edge);
if ((edgeRole != null) && edgeRole.isPredicated()) {
addEdge(edge);
}
}
}
//
// A predicated head node success must be checked before anything else.
//
for (@NonNull Node headNode : headNodes) { // FIXME This should be redundant wrt prioritized CheckConditions
Edge localSuccessEdge = regionAnalysis.basicGetLocalSuccessEdge(headNode);
if (localSuccessEdge != null) {
Role localSuccessEdgeRole = partition.getRole(localSuccessEdge);
if ((localSuccessEdgeRole != null) && localSuccessEdgeRole.isPredicated()) {
addEdge(localSuccessEdge);
}
}
Edge globalSuccessEdge = regionAnalysis.basicGetGlobalSuccessEdge(headNode);
if (globalSuccessEdge != null) {
Role globalSuccessEdgeRole = partition.getRole(globalSuccessEdge);
if ((globalSuccessEdgeRole != null) && globalSuccessEdgeRole.isPredicated()) {
addEdge(globalSuccessEdge);
}
}
}
//
// An unreachable constant may be an outright suppression.
//
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(QVTscheduleUtil.getRegion(partition))) {
Role role = partition.getRole(node);
if ((role != null) && role.isConstant()) {
if (node.getIncomingEdges().isEmpty() && node.getOutgoingEdges().isEmpty()) {
OCLExpression exp = createVariableExp(node);
CheckStatement checkStatement = createCheckStatement(exp);
addTrace(checkStatement, node);
}
}
}
//
// Speculated edges must participate in a global decision before anything else is done.
/*
Iterable<@NonNull SuccessEdge> speculatedEdges = checkedConditionAnalysis.getSpeculatedEdges();
if (speculatedEdges != null) {
for (@NonNull SuccessEdge speculatedEdge : speculatedEdges) { // Ensure that all speculated objects are located
addNode(QVTscheduleUtil.getSourceNode(speculatedEdge));
}
for (@NonNull SuccessEdge speculatedEdge : speculatedEdges) { // then traverse the speculated properties all in sequence
addEdge(speculatedEdge);
}
} */
Set<@NonNull CheckedCondition> checkedConditions = checkedConditionAnalysis.computeCheckedConditions();
int checkableSize = checkedConditions.size();
// if (checkableSize > 0) {
List<@NonNull CheckedCondition> sortedCheckedConditions = new ArrayList<>(checkedConditions);
if (checkableSize > 1) {
Collections.sort(sortedCheckedConditions, new CheckedConditionWeightComparator(BasicPartition2Mapping.this));
}
for (@NonNull CheckedCondition checkedCondition : sortedCheckedConditions) { // ?? speculated edges first
Iterable<@NonNull Edge> edges = checkedCondition.getEdges();
if (edges != null) {
for (@NonNull Edge edge : edges) {
if (!edge.isConditional()) {
addEdge(edge);
}
}
}
Node node = checkedCondition.getNode();
if (node != null) {
addNode(node);
}
}
// }
//
// Speculated edges must participate in a global decision before anything else is done.
//
Iterable<@NonNull SuccessEdge> speculatedEdges = checkedConditionAnalysis.getSpeculatedEdges();
if (speculatedEdges != null) {
for (@NonNull SuccessEdge speculatedEdge : speculatedEdges) { // Ensure that all speculated objects are located
addNode(QVTscheduleUtil.getSourceNode(speculatedEdge));
}
for (@NonNull SuccessEdge speculatedEdge : speculatedEdges) { // then traverse the speculated properties all in sequence
addEdge(speculatedEdge);
}
}
//
List<@NonNull Edge> residualEdges = null;
for (@NonNull Edge edge : checkedConditionAnalysis.getOldUnconditionalEdges()) {
if (residualEdges == null) {
residualEdges = new ArrayList<>();
}
residualEdges.add(edge);
}
if (residualEdges != null) {
Collections.sort(residualEdges, reachabilityForest.getEdgeCostComparator());
for (@NonNull Edge edge : residualEdges) {
addEdge(edge);
}
}
// assert edgeSchedule.size() == oldEdges.size(); -- FIXME oppositeEdges inhibit simple equality check
// assert new HashSet<>(edgeSchedule).equals(new HashSet<>(oldEdges));
return sortedCheckedConditions;
}
protected void createConstantCheck(@NonNull Edge edge, @NonNull OCLExpression checkExpression) {
Role edgeRole = partition.getRole(edge);
assert edgeRole != null;
if (edgeRole.isSpeculated()) {
assert edge.isSuccess();
boolean isInfallible = isInfallible();
boolean isTerminating = isTerminating();
if (isInfallible) { // If cycles are guaranteed not to fail (e.g. Forward2Reverse)
// Speculation code can be omitted.
}
else if (isTerminating) { // If cycles are guaranteed to terminate (e.g. ATL2QVTr containment ascent)
CheckStatement checkStatement = createCheckStatement(checkExpression);
addTrace(checkStatement, edge);
}
else {
QVTruntimeUtil.errPrintln("Speculation code omitted for " + partition);
}
}
else {
CheckStatement checkStatement = createCheckStatement(checkExpression);
addTrace(checkStatement, edge);
}
}
public void synthesize(@NonNull Iterable<@NonNull CheckedCondition> checkedConditions) {
assert mapping.getOwnedStatements().isEmpty(); // Enforce analyze then synthesize design policy
List<@NonNull OCLExpression> speculatedExpressions = null;
List<@NonNull Edge> speculatedEdges = null;
for (@NonNull Edge edge : edgeSchedule) {
assert !edge.isConditional();
Role edgeRole = partition.getRole(edge);
assert edgeRole != null;
Node sourceNode = QVTscheduleUtil.getSourceNode(edge);
assert !sourceNode.isConditional();
Role sourceNodeRole = partition.getRole(sourceNode);
assert sourceNodeRole != null;
Node targetNode = QVTscheduleUtil.getTargetNode(edge);
assert !targetNode.isConditional();
Role targetNodeRole = partition.getRole(targetNode);
assert targetNodeRole != null;
if (edgeRole.isSpeculated()) {
if (speculatedExpressions == null) {
speculatedExpressions = new ArrayList<>();
speculatedEdges = new ArrayList<>();
}
else {
assert !speculatedExpressions.isEmpty() : "Speculated edges must be scheduled in sequence";
}
SuccessEdge speculatedEdge = (SuccessEdge)edge;
assert speculatedEdges != null;
speculatedEdges.add(speculatedEdge);
OCLExpression sourceExp = createVariableExp(sourceNode);
Property successProperty = QVTscheduleUtil.getReferredProperty(speculatedEdge);
OCLExpression propertyCallExp = helper.createPropertyCallExp(sourceExp, successProperty);
addTrace(propertyCallExp, edge);
if (targetNode instanceof BooleanLiteralNode) {
if (!((BooleanLiteralNode)targetNode).isBooleanValue()) {
// propertyCallExp = helper.createOperationCallExp(propertyCallExp, "not"); // FIXME can we do better than ignoring awkward speculations
}
else {
speculatedExpressions.add(propertyCallExp);
}
}
else {
OCLExpression valueExp = createVariableExp(targetNode);
propertyCallExp = helper.createOperationCallExp(propertyCallExp, "=", valueExp);
speculatedExpressions.add(propertyCallExp);
}
}
else {
if ((speculatedExpressions != null) && !speculatedExpressions.isEmpty()) {
assert speculatedEdges != null;
createSpeculateStatement(speculatedExpressions, speculatedEdges);
speculatedExpressions = Collections.emptyList(); // Prime assertion bomb for disjoint soeculations
speculatedEdges = Collections.emptyList();
}
if (edge.isNavigation()) {
NavigationEdge navigationEdge = (NavigationEdge)edge;
Property property = QVTscheduleUtil.getReferredProperty(navigationEdge);
OCLExpression sourceExp = createVariableExp(sourceNode);
Type sourceType = sourceExp.getType();
Type requiredType = property.getOwningClass();
if ((requiredType != null) && !sourceType.conformsTo(getMetamodelManager().getStandardLibrary(), requiredType)) {
String castName = "cast_" + sourceNode.getName(); // FIXME BUG 530033 in a closed world this is always a fail
DeclareStatement castStatement = createCheckedDeclareStatement(castName, sourceExp, requiredType);
sourceExp = helper.createVariableExp(castStatement);
}
OCLExpression source2targetExp = createCallExp(sourceExp, property);
addTrace(source2targetExp, edge);
if (targetNode.isNullLiteral()) {
NullLiteralExp nullLiteralExp = helper.createNullLiteralExp();
addTrace(nullLiteralExp, targetNode);
createCheckStatement(source2targetExp, "=", nullLiteralExp, edge);
}
else if (targetNodeRole.isConstant() && !sourceNodeRole.isNew()) {
// createCheckStatement(source2targetExp);
VariableDeclaration nodeVariable = node2variable.get(targetNode);
/*if (nodeVariable == null) {
VariableExp targetVariableExp = getSubexpressionVariableExp(targetNode);
createCheckStatement(targetVariableExp, "=", source2targetExp);
}
else*/ if (navigationEdge.isPartial()) {
OCLExpression targetExpression = getExpression(targetNode);
createCheckStatement(source2targetExp, "includes", targetExpression, edge);
}
else if ((targetNode instanceof BooleanLiteralNode) && ((BooleanLiteralNode)targetNode).isBooleanValue()) {
createConstantCheck(edge, source2targetExp);
}
else if (targetNodeRole == Role.CONSTANT_SUCCESS_TRUE) {
createConstantCheck(edge, source2targetExp);
}
else if (nodeVariable == null) {
QVTs2QVTiNodeVisitor expressionCreator = new QVTs2QVTiNodeVisitor(BasicPartition2Mapping.this);
OCLExpression targetExpression = expressionCreator.getExpression(targetNode);
createCheckStatement(source2targetExp, "=", targetExpression, edge);
}
else {
OCLExpression targetExpression = getExpression(targetNode);
createCheckStatement(source2targetExp, "=", targetExpression, edge);
}
}
else {
VariableDeclaration nodeVariable = node2variable.get(targetNode);
if (nodeVariable == null) {
createDeclareStatement(targetNode, source2targetExp);
// createCastPredicates(targetNode, declareStatement);
}
else if (navigationEdge.isPartial()) {
OCLExpression targetExpression = getExpression(targetNode);
createCheckStatement(source2targetExp, "includes", targetExpression, edge);
}
else if (!edge.isNew()) { // No need to check predication of a realized edge
OCLExpression targetExpression = getExpression(targetNode);
createCheckStatement(targetExpression, "=", source2targetExp, edge);
}
}
}
else if (edge instanceof PredicateEdge) {
// FIXME assert !((PredicateEdge)edge).isPartial();
OCLExpression sourceExpression = getExpression(sourceNode);
if (!(targetNode instanceof BooleanLiteralNode)) {
String edgeName = ClassUtil.nonNullState(edge.getName()).trim();
if (edgeName.length() >= 2) {
edgeName = edgeName.substring(1, edgeName.length()-1); // Lose guillemets
}
if ("equals".equals(edgeName)) {
edgeName = "="; // FIXME regularize
}
OCLExpression targetExpression = getExpression(targetNode);
createCheckStatement(sourceExpression, edgeName, targetExpression, edge);
}
else if (((BooleanLiteralNode)targetNode).isBooleanValue()) {
createConstantCheck(edge, sourceExpression);
}
else {
createCheckStatement(sourceExpression, "=", helper.createBooleanLiteralExp(false), edge);
}
}
else if (edge instanceof ExpressionEdge) {
getVariableDeclaration(targetNode);
}
}
}
}
@Override
public @NonNull String toString() {
StringBuilder s = new StringBuilder();
boolean isFirst = true;
for (@NonNull Edge edge : edgeSchedule) {
if (!isFirst) {
s.append("\n");
}
s.append(edge);
isFirst = false;
}
return s.toString();
}
}
protected final @NonNull RegionAnalysis regionAnalysis;
/**
* Lazily computed closure of all preceding nodes, icluding the final node, of each node.
*/
private @Nullable Map<@NonNull Node, @NonNull Set<@NonNull Node>> node2precedingNodeClosure = null;
/**
* The selected head from each head group that is actually passed. Other heads are computed.
*/
private final @NonNull List<@NonNull Node> headNodes = new ArrayList<>();
/**
* The subset of possible guard nodes that all callers can pass.
*/
private final @NonNull List<@NonNull Node> guardNodes = new ArrayList<>();
/**
* Mapping from the scheduled Nodes to their QVTi variables.
*/
private final @NonNull Map<@NonNull Node, @NonNull VariableDeclaration> node2variable = new HashMap<>();
/**
* The mechanisms to reach required nodes.
*/
private final @NonNull ReachabilityForest reachabilityForest;
/**
* The failure mechanisms.
*/
private final @NonNull CheckedConditionAnalysis checkedConditionAnalysis;
public BasicPartition2Mapping(@NonNull QVTs2QVTiVisitor visitor, @NonNull Mapping mapping, @NonNull MappingPartitionAnalysis<?> partitionAnalysis) {
super(visitor, mapping, partitionAnalysis.getPartition());
this.regionAnalysis = scheduleManager.getRegionAnalysis(QVTscheduleUtil.getRegion(partition));
this.reachabilityForest = partitionAnalysis.getReachabilityForest();
this.checkedConditionAnalysis = new CheckedConditionAnalysis(partitionAnalysis, scheduleManager);
addTrace(mapping, partition);
StringBuilder s = TransformationPartitioner.PROPERTY_OBSERVE.isActive() ? new StringBuilder() : null;
if (s != null) {
s.append("[" + partition.getPassRangeText() + "] " + partition.getName());
}
checkedConditionAnalysis.computeCheckedPropertyDatums(s);
if (s != null) {
TransformationPartitioner.PROPERTY_OBSERVE.println(s.toString());
}
}
public void addVariable(@NonNull Node node, @NonNull VariableDeclaration variableDeclaration) {
VariableDeclaration oldVariableDeclaration = node2variable.put(node, variableDeclaration);
assert oldVariableDeclaration == null;
addTrace(variableDeclaration, node);
}
public @Nullable VariableDeclaration basicGetVariable(@NonNull Node node) {
return node2variable.get(node);
}
/**
* Create accumulation assignments for connections.
*/
private void createAddStatements() {
if (connection2variable != null) {
List<@NonNull NodeConnection> connections = new ArrayList<>(connection2variable.keySet());
Collections.sort(connections, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull NodeConnection connection : connections) {
Node sourceNode = connection.getSource(partition);
OCLExpression variableExpression = createVariableExp(sourceNode);
ConnectionVariable connectionVariable = connection2variable.get(connection);
assert connectionVariable != null;
AddStatement addStatement = createAddStatement(connectionVariable, variableExpression);
addTrace(addStatement, sourceNode);
}
}
}
private void createCallStatements(@NonNull Map<@NonNull Partition, @NonNull Map<@NonNull Node, @NonNull Node>> calls) {
List<@NonNull MappingStatement> mappingStatements = new ArrayList<>();
Map<@NonNull LoopVariable, @NonNull OCLExpression> loopVariables = null;
for (Map.Entry<@NonNull Partition, @NonNull Map<@NonNull Node, @NonNull Node>> entry : calls.entrySet()) {
@NonNull Partition calledPartition = entry.getKey();
AbstractPartition2Mapping calledRegion2Mapping = visitor.getPartition2Mapping(calledPartition);
List<@NonNull MappingParameterBinding> mappingParameterBindings = new ArrayList<>();
for (Map.Entry<@NonNull Node, @NonNull Node> entry2 : entry.getValue().entrySet()) {
@NonNull Node sourceNode = entry2.getKey();
@NonNull Node targetNode = entry2.getValue();
OCLExpression sourceExpression = createVariableExp(sourceNode);
Type type = sourceExpression.getType();
if (type instanceof CollectionType) {
if (loopVariables == null) {
loopVariables = new HashMap<>();
}
Type elementType = ((CollectionType)type).getElementType();
assert elementType != null;
LoopVariable loopVariable = helper.createLoopVariable(getSafeName("loop" + loopVariables.size()), elementType);//, true, sourceExpression);
loopVariables.put(loopVariable, sourceExpression);
// sourceExpression = PivotUtil.createVariableExp(loopVariable);
VariableDeclaration guardVariable = calledRegion2Mapping.getGuardVariable(targetNode);
mappingParameterBindings.add(helper.createLoopParameterBinding((GuardParameter) guardVariable, loopVariable));
}
else {
VariableDeclaration guardVariable = calledRegion2Mapping.getGuardVariable(targetNode);
mappingParameterBindings.add(helper.createSimpleParameterBinding((SimpleParameter) guardVariable, sourceExpression));
}
}
Collections.sort(mappingParameterBindings, QVTimperativeUtil.MappingParameterBindingComparator.INSTANCE);
MappingStatement mappingCallStatement = calledRegion2Mapping.createMappingCall(mappingParameterBindings);
if (loopVariables != null) {
for (Map.Entry<@NonNull LoopVariable, @NonNull OCLExpression> loopEntry : loopVariables.entrySet()) {
@NonNull LoopVariable loopVariable = loopEntry.getKey();
@NonNull OCLExpression loopSource = loopEntry.getValue();
mappingCallStatement = helper.createMappingLoop(loopSource, loopVariable, mappingCallStatement);
}
}
mappingStatements.add(mappingCallStatement);
}
mapping.getOwnedStatements().addAll(mappingStatements);
}
private void createAssignedValues(@NonNull Iterable<@NonNull NavigableEdge> sortedRealizedEdges) {
for (@NonNull NavigableEdge edge : sortedRealizedEdges) {
Node sourceNode = edge.getEdgeSource();
Node targetNode = edge.getEdgeTarget();
if (sourceNode.isOld() && !sourceNode.isConstant()) {
getVariableDeclaration(sourceNode);
}
if (targetNode.isOld() && !targetNode.isConstant()) {
getVariableDeclaration(targetNode);
}
}
}
private @NonNull CheckStatement createCheckStatement(@NonNull OCLExpression booleanExpression) {
CheckStatement checkStatement = helper.createCheckStatement(booleanExpression);
mapping.getOwnedStatements().add(checkStatement);
return checkStatement;
}
private void createCheckStatement(@NonNull OCLExpression firstExpression, @NonNull String operatorName, @NonNull OCLExpression secondExpression, @NonNull GraphElement graphElement) {
OCLExpression booleanExpression = helper.createOperationCallExp(firstExpression, operatorName, secondExpression);
addTrace(booleanExpression, graphElement);
CheckStatement checkStatement = createCheckStatement(booleanExpression);
addTrace(checkStatement, graphElement);
}
private @NonNull DeclareStatement createCheckedDeclareStatement(@NonNull String name, @NonNull OCLExpression sourceExp, @NonNull Type requiredType) {
String safeName = getSafeName(name);
DeclareStatement castStatement = helper.createDeclareStatement(safeName, requiredType, true, sourceExp);
mapping.getOwnedStatements().add(castStatement);
return castStatement;
}
private void createClassSetStatement(@Nullable StringBuilder s, @NonNull NavigableEdge edge) {
if (edge instanceof NavigationEdge) {
NavigationEdge navigationEdge = (NavigationEdge)edge;
Node sourceNode = edge.getEdgeSource();
Node targetNode = edge.getEdgeTarget();
Property property = QVTscheduleUtil.getReferredProperty(navigationEdge);
boolean isNotify = connectionManager.isHazardousWrite(s, navigationEdge);
Property setProperty;
VariableDeclaration slotVariable;
OCLExpression valueExpression;
SetStatement setStatement;
boolean isPartial;
if (/*!isPartial &&*/ property.isIsImplicit()) {
slotVariable = getVariableDeclaration(targetNode);
setProperty = QVTrelationUtil.getOpposite(property);
valueExpression = createVariableExp(sourceNode);
isPartial = setProperty.isIsMany();
}
else {
slotVariable = getVariableDeclaration(sourceNode);
setProperty = property;
valueExpression = createVariableExp(targetNode);
isPartial = edge.isPartial();
}
if (!sourceNode.isStrict()) {
setStatement = helper.createSetStatement(slotVariable, setProperty, valueExpression, isPartial, isNotify);
mapping.getOwnedStatements().add(setStatement);
addTrace(setStatement, edge);
}
else {
assert !property.isIsImplicit();
assert !isPartial;
// assert !isNotify;
NewStatement newStatement = (NewStatement)slotVariable;
NewStatementPart newStatementPart = helper.createNewStatementPart(setProperty, valueExpression); //, isNotify);
newStatement.getOwnedParts().add(newStatementPart);
addTrace(newStatementPart, edge);
}
}
else {
// SharedEdge
}
}
@NonNull VariableExp createContextVariableExp() {
StandardLibrary standardLibrary = getStandardLibrary();
Transformation iTransformation = visitor.getImperativeTransformation();
VariableDeclaration contextVariable = QVTbaseUtil.getContextVariable(standardLibrary, iTransformation);
return helper.createVariableExp(contextVariable);
}
private @NonNull DeclareStatement createDeclareStatement(@NonNull Node node, @NonNull OCLExpression initExpression) {
Type variableType = node.getClassDatum().getPrimaryClass();
assert variableType != null;
boolean isRequired = !node.isConditional() && node.isRequired();
if (initExpression.isIsRequired()) {
isRequired = true;
}
String safeName = getSafeName(node);
DeclareStatement newVariable = helper.createDeclareStatement(safeName, variableType, isRequired, initExpression);
List<Statement> ownedStatements = mapping.getOwnedStatements();
int i = ownedStatements.size();
while (--i >= 0) {
Statement statement = ownedStatements.get(i);
if (!(statement instanceof SetStatement) && !(statement instanceof NewStatement)) {
break;
}
}
ownedStatements.add(i+1, newVariable);
addVariable(node, newVariable);
return newVariable;
}
private @NonNull GuardParameter createGuardParameter(@NonNull Node guardNode) {
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(guardNode);
Type variableType = guardNode.getClassDatum().getPrimaryClass();
TypedModel iTypedModel = ClassUtil.nonNullState(visitor.getQVTiTypedModel(classDatum.getReferredTypedModel()));
GuardParameter guardParameter = helper.createGuardParameter(getSafeName(guardNode), iTypedModel, variableType, true);
Property successProperty = null;
Rule2TraceGroup rule2traceGroup = regionAnalysis.basicGetRule2TraceGroup();
if (rule2traceGroup != null) {
Element2MiddleProperty relation2globalSuccessProperty = rule2traceGroup.basicGetRelation2GlobalSuccessProperty();
Property globalSuccessProperty = null;
if (relation2globalSuccessProperty != null) {
globalSuccessProperty = relation2globalSuccessProperty.getTraceProperty();
NavigableEdge globalSuccessEdge = guardNode.getOutgoingNavigableEdge(globalSuccessProperty);
if (globalSuccessEdge != null) {
Role globalSuccessEdgeRole = partition.getRole(globalSuccessEdge);
if (globalSuccessEdgeRole != null) {
Node globalSuccessNode = QVTscheduleUtil.getTargetNode(globalSuccessEdge);
if (globalSuccessNode.isSuccess() && !globalSuccessEdgeRole.isPredicated()) { // Skip predicated success
assert globalSuccessEdgeRole.isRealized();
successProperty = globalSuccessProperty;
// assert !isHazardousWrite(globalSuccessEdge);
}
}
}
}
Property localSuccessProperty = null;
Element2MiddleProperty relation2localSuccessProperty = rule2traceGroup.basicGetRelation2LocalSuccessProperty();
if (relation2localSuccessProperty != null) {
localSuccessProperty = relation2localSuccessProperty.getTraceProperty();
NavigableEdge localSuccessEdge = guardNode.getOutgoingNavigableEdge(localSuccessProperty);
if (localSuccessEdge != null) {
Role localSuccessEdgeRole = partition.getRole(localSuccessEdge);
if (localSuccessEdgeRole != null) {
Node localSuccessNode = QVTscheduleUtil.getTargetNode(localSuccessEdge);
if (localSuccessNode.isSuccess() && !localSuccessEdgeRole.isPredicated()) { // Skip predicated local-success
assert localSuccessEdgeRole.isRealized();
assert successProperty == null;
successProperty = localSuccessProperty;
// assert !isHazardousWrite(localSuccessEdge);
}
}
}
}
}
if (successProperty != null) {
guardParameter.setSuccessProperty(successProperty);
}
mapping.getOwnedMappingParameters().add(guardParameter);
addVariable(guardNode, guardParameter);
return guardParameter;
}
/**
* Identify the headNodes/guardNodes and create a guard variable for each guard node.
*/
private void createHeadAndGuardNodeVariables() {
Set<@NonNull Region> headCallingRegions = new HashSet<@NonNull Region>();
// Iterable<@NonNull Edge> recursionEdges = region.getRecursionEdges();
// if (Iterables.size(recursionEdges) > 0) {
// headCallingRegions.add(region);
// }
for (@NonNull Node headNode : QVTscheduleUtil.getHeadNodes(partition)) { // FIXME Move best bead selection to QVTs2QVTs so that diagrams show best head
if (!headNode.isDependency() && !headNode.isThis()) {
Node bestHeadNode = null;
Iterable<@NonNull Node> callingSources = connectionManager.getPassedBindingSources(headNode);
if (!Iterables.isEmpty(callingSources)) {
bestHeadNode = headNode;
}
for (@NonNull Node callingSource : callingSources) {
headCallingRegions.add(QVTscheduleUtil.getOwningRegion(callingSource));
}
if (bestHeadNode != null) {
headNodes.add(bestHeadNode);
}
else {
CompilerUtil.addPartitionError(visitor.getProblemHandler(), partition, "No best head");
}
}
}
guardNodes.addAll(headNodes);
Collections.sort(guardNodes, NameUtil.NAMEABLE_COMPARATOR);
//
// Set Mapping useClassMappings if consumes are not inherently unique.
// DataTypes are difficuilt to prove uniqueness.
boolean isStrict = false;
for (@NonNull Node guardNode : guardNodes) {
if (!guardNode.isDependency()) {
createGuardParameter(guardNode);
for (@NonNull Edge edge : QVTscheduleUtil.getOutgoingEdges(guardNode)) {
if (!edge.isSuccess()) {
Role role = partition.getRole(edge);
if ((role != null) && role.isOld()) {
Node targetNode = QVTscheduleUtil.getTargetNode(edge);
if (targetNode.isDataType()) {
isStrict = true;
}
}
}
}
Connection connection = guardNode.getIncomingConnection();
if (connection != null) {
int lastProducer = connection.getLastPass();
int lastConsumer = partition.getLastPass();
if (lastConsumer <= lastProducer) {
isStrict = true; // ?? do we need a separate isCyclic
}
}
}
}
mapping.setIsStrict(isStrict);
//
// Create any connectionVariable guards
//
createAppendParameters();
}
private void createObservedProperties() {
Set<@NonNull PropertyDatum> allCheckedPropertyDatums = checkedConditionAnalysis.getAllCheckedPropertyDatums();
if (!allCheckedPropertyDatums.isEmpty()) { // FIXME Use PropertyDatum to handle derived accesses
// Set<@NonNull Property> allCheckedProperties = new HashSet<>();
// for (@NonNull PropertyDatum propertyDatum : allCheckedPropertyDatums) {
// allCheckedProperties.add(QVTscheduleUtil.getReferredProperty(propertyDatum));
// }
//
// Ideally we could install each observed property as it is actually used. But
// this needs to be coded in many places.
//
for (Statement asStatement : mapping.getOwnedStatements()) {
if (asStatement instanceof ObservableStatement) {
List<Property> observedProperties = ((ObservableStatement)asStatement).getObservedProperties();
for (EObject eObject : new TreeIterable(asStatement, false)) {
if (eObject instanceof NavigationCallExp) {
boolean allCheckedPropertyDatumsContainsPropertyDatum = false;
NavigationCallExp navigationCallExp = (NavigationCallExp) eObject;
Property property = PivotUtil.getReferredProperty(navigationCallExp);
for (@NonNull GraphElement graphElement : getTrace(navigationCallExp)) { // Only one in practice
if (graphElement instanceof NavigationEdge) { // Always NavigationEdge
for (@NonNull PropertyDatum propertyDatum : scheduleManager.getPropertyDatums((NavigationEdge)graphElement)) {
if (allCheckedPropertyDatums.contains(propertyDatum)) {
allCheckedPropertyDatumsContainsPropertyDatum = true;
// FIXME check timing
int earliestConsumption = mapping.getFirstPass();
// connectionManager.get
}
}
}
}
// boolean allCheckedPropertiesContainsProperty = allCheckedProperties.contains(property);
/* if (allCheckedPropertiesContainsProperty && !allCheckedPropertyDatumsContainsPropertyDatum) {
getClass();
for (@NonNull GraphElement graphElement : getTrace(navigationCallExp)) { // Only one in parctice
if (graphElement instanceof NavigationEdge) { // Always NavigationEdge
for (@NonNull PropertyDatum propertyDatum : scheduleManager.getPropertyDatums((NavigationEdge)graphElement)) {
if (allCheckedPropertyDatums.contains(propertyDatum)) {
allCheckedPropertyDatumsContainsPropertyDatum = true;
}
}
}
}
} */
if (allCheckedPropertyDatumsContainsPropertyDatum && !observedProperties.contains(property)) {
observedProperties.add(property);
}
}
/* else if (eObject instanceof OppositePropertyCallExp) {
Property property = PivotUtil.getReferredProperty((NavigationCallExp) eObject).getOpposite();
if (allCheckedProperties.contains(property) && !observedProperties.contains(property)) {
observedProperties.add(property);
}
} */
}
}
}
}
}
@SuppressWarnings("unchecked")
public <T extends Operation> @NonNull T createOperation(@NonNull T operation) {
Operation newOperation = visitor.create(operation);
assert newOperation != null;
return (@NonNull T) newOperation;
}
/**
* Determine a traversal order for the old edges then synthesize the patttern matching statements.
*/
private void createPatternMatch() {
OldEdgeSchedule oldSchedule = new OldEdgeSchedule();
Iterable<@NonNull CheckedCondition> checkedConditions = oldSchedule.analyze();
oldSchedule.synthesize(checkedConditions);
}
private void createPropertyAssignments(@NonNull Iterable<@NonNull NavigableEdge> sortedRealizedEdges) {
StringBuilder s = TransformationPartitioner.PROPERTY_NOTIFY.isActive() ? new StringBuilder() : null;
if (s != null) {
s.append("[" + partition.getPassRangeText() + "] " + partition.getName());
}
Map<@NonNull Node, @NonNull List<@NonNull NavigationEdge>> classAssignments = null;
for (@NonNull NavigableEdge edge : sortedRealizedEdges) {
if ((edge instanceof NavigationEdge) && !(edge instanceof KeyPartEdge)) {
NavigationEdge navigationEdge = (NavigationEdge)edge;
Node sourceNode = edge.getEdgeSource();
Node targetNode = edge.getEdgeTarget();
if (targetNode.isSuccess()) {} // SuccessNode has a 'magic' automatic assignment
else if (targetNode.isDataType()) {
VariableDeclaration asVariable = getVariableDeclaration(sourceNode);
Property property = QVTscheduleUtil.getReferredProperty(navigationEdge);
QVTs2QVTiNodeVisitor expressionCreator = new QVTs2QVTiNodeVisitor(this);
OCLExpression valueExp = expressionCreator.getExpression(targetNode);
boolean isNotify = connectionManager.isHazardousWrite(s, navigationEdge);
boolean isPartial = edge.isPartial();
if (!sourceNode.isStrict()) {
SetStatement setStatement = helper.createSetStatement(asVariable, property, valueExp, isPartial, isNotify);
// addObservedProperties(setStatement);
mapping.getOwnedStatements().add(setStatement);
addTrace(setStatement, edge);
}
else {
assert !isPartial;
// assert !isNotify;
NewStatement newStatement = (NewStatement)asVariable;
NewStatementPart newStatementPart = helper.createNewStatementPart(property, valueExp);
newStatement.getOwnedParts().add(newStatementPart);
addTrace(newStatementPart, edge);
}
}
else {
if (classAssignments == null) {
classAssignments = new HashMap<>();
}
List<@NonNull NavigationEdge> edges = classAssignments.get(sourceNode);
if (edges == null) {
edges = new ArrayList<>();
classAssignments.put(sourceNode, edges);
}
edges.add(navigationEdge);
}
}
else {
// SharedEdge
}
}
if (classAssignments != null) {
pruneClassAssignments(classAssignments);
Set<@NonNull NavigationEdge> classAssignmentEdges = new HashSet<>();
for (@NonNull List<@NonNull NavigationEdge> values : classAssignments.values()) {
classAssignmentEdges.addAll(values);
}
for (@NonNull NavigableEdge edge : sortedRealizedEdges) {
if (classAssignmentEdges.contains(edge)) {
createClassSetStatement(s, edge);
}
}
}
// @SuppressWarnings("null")
// @NonNull EList<@NonNull Statement> statements = mapping.getOwnedStatements();
// ECollections.sort(statements, new StatementComparator(statements));
if (s != null) {
TransformationPartitioner.PROPERTY_NOTIFY.println(s.toString());
}
}
/* private void createRealizedIncludesAssignments() {
List<@NonNull Edge> realizedIncludesEdges = null;
for (@NonNull Edge edge : region.getRealizedEdges()) {
// if (QVTscheduleUtil.isRealizedIncludes(edge)) {
// if (realizedIncludesEdges == null) {
// realizedIncludesEdges = new ArrayList<>();
// }
// realizedIncludesEdges.add(edge);
// }
}
if (realizedIncludesEdges != null) {
if (realizedIncludesEdges.size() > 1) {
Collections.sort(realizedIncludesEdges, ToStringComparator.INSTANCE);
}
for (@NonNull Edge edge : realizedIncludesEdges) {
Node sourceNode = edge.getEdgeSource();
Node targetNode = edge.getEdgeTarget();
/ * if (targetNode.isDataType()) {
OCLExpression slotVariableExp = createVariableExp(sourceNode);
Property property = edge.getProperty();
OCLExpression valueExp = expressionCreator.getExpression(targetNode);
if (valueExp == null) {
valueExp = expressionCreator.getExpression(targetNode); // FIXME debugging
}
if (valueExp != null) {
PropertyAssignment propertyAssignment = QVTimperativeUtil.createPropertyAssignment(slotVariableExp, property, valueExp);
bottomPattern.getAssignment().add(propertyAssignment);
}
else {
QVTruntimeUtil.errPrintln("No assignment in " + this + " to " + slotVariableExp + "." + property);
* /
}
}
} */
private void createRealizedVariables() {
List<@NonNull Node> sortedNodes = Lists.newArrayList(partition.getPartialNodes());
Collections.sort(sortedNodes,NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull Node node : sortedNodes) {
Role nodeRole = partition.getRole(node);
if ((nodeRole != null) && nodeRole.isNew() && /*newNode.isPattern() &&*/ node.isClass()) {
OCLExpression constructor = null;
if (node.isOperation()) {
if (node instanceof KeyedValueNode) {
KeyedValueNode keyedValueNode = (KeyedValueNode)node;
ClassDatum classDatum = keyedValueNode.getClassDatumValue();
assert classDatum != null;
Function function = visitor.getKeyFunction(classDatum);
VariableExp thisExp = createContextVariableExp();
Map<@NonNull PropertyDatum, @NonNull OCLExpression> propertyDatum2expression = new HashMap<>();
for (@NonNull Edge edge : QVTscheduleUtil.getIncomingEdges(node)) {
if (edge instanceof KeyPartEdge) {
KeyPartEdge keyPartEdge = (KeyPartEdge)edge;
PropertyDatum propertyDatum = QVTscheduleUtil.getReferredPart(keyPartEdge);
Node partNode = QVTscheduleUtil.getSourceNode(edge);
OCLExpression partExpression = getExpression(partNode);
propertyDatum2expression.put(propertyDatum, partExpression);
}
}
List<@NonNull OCLExpression> asArguments = new ArrayList<>();
for (@NonNull PropertyDatum propertyDatum : QVTscheduleUtil.getOwnedPropertyDatums(classDatum)) {
if (propertyDatum.isKey()) {
OCLExpression asArgument = propertyDatum2expression.get(propertyDatum);
if (asArgument == null) {
asArgument = helper.createInvalidExpression();
}
asArguments.add(asArgument);
}
}
constructor = helper.createOperationCallExp(thisExp, function, asArguments);
addTrace(constructor, node);
}
if (constructor == null) {
QVTs2QVTiNodeVisitor expressionCreator = new QVTs2QVTiNodeVisitor(this);
constructor = ((OperationCallExp)node.getOriginatingElement()).accept(expressionCreator);
addTrace(constructor, node);
}
}
ClassDatum classDatum = node.getClassDatum();
TypedModel pTypedModel = classDatum.getReferredTypedModel();
TypedModel iTypedModel = ClassUtil.nonNullState(visitor.getQVTiTypedModel(pTypedModel));
NewStatement newStatement = helper.createNewStatement(getSafeName(node), iTypedModel, classDatum.getPrimaryClass());
newStatement.setOwnedExpression(constructor);
newStatement.setIsContained(node.isContained());
mapping.getOwnedStatements().add(newStatement);
addVariable(node, newStatement);
}
}
}
private @NonNull SpeculateStatement createSpeculateStatement(@NonNull Iterable<@NonNull OCLExpression> speculateExpressions, @NonNull Iterable<@NonNull Edge> speculatedEdges) {
SpeculateStatement speculateStatement = helper.createSpeculateStatement(speculateExpressions);
mapping.getOwnedStatements().add(speculateStatement);
for (@NonNull Edge speculatedEdge : speculatedEdges) {
addTrace(speculateStatement, speculatedEdge);
}
return speculateStatement;
}
private @NonNull OCLExpression createVariableExp(@NonNull Node node) {
// assert !node.isConditional(); -- permitted to avoid Set value expression hazards
if (node.isNullLiteral()) {
NullLiteralExp nullLiteralExp = helper.createNullLiteralExp();
addTrace(nullLiteralExp, node);
return nullLiteralExp;
}
VariableDeclaration variable = getVariableDeclaration(node);
return PivotUtil.createVariableExp(variable);
}
/**
* Return the navigable edges that may be used by to locate nodes by this partition.
* The default implementation returns all old primary navigable edges
* and all already realized navigable edges
*/
protected @NonNull Iterable<@NonNull NavigableEdge> getAvailableNavigableEdges() {
Set<@NonNull NavigableEdge> oldEdges = new HashSet<>();
for (@NonNull Edge edge : partition.getPartialEdges()) {
assert !edge.isCast();
Role edgeRole = partition.getRole(edge);
assert edgeRole != null;
if (edgeRole.isOld() && edge.isNavigation() /*&& edge.isUnconditional()*/) {
Node sourceNode = QVTscheduleUtil.getSourceNode(edge);
Role sourceNodeRole = partition.getRole(sourceNode);
assert sourceNodeRole != null;
if (sourceNodeRole.isOld()){
Node targetNode = QVTscheduleUtil.getTargetNode(edge);
Role targetNodeRole = partition.getRole(targetNode);
assert targetNodeRole != null;
if (targetNodeRole.isOld()) {
oldEdges.add((NavigableEdge) edge);
}
}
}
}
return oldEdges;
}
public @NonNull OCLExpression getExpression(@NonNull Node node) {
VariableDeclaration variable = node2variable.get(node);
if (variable == null) {
QVTs2QVTiNodeVisitor expressionCreator = new QVTs2QVTiNodeVisitor(this);
OCLExpression targetExpression = expressionCreator.getExpression(node);
if (node.isConditional()) {
return targetExpression;
}
variable = createDeclareStatement(node, targetExpression);
}
return helper.createVariableExp(variable);
}
@Override
public @NonNull List<@NonNull Node> getGuardNodes() {
return guardNodes;
}
@Override
public @NonNull MappingParameter getGuardVariable(@NonNull Node node) {
VariableDeclaration variable = node2variable.get(node);
assert variable != null;
return (MappingParameter) variable;
}
public @NonNull QVTimperativeHelper getHelper() {
return helper;
}
public @NonNull Partition getPartition() {
return partition;
}
public @NonNull Set<@NonNull Node> getPrecedingNodes(@NonNull Node targetNode) {
Map<@NonNull Node, @NonNull Set<@NonNull Node>> node2precedingNodeClosure2 = node2precedingNodeClosure;
if (node2precedingNodeClosure2 == null) {
node2precedingNodeClosure = node2precedingNodeClosure2 = new HashMap<>();
}
Set<@NonNull Node> precedingNodes = node2precedingNodeClosure2.get(targetNode);
if (precedingNodes == null) {
precedingNodes = new HashSet<>();
node2precedingNodeClosure2.put(targetNode, precedingNodes);
precedingNodes.add(targetNode);
for (@NonNull Node sourceNode : reachabilityForest.getPredecessorsClosure(targetNode)) {
precedingNodes.addAll(getPrecedingNodes(sourceNode));
}
}
assert precedingNodes.size() > 0;
return precedingNodes;
}
public @NonNull QVTruntimeLibraryHelper getQVTruntimeLibraryHelper() {
return scheduleManager.getQVTruntimeLibraryHelper();
}
public @NonNull ReachabilityForest getReachabilityForest() {
return reachabilityForest;
}
protected @NonNull Iterable<@NonNull NavigableEdge> getSortedAssignments() {
List<@NonNull NavigableEdge> navigableEdges = new ArrayList<>();
for (@NonNull Edge edge : partition.getPartialEdges()) {
if (edge.isNavigation()) {
Role edgeRole = partition.getRole(edge);
if ((edgeRole != null) && edgeRole.isRealized()) {
navigableEdges.add((NavigableEdge)edge);
}
}
}
return NavigationEdgeSorter.getSortedAssignments(navigableEdges);
}
public @NonNull StandardLibrary getStandardLibrary() {
return scheduleManager.getStandardLibrary();
}
// public @NonNull Transformation getTransformation() {
// return visitor.getTransformation();
// }
protected @NonNull VariableDeclaration getVariableDeclaration(@NonNull Node node) {
VariableDeclaration variable = node2variable.get(node);
if (variable == null) {
QVTs2QVTiNodeVisitor expressionCreator = new QVTs2QVTiNodeVisitor(this);
OCLExpression targetExpression = expressionCreator.getExpression(node);
variable = createDeclareStatement(node, targetExpression);
// LetVariable letVariable = helper.createLetVariable(node.getName(), targetExpression.getType(, targetExpression.isIsRequired());
// variable = helper.createLetExp(letVariable, targetExpression);
}
return variable;
}
/* protected @NonNull VariableDeclaration getVariableDeclaration(@NonNull Node node) {
return getSubexpressionDeclaration(node);
/ * VariableDeclaration variable = node2variable.get(node);
if (variable == null) {
ExpressionCreator expressionCreator = new ExpressionCreator();
TypedElement typedElement = node.getTypedElements().iterator().next();
OCLExpression initExpression = typedElement.accept(expressionCreator);
variable = createDeclareStatement(node, initExpression);
}
return variable; * /
} */
private boolean isInfallible() {
PartitionedTransformationAnalysis partitionedTransformationAnalysis = regionAnalysis.getPartitionedTransformationAnalysis();
FallibilityAnalysis fallibilityAnalysis = partitionedTransformationAnalysis.getFallibilityAnalysis(partition);
return fallibilityAnalysis.isInfallible();
}
private boolean isTerminating() {
PartitionedTransformationAnalysis partitionedTransformationAnalysis = regionAnalysis.getPartitionedTransformationAnalysis();
FallibilityAnalysis fallibilityAnalysis = partitionedTransformationAnalysis.getFallibilityAnalysis(partition);
return fallibilityAnalysis.isTerminating();
}
/**
* Filter classAssignments to retain only one of each opposite-property assignment pair.
*/
private void pruneClassAssignments(@NonNull Map<@NonNull Node, @NonNull List<@NonNull NavigationEdge>> classAssignments) {
for (@NonNull Node sourceNode : new ArrayList<>(classAssignments.keySet())) {
List<@NonNull NavigationEdge> forwardEdges = classAssignments.get(sourceNode);
assert forwardEdges != null;
for (int iForward = forwardEdges.size()-1; iForward >= 0; iForward--) {
NavigationEdge forwardEdge = forwardEdges.get(iForward);
Node targetNode = forwardEdge.getEdgeTarget();
List<@NonNull NavigationEdge> reverseEdges = classAssignments.get(targetNode);
if (reverseEdges != null) {
for (int iReverse = reverseEdges.size()-1; iReverse >= 0; iReverse--) {
NavigationEdge reverseEdge = reverseEdges.get(iReverse);
if (sourceNode == reverseEdge.getEdgeTarget()) {
Property forwardProperty = QVTscheduleUtil.getReferredProperty(forwardEdge);
Property reverseProperty = QVTscheduleUtil.getReferredProperty(reverseEdge);
if (forwardProperty.getOpposite() == reverseProperty) {
if (forwardProperty.isIsImplicit()) {
forwardEdges.remove(forwardEdge);
}
else if (reverseProperty.isIsImplicit()) {
reverseEdges.remove(reverseEdge);
}
else if (sourceNode.isDependency()) {
forwardEdges.remove(forwardEdge);
}
else if (targetNode.isDependency()) {
reverseEdges.remove(reverseEdge);
}
else { // FIXME do we prefer either direction ?
reverseEdges.remove(reverseEdge);
}
}
}
}
}
}
}
}
public void removeVariable(@NonNull Node node) {
VariableDeclaration oldVariableDeclaration = node2variable.remove(node);
assert oldVariableDeclaration != null;
}
@Override
public void synthesizeCallStatements() {
Map<@NonNull Partition, @NonNull Map<@NonNull Node, @NonNull Node>> calls = null;
for (@NonNull Partition calledPartition : connectionManager.getCallableChildren(partition)) {
if (calls == null) {
calls = new HashMap<>();
}
Map<@NonNull Node, @NonNull Node> source2target = calls.get(calledPartition);
if (source2target == null) {
source2target = new HashMap<>();
calls.put(calledPartition, source2target);
}
AbstractPartition2Mapping calledRegion2Mapping = visitor.getPartition2Mapping(calledPartition);
for (@NonNull Node calledGuardNode : calledRegion2Mapping.getGuardNodes()) {
for (@NonNull Node callingNode : connectionManager.getPassedBindingSources(calledGuardNode)) {
if (partition.getRole(callingNode) != null) {
Node oldNode = source2target.put(callingNode, calledGuardNode);
assert oldNode == null;
}
}
for (@NonNull Node callingNode : connectionManager.getUsedBindingSources(calledGuardNode)) {
if (partition.getRole(callingNode) != null) {
Node oldNode = source2target.put(callingNode, calledGuardNode);
assert oldNode == null;
}
}
}
}
if (calls != null) {
createCallStatements(calls);
}
}
/**
* Create all the statements that support the local content of this micromapping.
*/
@Override
public void synthesizeLocalStatements() {
createHeadAndGuardNodeVariables(); // BLUE/CYAN guard/append nodes
createPatternMatch(); // BLUE/CYAN nodes and edges
Iterable<@NonNull NavigableEdge> sortedRealizedEdges = getSortedAssignments();
createAssignedValues(sortedRealizedEdges); // Reify Variables for ends of GREEN edges
createRealizedVariables(); // GREEN nodes
createPropertyAssignments(sortedRealizedEdges); // GREEN edges
createAddStatements(); // export to append nodes
// createRealizedIncludesAssignments();
checkTrace();
createObservedProperties(); // wrap observable clauses around hazardous accesses
}
@Override
public @NonNull String toString() {
return String.valueOf(partition);
}
}