blob: 72181b0f1f55f3ce87c7791053cda5370daddd89 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 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.compiler.internal.qvts2qvts;
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.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.DataType;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.qvtd.compiler.ProblemHandler;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.OriginalContentsAnalysis;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.LoadingRegionAnalysis;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.ScheduleManager;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionAnalysis;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
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.EdgeConnection;
import org.eclipse.qvtd.pivot.qvtschedule.LoadingRegion;
import org.eclipse.qvtd.pivot.qvtschedule.MappingPartition;
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.QVTscheduleFactory;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import org.eclipse.qvtd.pivot.qvtschedule.Role;
import org.eclipse.qvtd.pivot.qvtschedule.RootRegion;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.DomainUsage;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleConstants;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.SymbolNameBuilder;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
/**
* ConnectionManager supervises the connections initially between rule-regions and ultimaately between partitions.
*/
public class ConnectionManager
{
private static final @NonNull String JOIN_ATTRIBUTE_PREFIX = "ja";
private static final @NonNull String JOIN_EDGE_PREFIX = "je_";
private static final @NonNull String JOIN_INPUT_PREFIX = "ji";
private static final @NonNull String JOIN_MIDDLE_PREFIX = "jm";
private static final @NonNull String JOIN_OUTPUT_PREFIX = "jo";
public static final @NonNull List<@NonNull Partition> EMPTY_PARTITION_LIST = Collections.emptyList();
protected final @NonNull ScheduleManager scheduleManager;
protected final @NonNull LoadingRegionAnalysis loadingRegionAnalysis;
/**
* The per-class node connections that unite a set of sources via a shared connection.
*/
public final @NonNull Map<@NonNull ClassDatum, @NonNull Map<@NonNull Set<@NonNull Node>, @NonNull NodeConnection>> classDatum2nodes2nodeConnections = new HashMap<>();
/**
* The edge connections that unite a set of sources via a shared connection.
*/
public final @NonNull Map<@NonNull Set<@NonNull NavigableEdge>, @NonNull EdgeConnection> edges2edgeConnection = new HashMap<>();
/**
* Analysis of the contents of the partitioned mappings, null prior to partitioning.
*/
private final @NonNull OriginalContentsAnalysis originalContentsAnalysis;
/**
* Ordered list of regions that call this region
*/
private final @NonNull Map<@NonNull Partition, @NonNull List<@NonNull Partition>> partition2parents = new HashMap<>();
/**
* Ordered list of regions that this region calls. May exclude some children whose dependencies are unsatisfied.
* May include non-children whose dependencies are satisfied by earlier child calls.
*/
private final @NonNull Map<@NonNull Partition, @NonNull List<@NonNull Partition>> partition2children = new HashMap<>();
public ConnectionManager(@NonNull ProblemHandler problemHandler, @NonNull ScheduleManager scheduleManager, @NonNull LoadingRegionAnalysis loadingRegionAnalysis) {
// super(qvtm2qvts.getEnvironmentFactory());
this.scheduleManager = scheduleManager;
this.loadingRegionAnalysis = loadingRegionAnalysis;
this.originalContentsAnalysis = scheduleManager.getOriginalContentsAnalysis();
}
public void addCallToChild(@NonNull Partition parentPartition, @NonNull Partition childPartition) {
getCallableChildren(parentPartition).add(childPartition);
getCallableParents(childPartition).add(parentPartition);
}
/**
* Create the Passed and Used Connections between all introducers and their corresponding consuming nodes.
*/
public void createConnections(@NonNull RootRegion rootRegion, @NonNull Iterable<@NonNull Concurrency> partitionSchedule) {
Set<@NonNull Region> regions = new HashSet<>();
for (@NonNull Concurrency concurrency : partitionSchedule) {
for (@NonNull PartitionAnalysis partitionAnalysis : concurrency) {
regions.add(QVTscheduleUtil.getRegion(partitionAnalysis.getPartition()));
}
}
// for (@NonNull Region region : regions) {
// createIncomingConnections(rootRegion, region);
// }
// scheduleManager.writeDebugGraphs("4-bindings", true, true, false);
// for (Region region : sortedCallableRegions) {
// region.checkIncomingConnections();
// }
}
/**
* Create an EdgeConnection for the predicatedEdge and/or its target node.
*/
private void createAttributeEdgeConnection(@NonNull RootRegion rootRegion, @NonNull Region region, @NonNull Node castTargetNode, @NonNull Iterable<@NonNull NavigableEdge> predicatedEdges) {
RootRegion invokingRegion2 = rootRegion;
assert invokingRegion2 != null;
Node castTarget = castTargetNode;
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(castTarget);
for (@NonNull NavigableEdge predicatedEdge : predicatedEdges) {
List<@NonNull NavigableEdge> attributeConnectionSourceEdges = null;
List<@NonNull String> partialNames = new ArrayList<>();
assert predicatedEdge.isNavigation();
assert predicatedEdge.getIncomingConnection() == null;
assert !predicatedEdge.isCast();
Property predicatedProperty = predicatedEdge.getProperty();
assert !predicatedProperty.isIsImplicit();
boolean isDataType = classDatum.getCompleteClass().getPrimaryClass() instanceof DataType;
assert isDataType;
Iterable<@NonNull NavigableEdge> realizedEdges = getNewEdges(predicatedEdge, classDatum);
if (realizedEdges != null) {
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
if (scheduleManager.isElementallyConformantSource(realizedEdge, predicatedEdge) && QVTscheduleUtil.isConformantTarget(realizedEdge, predicatedEdge)) {
if (attributeConnectionSourceEdges == null) {
attributeConnectionSourceEdges = new ArrayList<>();
}
attributeConnectionSourceEdges.add(realizedEdge);
}
}
partialNames.add(QVTscheduleUtil.getName(predicatedEdge.getEdgeSource().getCompleteClass()));
partialNames.add(QVTscheduleUtil.getName(predicatedProperty));
}
if (attributeConnectionSourceEdges != null) {
EdgeConnection edgeConnection = getAttributeConnection(invokingRegion2, attributeConnectionSourceEdges, partialNames, predicatedProperty);
edgeConnection.addUsedTargetEdge(predicatedEdge, false);
if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) {
QVTscheduleConstants.CONNECTION_CREATION.println(" Attribute EdgeConnection \"" + edgeConnection + "\" to " + castTarget);
// Scheduler.CONNECTIONS.println(" classDatumAnalysis " + classDatumAnalysis);
// for (@NonNull Node sourceNode : sourceNodes) {
// Scheduler.CONNECTIONS.println(" from " + sourceNode.getRegion());
// Scheduler.CONNECTIONS.println(" " + sourceNode);
// }
// for (@NonNull NavigationEdge realizedEdge : realizedEdges) {
// Scheduler.CONNECTIONS.println(" edge " + realizedEdge);
// }
}
}
}
}
/**
* Create an EdgeConnection for the predicatedEdge and/or its target node.
*/
private void createClassEdgeConnection(@NonNull RootRegion rootRegion, @NonNull Region region, @NonNull Node castTargetNode, @NonNull Iterable<@NonNull NavigableEdge> predicatedEdges) {
RootRegion invokingRegion2 = rootRegion;
assert invokingRegion2 != null;
Node castTarget = castTargetNode;
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(castTarget);
for (@NonNull NavigableEdge predicatedEdge : predicatedEdges) {
assert predicatedEdge.isNavigation();
assert predicatedEdge.getIncomingConnection() == null;
assert !predicatedEdge.isCast();
Property predicatedProperty = predicatedEdge.getProperty();
assert !predicatedProperty.isIsImplicit();
NavigableEdge castEdge = QVTscheduleUtil.getCastTarget(predicatedEdge);
boolean isDataType = classDatum.getCompleteClass().getPrimaryClass() instanceof DataType;
assert !isDataType;
Iterable<@NonNull Node> sourceNodes = getNewNodes(classDatum);
// if (sourceNodes != null) {
Iterable<@NonNull NavigableEdge> realizedEdges = getNewEdges(predicatedEdge, classDatum);
if (realizedEdges != null) {
Set<@NonNull Region> edgeSourceRegions = new HashSet<>();
Set<@NonNull Region> nodeSourceRegions = new HashSet<>();
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
edgeSourceRegions.add(QVTscheduleUtil.getOwningRegion(realizedEdge));
}
if (sourceNodes != null) {
for (@NonNull Node sourceNode : sourceNodes) {
nodeSourceRegions.add(QVTscheduleUtil.getOwningRegion(sourceNode));
}
}
//
// Create an EdgeConnection for the edge realizations unless all edges are sources by node sources.
//
if (!nodeSourceRegions.containsAll(edgeSourceRegions)) { // If edges are assigned independently of their targets.
Set<@NonNull Region> conformantEdgeSourceRegions = null;
List<@NonNull NavigableEdge> thoseEdges = null;
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
if (scheduleManager.isElementallyConformantSource(realizedEdge, predicatedEdge) && QVTscheduleUtil.isConformantTarget(realizedEdge, predicatedEdge)) {
if (thoseEdges == null) {
thoseEdges = new ArrayList<>();
conformantEdgeSourceRegions = new HashSet<>();
}
if (!thoseEdges.contains(realizedEdge)) {
thoseEdges.add(realizedEdge);
assert conformantEdgeSourceRegions != null;
conformantEdgeSourceRegions.add(QVTscheduleUtil.getOwningRegion(realizedEdge));
}
}
}
if ((thoseEdges != null) && !nodeSourceRegions.containsAll(conformantEdgeSourceRegions)) {
EdgeConnection edgeConnection = getEdgeConnection(invokingRegion2, thoseEdges, predicatedProperty);
if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) {
QVTscheduleConstants.CONNECTION_CREATION.println(" EdgeConnection \"" + edgeConnection + "\" to " + predicatedEdge);
}
if (!Iterables.contains(edgeConnection.getTargetEdges(), castEdge)) {
edgeConnection.addUsedTargetEdge(castEdge, false);
if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) {
for (@NonNull NavigableEdge thatEdge : thoseEdges) {
QVTscheduleConstants.CONNECTION_CREATION.println(" from " + thatEdge.getOwningRegion() + " : " + thatEdge);
}
}
}
}
}
//
// Create a NodeConnection for the node realizations.
//
if ((sourceNodes != null)
&& !castTarget.isLoaded() // WIP and !isOnlyCast
&& !castTarget.isConstant()
&& !castTarget.isHead()
&& !castTarget.isOperation()
&& (castTarget.getIncomingConnection() == null)
// && !castTarget.isAttributeNode()
// && !rootRootRegion.isOnlyCastOrRecursed(predicatedNode)
// && !hasEdgeConnection(predicatedNode)
) {
NodeConnection predicatedConnection = getNodeConnection(invokingRegion2, sourceNodes, classDatum, scheduleManager.getDomainUsage(classDatum));
predicatedConnection.addUsedTargetNode(castTarget, false);
if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) {
QVTscheduleConstants.CONNECTION_CREATION.println(" NodeConnection \"" + predicatedConnection + "\" to " + castTarget);
for (@NonNull Node sourceNode : sourceNodes) {
QVTscheduleConstants.CONNECTION_CREATION.println(" from " + sourceNode.getOwningRegion() + " : " + sourceNode);
}
}
}
// }
}
}
}
private @NonNull EdgeConnection createEdgeConnection(@NonNull RootRegion rootRegion, @NonNull Set<@NonNull NavigableEdge> sourceSet, @NonNull Property property, @NonNull SymbolNameBuilder s) {
assert !property.isIsImplicit();
EdgeConnection edgeConnection = QVTscheduleFactory.eINSTANCE.createEdgeConnection();
// protected ConnectionImpl(@NonNull RootRegion region, @NonNull Set<@NonNull CE> sourceEnds, @NonNull String name) {
edgeConnection.setOwningRootRegion(rootRegion);
edgeConnection.setName(scheduleManager.getScheduleModel().reserveSymbolName(s, edgeConnection));
QVTscheduleUtil.getSourceEnds(edgeConnection).addAll(sourceSet);
// }
// public EdgeConnectionImpl(@NonNull RootRegion region, @NonNull Set<@NonNull NavigableEdge> sourceEdges, @NonNull String name, @NonNull Property property) {
// super(region, sourceEdges, name);
edgeConnection.setReferredProperty(property);
for (@NonNull NavigableEdge sourceEdge : sourceSet) {
assert Iterables.contains(QVTscheduleUtil.getSourceEnds(edgeConnection), sourceEdge);
// assert edge.getRegion() == getRegion();
List<EdgeConnection> outgoingConnections2 = sourceEdge.getOutgoingConnections();
assert !outgoingConnections2.contains(edgeConnection);
outgoingConnections2.add(edgeConnection);
}
// }
// return new EdgeConnectionImpl(this, sourceSet, s, property);
return edgeConnection;
}
/**
* Create and return a NodeConnection to the nodes that can provide the sources for headNode.
* Returns null if the pattern surrounding the headNode conflicts with the pattern
* surrounding all possible sources.
*/
private @Nullable NodeConnection createHeadConnection(@NonNull RootRegion rootRegion, @NonNull Region region, @NonNull Node headNode) {
RootRegion invokingRegion2 = rootRegion;
List<@NonNull Node> headSources = null;
//
// Locate compatible introducers and non-recursive producers
//
boolean isSpeculation = false;
Iterable<@NonNull Node> sourceNodes = getIntroducingOrNewNodes(headNode);
if (!scheduleManager.useActivators() && (sourceNodes != null)) {
for (@NonNull Node sourceNode : sourceNodes) {
if (sourceNode.isSpeculation()) {
isSpeculation = true;
}
}
}
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(headNode);
if (isSpeculation && !headNode.isSpeculated()) {
sourceNodes = originalContentsAnalysis.getOldNodes(classDatum);
assert sourceNodes != null;
}
if (sourceNodes != null) {
for (@NonNull Node sourceNode : sourceNodes) {
boolean isSource = true;
if (isSpeculation) {
if (headNode.isSpeculated()) {
// if (!sourceNode.isOld()) {
// isSource = false;
// }
}
else {
if (!sourceNode.isSpeculated() || !sourceNode.isHead()) {
isSource = false;
}
}
}
// Region sourceRegion = sourceNode.getRegion();
// if (sourceRegion != this) {
if (isSource) {
Map<@NonNull Node, @NonNull Node> called2calling = new HashMap<>();
if (isCompatiblePattern(region, headNode, sourceNode, called2calling)) {
if (headSources == null) {
headSources = new ArrayList<>();
}
headSources.add(sourceNode);
}
}
// }
}
}
if (headSources == null) {
return null;
}
//
// Connect up the head
//
NodeConnection headConnection = getNodeConnection(invokingRegion2, headSources, classDatum, scheduleManager.getDomainUsage(classDatum));
if (headNode.isDependency()) {
headConnection.addUsedTargetNode(headNode, false);
}
else {
headConnection.addPassedTargetNode(headNode);
}
if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) {
QVTscheduleConstants.CONNECTION_CREATION.println((headNode.isDependency() ? " Extra NodeConnection " : " Head NodeConnection \"") + headConnection + "\" to " + headNode);
for (@NonNull Node sourceNode : headSources) {
QVTscheduleConstants.CONNECTION_CREATION.println(" from " + sourceNode.getOwningRegion() + " : " + sourceNode);
}
}
return headConnection;
}
/**
* Return the Connections to each of the head nodes. Returns null if the pattern surrounding any headNode conflicts with the pattern
* surrounding all its possible sources. (Any head with no sources is a non-invocation.)
*/
private @Nullable Iterable<@NonNull NodeConnection> createHeadConnections(@NonNull RootRegion rootRegion, @NonNull Region region) {
List<@NonNull NodeConnection> headConnections = null;
for (@NonNull Node headNode : QVTscheduleUtil.getHeadNodes(region)) {
if (headNode.isDependency()) {
createHeadConnection(rootRegion, region, headNode); /** Dependency nodes have extra not-head connections. */
}
else {
NodeConnection headConnection = createHeadConnection(rootRegion, region, headNode);
if (headConnection == null) {
scheduleManager.addRegionWarning(region, "No incoming connections for " + headNode.getName());
headConnection = createHeadConnection(rootRegion, region, headNode); // FIXME debugging
return null; // so matching only fails for unmatchable real heads
}
else {
if (headConnections == null) {
headConnections = new ArrayList<>();
}
headConnections.add(headConnection);
}
// FIXME. If there are multiple heads and an internal node is reachable from more than one head, then the possible
// sources for the internal node are the intersection of the alternatives which may eliminate some call paths.
}
}
return headConnections;
}
/**
* Create the connections that establish the inter-region dependencies. A passed connection for each head node and
* used connections for each predicated edge. (Non-head nodes are necessarily connected by predicates to the head.)
*
* Every node/edge must have a connection to all its possible sources to ensure that the compile-time / run-time
* scheduler delays the execution of this region until the sources are available.
*
* Connections may be omitted when we can prove that the connection is available as a consequence of some restriction.
* - a connection to a CONSTANT source is unnecessary (always available)
* - a connection to a LOADED source is unnecessary (always available)
* - a connection to a source whose navigation path is incompatible with the head-to-target path is unnecessary
* - a connection to a node that is only used in cast form is unnecessary (the cast node provides more precision)
* - a connection to a cast edge is unnecessary (the cast edge extends a navigation edge that has a connection)
*
* Connections to attribute nodes are connected to just the edge; the datatyped node can be ambiguous
*
* Connections to realized nodes can be omitted if they are at one end of a realized edge
*
* Connections to edges account for type conformance of the nodes ends. The edge ends extend to account for casts.
*
* Each head node has a passed connection from its sources.
* Consistently related nodes navigable from the head have a bindable connection to the correspondingly related sources.
* Inconsistently related nodes navigable from the head have a computable connection to all compatibly typed sources.
* Unrelated nodes such as the internals of computations are not connected; their dependencies should be in dependency heads.
* Edges dependent on realization elsewhere are represented by connection from all head nodes of the dependent region
* to all heads of the realizing region.
*/
public void createIncomingConnections(@NonNull RootRegion rootRegion, @NonNull Region region) {
if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) {
QVTscheduleConstants.CONNECTION_CREATION.println("connecting " + region);
}
assert !(region instanceof LoadingRegion);
Iterable<@NonNull NodeConnection> headConnections = createHeadConnections(rootRegion, region);
assert (headConnections != null);// {
//
// Gather multiple edges sharing the same target to avoid multiple incoming connections -- FIXME no need to gather
//
Map<@NonNull Node, @NonNull List<@NonNull NavigableEdge>> castTargetNode2predicatedEdges = new HashMap<>();
for (@NonNull NavigationEdge predicatedEdge : region.getPredicatedNavigationEdges()) {
assert !predicatedEdge.isCast();
assert predicatedEdge.isNavigation();
assert predicatedEdge.getIncomingConnection() == null;
Property predicatedProperty = predicatedEdge.getReferredProperty();
if (!predicatedProperty.isIsImplicit()) { // unnavigable opposites are handled by the navigable property
NavigableEdge castEdge = QVTscheduleUtil.getCastTarget(predicatedEdge);
Node castTargetNode = QVTscheduleUtil.getCastTarget(castEdge.getEdgeTarget());
List<@NonNull NavigableEdge> predicatedEdges = castTargetNode2predicatedEdges.get(castTargetNode);
if (predicatedEdges == null) {
predicatedEdges = new ArrayList<>();
castTargetNode2predicatedEdges.put(castTargetNode, predicatedEdges);
}
predicatedEdges.add(predicatedEdge);
}
}
for (@NonNull Node castTargetNode : castTargetNode2predicatedEdges.keySet()) {
List<@NonNull NavigableEdge> predicatedEdges = castTargetNode2predicatedEdges.get(castTargetNode);
assert predicatedEdges != null;
if (castTargetNode.isClass()) {
createClassEdgeConnection(rootRegion, region, castTargetNode, predicatedEdges);
}
else {
createAttributeEdgeConnection(rootRegion, region, castTargetNode, predicatedEdges);
}
}
//}
}
private @NonNull NodeConnection createNodeConnection(@NonNull RootRegion rootRegion, @NonNull Set<@NonNull Node> sourceSet, @NonNull ClassDatum classDatum, @NonNull SymbolNameBuilder s) {
NodeConnection connection = QVTscheduleFactory.eINSTANCE.createNodeConnection();
connection.setOwningRootRegion(rootRegion);
QVTscheduleUtil.getSourceEnds(connection).addAll(sourceSet);
connection.setName(scheduleManager.getScheduleModel().reserveSymbolName(s, connection));
connection.setClassDatum(classDatum);
for (@NonNull Node sourceNode : sourceSet) {
// assert !sourceNode.isConstant();
assert Iterables.contains(QVTscheduleUtil.getSourceEnds(connection), sourceNode);
// assert edge.getRegion() == getRegion();
List<NodeConnection> outgoingConnections2 = sourceNode.getOutgoingConnections();
assert !outgoingConnections2.contains(connection);
outgoingConnections2.add(connection);
}
return connection;
}
/**
* Create the Passed Node Connection between the trace node realized in an activator partition and predicated in other partitions.
* Create Used Edge Connections between each edge realized in the origial region and predicated in a aprtition.
*/
public void createPartitionConnections(@NonNull RootRegion rootRegion, @NonNull Region region) {
Iterable<@NonNull MappingPartition> partitions = QVTscheduleUtil.getRegionPartitions(region);
RegionAnalysis regionAnalysis = scheduleManager.getRegionAnalysis(region);
if (Iterables.size(partitions) <= 1) {
return; // No trace connections if not actually partitioned
}
//
// Every (the) trace node that is realized in the original region and is predicated in one of the partitions requires
// a passed node connection.
//
Iterable <@NonNull Node> traceNodes = regionAnalysis.getTraceNodes();
for (@NonNull Node traceNode : traceNodes) {
Set<@NonNull Node> sourceNodes = new HashSet<>();
for (@NonNull Partition partition : partitions) {
Role nodeRole = QVTscheduleUtil.getRole(partition, traceNode);
if ((nodeRole != null) && nodeRole.isNew()) {
sourceNodes.add(traceNode);
}
}
//
if (!sourceNodes.isEmpty()) {
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(traceNode);
NodeConnection connection = getNodeConnection(rootRegion, sourceNodes, classDatum, scheduleManager.getDomainUsage(classDatum));
//
Set<@NonNull Node> targetNodes = new HashSet<>();
for (@NonNull Partition partition : partitions) {
Role nodeRole = QVTscheduleUtil.getRole(partition, traceNode);
if ((nodeRole != null) && nodeRole.isOld()) {
if (targetNodes.add(traceNode)) {
connection.addPassedTargetNode(traceNode);
}
}
}
}
}
//
// Every edge that is realized in the original region and is predicated in one of the partitions requires
// a used edge connection.
//
for (@NonNull NavigableEdge edge : region.getRealizedNavigationEdges()) {
if (!edge.isSecondary()) {
boolean isAwaited = false;
for (@NonNull PartitionAnalysis partitionAnalysis : regionAnalysis.getPartitionAnalyses()) {
Role role = partitionAnalysis.getPartition().getRole(edge);
if ((role != null) && role.isChecked()) {
isAwaited = true;
break;
}
}
if (isAwaited) {
Property property = QVTscheduleUtil.getProperty(edge);
EdgeConnection connection = getEdgeConnection(rootRegion, Collections.singleton(edge), property);
connection.addUsedTargetEdge(edge, true);
}
}
}
}
private @NonNull EdgeConnection getAttributeConnection(@NonNull RootRegion rootRegion, @NonNull Iterable<@NonNull NavigableEdge> sourceEdges, @NonNull List<@NonNull String> partialNames, @NonNull Property property) {
Set<@NonNull NavigableEdge> sourceSet = Sets.newHashSet(sourceEdges);
EdgeConnection connection = edges2edgeConnection.get(sourceSet);
if (connection == null) {
SymbolNameBuilder s = new SymbolNameBuilder();
s.appendString(JOIN_ATTRIBUTE_PREFIX);
for (@NonNull String partialName : partialNames) {
s.appendString("_");
s.appendName(partialName);
}
connection = createEdgeConnection(rootRegion, sourceSet, property, s);
edges2edgeConnection.put(sourceSet, connection);
}
return connection;
}
/**
* Return the regions that this region can actually call directly after taking account the connection dependencies.
*/
public @NonNull List<@NonNull Partition> getCallableChildren(@NonNull Partition partition) {
List<@NonNull Partition> children = partition2children.get(partition);
if (children == null) {
children = new ArrayList<>();
partition2children.put(partition, children);
}
return children;
}
/**
* Return the regions that can actually call this region directly after taking account the connection dependencies.
*/
public @NonNull List<@NonNull Partition> getCallableParents(@NonNull Partition partition) {
List<@NonNull Partition> parents = partition2parents.get(partition);
if (parents == null) {
parents = new ArrayList<>();
partition2parents.put(partition, parents);
}
return parents;
}
private @NonNull EdgeConnection getEdgeConnection(@NonNull RootRegion rootRegion, @NonNull Iterable<@NonNull NavigableEdge> sourceEdges, @NonNull Property property) {
Set<@NonNull NavigableEdge> sourceSet = Sets.newHashSet(sourceEdges);
EdgeConnection connection = edges2edgeConnection.get(sourceSet);
if (connection == null) {
SymbolNameBuilder s = new SymbolNameBuilder();
s.appendString(JOIN_EDGE_PREFIX);
s.appendName(property.getOwningClass().getName());
s.appendString("_");
s.appendName(property.getName());
connection = createEdgeConnection(rootRegion, sourceSet, property, s);
edges2edgeConnection.put(sourceSet, connection);
}
return connection;
}
public @NonNull Iterable<@NonNull Connection> getIncomingConnections(@NonNull PartitionAnalysis partitionAnalysis) { // FIXME cache
Partition partition = partitionAnalysis.getPartition();
/* Region region = partition.getOriginalRegion();
if (region != null) {
List<@NonNull Connection> connections = new ArrayList<>();
for (@NonNull Node headNode : QVTscheduleUtil.getHeadNodes(region)) {
NodeConnection connection = headNode.getIncomingPassedConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
/* if (node.isDependency() || node.isPattern()) {
if (node.isLoaded() || node.isSpeculated() || node.isPredicated()) { // A DataType may be loaded but subject to an edge predication
NodeConnection connection = node.getIncomingUsedConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
}
else if (node.isTrue()) { // A <<success>> node * /
NodeConnection connection = getIncomingUsedConnection(node);
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
// }
}
for (@NonNull NavigationEdge edge : region.getPredicatedNavigationEdges()) {
EdgeConnection connection = edge.getIncomingConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
return connections;
}
else { */
List<@NonNull Connection> connections = new ArrayList<>();
for (@NonNull Node headNode : QVTscheduleUtil.getHeadNodes(partition)) {
NodeConnection connection = headNode.getIncomingPassedConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
for (@NonNull Node node : partition.getPartialNodes()) {
/* if (node.isDependency() || node.isPattern()) {
if (node.isLoaded() || node.isSpeculated() || node.isPredicated()) { // A DataType may be loaded but subject to an edge predication
NodeConnection connection = node.getIncomingUsedConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
}
else if (node.isTrue()) { // A <<success>> node */
if (!node.isHead() || node.isDependency()) {
NodeConnection connection = getIncomingUsedConnection(node);
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
// }
}
for (@NonNull Edge edge : partition.getPartialEdges()) {
if (partitionAnalysis.isChecked(edge) && edge.isNavigation()) {
NavigationEdge navigationEdge = (NavigationEdge) edge;
EdgeConnection connection = navigationEdge.getIncomingConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
}
return connections;
// }
}
public @Nullable NodeConnection getIncomingUsedConnection(@NonNull Node node) {
NodeConnection incomingConnection = node.getIncomingConnection();
if ((incomingConnection != null) && incomingConnection.isUsed(node)) {
return incomingConnection;
}
else {
return null;
}
}
private @Nullable Iterable<@NonNull Node> getIntroducingOrNewNodes(@NonNull Node headNode) {
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(headNode);
if (!scheduleManager.getDomainUsage(classDatum).isInput()) {
return originalContentsAnalysis.getNewNodes(classDatum); // FIXME also dependsOn ??
}
List<@NonNull Node> nodes = new ArrayList<>();
nodes.add(loadingRegionAnalysis.getIntroducerNode(headNode));
for (@NonNull TypedModel dependsOn : QVTbaseUtil.getDependsOns(QVTscheduleUtil.getTypedModel(classDatum))) {
ClassDatum classDatum2 = scheduleManager.getClassDatum(dependsOn, headNode.getCompleteClass().getPrimaryClass());
Iterable<@NonNull Node> newNodes = originalContentsAnalysis.getNewNodes(classDatum2);
if (newNodes != null) {
for (@NonNull Node newNode : newNodes) {
if (!nodes.contains(newNode)) {
nodes.add(newNode);
}
}
}
}
return nodes;
}
public @NonNull List<@NonNull Connection> getLoopingConnections(@NonNull Partition partition) {
List<@NonNull Connection> loopingConnections = new ArrayList<>();
for (@NonNull Connection connection : getOutgoingConnections(partition)) {
for (@NonNull Partition sourcePartition : connection.getSourcePartitions()) {
if (partition == sourcePartition) {
for (@NonNull Partition targetPartition : connection.getTargetPartitions()) {
if ((partition == targetPartition) && !loopingConnections.contains(connection)) {
loopingConnections.add(connection);
}
}
}
}
}
return loopingConnections;
}
private @Nullable Iterable<@NonNull NavigableEdge> getNewEdges(@NonNull NavigableEdge edge, @NonNull ClassDatum requiredClassDatum) {
return originalContentsAnalysis.getNewEdges(edge, requiredClassDatum);
}
private @Nullable Iterable<@NonNull Node> getNewNodes(@NonNull ClassDatum classDatum) {
return originalContentsAnalysis.getNewNodes(classDatum);
}
/**
* Return all the next connections from this region to another region.
* Where this is a hierarchical region the connections are those from the hierarchical head to its immediate internal regions.
*/
public @NonNull Iterable<@NonNull Connection> getNextConnections(@NonNull Partition partition) {
return getOutgoingConnections(partition);
}
private @NonNull NodeConnection getNodeConnection(@NonNull RootRegion rootRegion, @NonNull Iterable<@NonNull Node> sourceNodes, @NonNull ClassDatum classDatum, @NonNull DomainUsage domainUsage) {
Map<@NonNull Set<@NonNull Node>, @NonNull NodeConnection> nodes2connection = classDatum2nodes2nodeConnections.get(classDatum);
if (nodes2connection == null) {
nodes2connection = new HashMap<>();
classDatum2nodes2nodeConnections.put(classDatum, nodes2connection);
}
Set<@NonNull Node> sourceSet = Sets.newHashSet(sourceNodes);
NodeConnection connection = nodes2connection.get(sourceSet);
if (connection == null) {
SymbolNameBuilder s = new SymbolNameBuilder();
s.appendString(domainUsage.isInput() ? JOIN_INPUT_PREFIX : domainUsage.isOutput() ? JOIN_OUTPUT_PREFIX : JOIN_MIDDLE_PREFIX);
s.appendString("_");
s.appendName(classDatum.getCompleteClass().getName());
connection = createNodeConnection(rootRegion, sourceSet, classDatum, s);
nodes2connection.put(sourceSet, connection);
}
return connection;
}
/**
* Return all connections from this (hierarchical) region to another (hierarchical) region.
*/
public @NonNull List<@NonNull Connection> getOutgoingConnections(@NonNull Partition partition) { // FIXME cache
List<@NonNull Connection> connections = new ArrayList<>();
// for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
for (@NonNull Node node : partition.getPartialNodes()) {
Role role = partition.getRole(node);
if ((role != null) && !role.isChecked()) { //(role.isNew() || role.isLoaded())) {
for (@NonNull NodeConnection connection : node.getOutgoingPassedConnections()) {
connections.add(connection);
}
for (@NonNull NodeConnection connection : node.getOutgoingUsedBindingEdges()) {
connections.add(connection);
}
}
}
// for (@NonNull NavigableEdge edge : region.getNavigationEdges()) {
for (@NonNull Edge edge : partition.getPartialEdges()) {
if (edge.isNavigation()) {
Role role = partition.getRole(edge);
if ((role != null) && !role.isChecked()) { // (role.isNew() || role.isLoaded())) {
for (@NonNull EdgeConnection connection : QVTscheduleUtil.getOutgoingConnections((NavigableEdge) edge)) {
connections.add(connection);
}
}
}
}
return connections;
}
public @NonNull ScheduleManager getScheduleManager() {
return scheduleManager;
}
public @NonNull Iterable<@NonNull Node> getPassedBindingSources(@NonNull Node node) {
List<@NonNull Node> sources = new ArrayList<>();
NodeConnection connection = node.getIncomingPassedConnection();
if (connection != null) {
for (@NonNull Node source : QVTscheduleUtil.getSourceEnds(connection)) {
if (!sources.contains(source)) {
sources.add(source);
}
}
}
return sources;
}
public @NonNull Iterable<@NonNull Node> getUsedBindingSources(@NonNull Node node) {
List<@NonNull Node> sources = new ArrayList<>();
NodeConnection connection = getIncomingUsedConnection(node);
if (connection != null) {
for (@NonNull Node source : QVTscheduleUtil.getSourceEnds(connection)) {
if (!sources.contains(source)) {
sources.add(source);
}
}
}
return sources;
}
/**
* Return true if the predicates of calledNode are not in conflict with the navigable paths from callingNode.
* called2calling identifies already identified conflict free pairs that do not need re-assessment.
*/
private boolean isCompatiblePattern(@NonNull Region region, @NonNull Node calledNode, @NonNull Node callingNode, @NonNull Map<@NonNull Node, @NonNull Node> called2calling) {
Node oldPrevNode = called2calling.put(calledNode, callingNode);
if (oldPrevNode != null) {
return oldPrevNode == callingNode;
}
for (@NonNull NavigableEdge calledEdge : calledNode.getNavigableEdges()) {
Node nextCalledNode = calledEdge.getEdgeTarget();
if (!nextCalledNode.isRealized() && !nextCalledNode.isDataType()) { // FIXME why exclude AttributeNodes?
Edge nextCallingEdge = callingNode.getNavigableEdge(QVTscheduleUtil.getProperty(calledEdge));
if (nextCallingEdge != null) {
Node nextCallingNode = nextCallingEdge.getEdgeTarget();
if ((nextCallingNode.isNullLiteral() != nextCalledNode.isNullLiteral())) {
return false;
}
if (!isCompatiblePattern(region, nextCalledNode, nextCallingNode, called2calling)) {
return false;
}
}
}
}
return true;
}
public void removeCallToChild(@NonNull Partition parentPartition, @NonNull Partition childPartition) {
getCallableChildren(parentPartition).remove(childPartition);
getCallableParents(childPartition).remove(parentPartition);
}
public void replaceCallToChild(@NonNull Partition parentPartition, @NonNull Partition oldPartition, @NonNull Partition newPartition) {
List<@NonNull Partition> parentPartitionCallableChildren = getCallableChildren(parentPartition);
int index = parentPartitionCallableChildren.indexOf(oldPartition);
parentPartitionCallableChildren.remove(oldPartition);
parentPartitionCallableChildren.add(index, newPartition);
List<@NonNull Partition> oldPartitionCallableParents = getCallableParents(oldPartition);
oldPartitionCallableParents.remove(parentPartition);
oldPartitionCallableParents.add(parentPartition);
}
}