| /******************************************************************************* |
| * 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.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.analysis.PartialRegionAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionsAnalysis; |
| 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.CollectionClassDatum; |
| 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.SharedEdge; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.DomainUsage; |
| 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"; |
| private static final @NonNull String JOIN_SHARED_PREFIX = "js"; |
| |
| 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 PartialRegionAnalysis<@NonNull PartitionsAnalysis> 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 predicatedEdges and/or their target DataType node. |
| */ |
| private void createAttributeEdgeConnection(@Nullable StringBuilder s, @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) { |
| if (predicatedEdge.isNavigation()) { |
| NavigationEdge predicatedNavigationEdge = (NavigationEdge)predicatedEdge; |
| List<@NonNull NavigableEdge> attributeConnectionSourceEdges = null; |
| List<@NonNull String> partialNames = new ArrayList<>(); |
| assert predicatedEdge.isNavigation(); |
| assert predicatedEdge.getIncomingConnection() == null; |
| assert !predicatedEdge.isCast(); |
| Property predicatedProperty = QVTscheduleUtil.getReferredProperty(predicatedNavigationEdge); |
| assert !predicatedProperty.isIsImplicit(); |
| boolean isDataType = classDatum.isDataType(); |
| assert isDataType; |
| Iterable<@NonNull NavigationEdge> realizedEdges = getNewEdges(predicatedEdge, classDatum); |
| if (realizedEdges != null) { |
| ClassDatum predicatedSourceClassDatum = QVTscheduleUtil.getClassDatum(QVTscheduleUtil.getSourceNode(predicatedEdge)); |
| ClassDatum predicatedTargetClassDatum = QVTscheduleUtil.getClassDatum(QVTscheduleUtil.getTargetNode(predicatedEdge)); |
| Property oppositeProperty = predicatedProperty.getOpposite(); |
| Boolean isOneToMany = predicatedProperty.isIsMany() && ((oppositeProperty != null) && !oppositeProperty.isIsMany()); |
| if (isOneToMany) { |
| // Type type = predicatedTargetClassDatum.getPrimaryClass(); |
| // Type elementType = QVTbaseUtil.getElementType(((CollectionType)type)); |
| // predicatedTargetClassDatum = scheduleManager.getEnvironmentFactory().getCompleteModel().getCompleteClass(elementType); |
| predicatedTargetClassDatum = QVTscheduleUtil.getElementalClassDatum((CollectionClassDatum)predicatedTargetClassDatum); |
| } |
| for (@NonNull NavigationEdge realizedEdge : realizedEdges) { |
| ClassDatum firstClassDatum = QVTscheduleUtil.getClassDatum(QVTscheduleUtil.getSourceNode(realizedEdge)); |
| ClassDatum secondTargetClassDatum = QVTscheduleUtil.getClassDatum(QVTscheduleUtil.getTargetNode(realizedEdge)); |
| ClassDatum realizedSourceClassDatum; |
| ClassDatum realizedTargetClassDatum; |
| Property realizedProperty = QVTscheduleUtil.getReferredProperty(realizedEdge); |
| if (realizedProperty == predicatedProperty) { |
| realizedSourceClassDatum = firstClassDatum; |
| realizedTargetClassDatum = secondTargetClassDatum; |
| } |
| else { |
| assert realizedProperty == oppositeProperty; |
| realizedSourceClassDatum = secondTargetClassDatum; |
| realizedTargetClassDatum = firstClassDatum; |
| } |
| boolean conformingSources = QVTscheduleUtil.conformantWith(predicatedSourceClassDatum, realizedSourceClassDatum); |
| boolean conformingTargets; |
| // if (isOneToMany) { |
| conformingTargets = QVTscheduleUtil.conformantWith(predicatedTargetClassDatum, realizedTargetClassDatum); |
| if (conformingSources && conformingTargets) { |
| if (attributeConnectionSourceEdges == null) { |
| attributeConnectionSourceEdges = new ArrayList<>(); |
| } |
| attributeConnectionSourceEdges.add(realizedEdge); |
| } |
| else { |
| // assert false; |
| } |
| } |
| /* } |
| else { |
| conformingTargets = areConforming(predicatedTargetCompleteClass, realizedTargetCompleteClass); |
| if (scheduleManager.isElementallyConformantSource(realizedEdge, predicatedEdge) && QVTscheduleUtil.isConformantTarget(realizedEdge, predicatedEdge)) { |
| assert conformingSources && conformingTargets; |
| if (attributeConnectionSourceEdges == null) { |
| attributeConnectionSourceEdges = new ArrayList<>(); |
| } |
| attributeConnectionSourceEdges.add(realizedEdge); |
| } |
| else { |
| assert !conformingSources || !conformingTargets; |
| } |
| } */ |
| // } |
| Node sourceNode = QVTscheduleUtil.getSourceNode(predicatedEdge); |
| ClassDatum sourceClassDatum = QVTscheduleUtil.getClassDatum(sourceNode); |
| partialNames.add(QVTscheduleUtil.getName(sourceClassDatum)); |
| partialNames.add(QVTscheduleUtil.getName(predicatedProperty)); |
| } |
| if (attributeConnectionSourceEdges != null) { |
| EdgeConnection edgeConnection = getAttributeConnection(invokingRegion2, attributeConnectionSourceEdges, partialNames, predicatedProperty); |
| edgeConnection.addUsedTargetEdge(predicatedEdge, false); |
| if (s != null) { |
| s.append("\n Attribute EdgeConnection \"" + edgeConnection + "\" to " + castTarget); |
| for (@NonNull Edge sourceEdge : attributeConnectionSourceEdges) { |
| s.append("\n from " + sourceEdge.getOwningRegion() + " : " + sourceEdge.getSourceNode()); |
| } |
| // 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); |
| // } |
| } |
| } |
| if (attributeConnectionSourceEdges != null) { |
| EdgeConnection edgeConnection = getAttributeConnection(invokingRegion2, attributeConnectionSourceEdges, partialNames, predicatedProperty); |
| edgeConnection.addUsedTargetEdge(predicatedEdge, false); |
| if (s != null) { |
| s.append("\n Attribute EdgeConnection \"" + edgeConnection + "\" to " + castTarget); |
| for (@NonNull Edge sourceEdge : attributeConnectionSourceEdges) { |
| s.append("\n from " + sourceEdge.getOwningRegion() + " : " + sourceEdge.getSourceNode()); |
| } |
| // 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); |
| // } |
| } |
| } |
| } |
| else { |
| // Shared Edge |
| } |
| } |
| } |
| |
| /** |
| * Create an EdgeConnection for the predicatedEdges and/or their target Class node. |
| */ |
| private void createClassEdgeConnection(@Nullable StringBuilder s, @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 = QVTscheduleUtil.getReferredProperty((NavigationEdge)predicatedEdge); |
| assert !predicatedProperty.isIsImplicit(); |
| boolean isDataType = classDatum.isDataType(); |
| assert !isDataType; |
| Iterable<@NonNull Node> sourceNodes = getNewNodes(classDatum); |
| // if (sourceNodes != null) { |
| Iterable<@NonNull NavigationEdge> 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 (s != null) { |
| s.append("\n EdgeConnection \"" + edgeConnection + "\" to " + predicatedEdge); |
| } |
| if (!Iterables.contains(edgeConnection.getTargetEdges(), predicatedEdge)) { |
| edgeConnection.addUsedTargetEdge(predicatedEdge, false); |
| if (s != null) { |
| for (@NonNull NavigableEdge thatEdge : thoseEdges) { |
| s.append("\n 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); |
| predicatedConnection.addUsedTargetNode(castTarget, false); |
| if (s != null) { |
| s.append("\n NodeConnection \"" + predicatedConnection + "\" to " + castTarget); |
| for (@NonNull Node sourceNode : sourceNodes) { |
| s.append("\n 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(@Nullable StringBuilder s, @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); |
| if (headNode.isDependency()) { |
| headConnection.addUsedTargetNode(headNode, false); |
| } |
| else { |
| headConnection.addPassedTargetNode(headNode); |
| } |
| if (s != null) { |
| s.append((headNode.isDependency() ? "\n Extra NodeConnection " : "\n Head NodeConnection \"") + headConnection + "\" to " + headNode); |
| for (@NonNull Node sourceNode : headSources) { |
| s.append("\n from " + sourceNode.getOwningRegion() + " : " + sourceNode); |
| } |
| } |
| return headConnection; |
| } |
| |
| /** |
| * Return the Connections to each of the head nodes. Returns null after registering region error problems, |
| * 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(@Nullable StringBuilder s, @NonNull RootRegion rootRegion, @NonNull Region region) { |
| List<@NonNull NodeConnection> headConnections = null; |
| Iterable<@NonNull Node> headNodes = QVTscheduleUtil.getHeadNodes(region); |
| if (Iterables.isEmpty(headNodes)) { |
| scheduleManager.addRegionError(region, "No head nodes"); |
| } |
| for (@NonNull Node headNode : headNodes) { |
| NodeConnection headConnection = null; |
| SharedEdge sharedEdge = isShared(headNode); |
| if (headNode.isDependency()) { |
| createHeadConnection(s, rootRegion, region, headNode); /** Dependency nodes have extra not-head connections. */ |
| } |
| else if (sharedEdge != null) { |
| headConnection = createSharedHeadConnection(s, rootRegion, region, sharedEdge); |
| } |
| else { |
| headConnection = createHeadConnection(s, rootRegion, region, headNode); |
| if (headConnection == null) { |
| scheduleManager.addRegionError(region, "No incoming connections for " + headNode.getName()); |
| headConnection = createHeadConnection(s, rootRegion, region, headNode); // FIXME debugging |
| return null; // so matching only fails for unmatchable real heads |
| } |
| } |
| if (headConnection != null) { |
| 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. |
| // } |
| } |
| if (headConnections == null) { |
| scheduleManager.addRegionError(region, "No incoming connections"); |
| } |
| if (headConnections == null) { |
| scheduleManager.addRegionError(region, "No incoming connections"); |
| } |
| 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(@Nullable StringBuilder s, @NonNull RootRegion rootRegion, @NonNull Region region) { |
| if (s != null) { |
| s.append("\n " + region); |
| } |
| assert !(region instanceof LoadingRegion); |
| Iterable<@NonNull NodeConnection> headConnections = createHeadConnections(s, rootRegion, region); |
| if (headConnections == null) { |
| return; // Error already reported. |
| } |
| // |
| // Gather multiple edges sharing the same target to avoid multiple incoming connections -- FIXME no need to gather |
| // |
| Map<@NonNull Node, @NonNull List<@NonNull NavigableEdge>> targetNode2predicatedEdges = new HashMap<>(); |
| for (@NonNull Edge edge : QVTscheduleUtil.getOwnedEdges(region)) { |
| assert !edge.isCast(); |
| if (edge.isPredicated()) { |
| if (edge.isNavigation()) { |
| NavigationEdge predicatedEdge = (NavigationEdge)edge; |
| assert predicatedEdge.getIncomingConnection() == null; |
| Property predicatedProperty = predicatedEdge.getReferredProperty(); |
| if (!predicatedProperty.isIsImplicit()) { // unnavigable opposites are handled by the navigable property |
| Node targetNode = predicatedEdge.getEdgeTarget(); |
| List<@NonNull NavigableEdge> predicatedEdges = targetNode2predicatedEdges.get(targetNode); |
| if (predicatedEdges == null) { |
| predicatedEdges = new ArrayList<>(); |
| targetNode2predicatedEdges.put(targetNode, predicatedEdges); |
| } |
| predicatedEdges.add(predicatedEdge); |
| } |
| } |
| else if (edge.isShared()) { |
| SharedEdge callingSharedEdge = (SharedEdge)edge; |
| SharedEdge calledSharedEdge = originalContentsAnalysis.getNewSharedEdge((SharedEdge)edge); |
| if (calledSharedEdge == null) { |
| scheduleManager.addRegionError(region, "no source for " + callingSharedEdge); |
| } |
| // else { |
| // NodeConnection ec = getNodeConnection(rootRegion, Collections.singletonList(sourceNode), classDatum, scheduleManager.getDomainUsage(classDatum)); |
| // } |
| /* assert predicatedEdge.getIncomingConnection() == null; |
| Property predicatedProperty = predicatedEdge.getReferredProperty(); |
| if (!predicatedProperty.isIsImplicit()) { // unnavigable opposites are handled by the navigable property |
| Node castTargetNode = predicatedEdge.getEdgeTarget(); |
| List<@NonNull NavigableEdge> predicatedEdges = castTargetNode2predicatedEdges.get(castTargetNode); |
| if (predicatedEdges == null) { |
| predicatedEdges = new ArrayList<>(); |
| castTargetNode2predicatedEdges.put(castTargetNode, predicatedEdges); |
| } |
| predicatedEdges.add(predicatedEdge); |
| } */ |
| } |
| } |
| } |
| for (@NonNull Node targetNode : targetNode2predicatedEdges.keySet()) { |
| List<@NonNull NavigableEdge> predicatedEdges = targetNode2predicatedEdges.get(targetNode); |
| assert predicatedEdges != null; |
| if (targetNode.isClass()) { |
| createClassEdgeConnection(s, rootRegion, region, targetNode, predicatedEdges); |
| } |
| else { |
| createAttributeEdgeConnection(s, rootRegion, region, targetNode, 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); |
| // |
| 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 Edge edge : QVTscheduleUtil.getOwnedEdges(region)) { |
| if (edge.isRealized() && edge.isNavigation()) { |
| NavigationEdge navigationEdge = (NavigationEdge)edge; |
| if (!edge.isSecondary()) { |
| boolean isAwaited = false; |
| for (@NonNull PartitionAnalysis partitionAnalysis : regionAnalysis.getPartitionAnalyses()) { |
| Role role = partitionAnalysis.getPartition().getRole(navigationEdge); |
| if ((role != null) && role.isChecked()) { |
| isAwaited = true; |
| break; |
| } |
| } |
| if (isAwaited) { |
| Property property = QVTscheduleUtil.getReferredProperty(navigationEdge); |
| EdgeConnection connection = getEdgeConnection(rootRegion, Collections.singleton(navigationEdge), property); |
| connection.addUsedTargetEdge(navigationEdge, true); |
| } |
| } |
| } |
| else { |
| // SharedEdge |
| } |
| } |
| } |
| |
| private @Nullable NodeConnection createSharedHeadConnection(@Nullable StringBuilder s, @NonNull RootRegion rootRegion, @NonNull Region region, @NonNull SharedEdge sharedEdge) { |
| Node headNode = QVTscheduleUtil.getSourceNode(sharedEdge); |
| Node sharedNode = QVTscheduleUtil.getTargetNode(sharedEdge); |
| ClassDatum classDatum = QVTscheduleUtil.getClassDatum(sharedNode); |
| Iterable<@NonNull SharedEdge> oldSharedEdges = originalContentsAnalysis.getOldSharedEdges(sharedEdge); |
| if (oldSharedEdges != null) { |
| List<@NonNull Node> sourceNodes = new ArrayList<>(); |
| for (@NonNull SharedEdge edge : oldSharedEdges) { |
| sourceNodes.add(QVTscheduleUtil.getSourceNode(edge)); |
| } |
| NodeConnection connection = getNodeConnection(rootRegion, sourceNodes, classDatum); |
| connection.setDataType(true); |
| // 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(headNode); |
| // } |
| // } |
| // } |
| return connection; |
| } |
| return null; |
| } |
| |
| 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; |
| } |
| |
| private int getFirstConsumption(@NonNull Connection connection) { |
| int firstConsumption = Integer.MAX_VALUE; |
| for (@NonNull Partition targetPartition : connection.getTargetPartitions()) { |
| int firstPass = targetPartition.getFirstPass(); |
| if (firstPass <= firstConsumption) { |
| firstConsumption = firstPass; |
| } |
| } |
| assert firstConsumption != Integer.MAX_VALUE; |
| return firstConsumption; |
| } |
| |
| public @NonNull Iterable<@NonNull Connection> getIncomingConnections(@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis> 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()) { |
| assert !edge.isCast(); |
| if (((PartitionAnalysis)partitionAnalysis).isChecked(edge) && edge.isNavigation()) { |
| NavigableEdge navigationEdge = (NavigableEdge) 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.getCompleteClasses()); |
| Iterable<@NonNull Node> newNodes = originalContentsAnalysis.getNewNodes(classDatum2); |
| if (newNodes != null) { |
| for (@NonNull Node newNode : newNodes) { |
| if (!nodes.contains(newNode)) { |
| nodes.add(newNode); |
| } |
| } |
| } |
| } |
| return nodes; |
| } |
| |
| private int getLastConsumption(@NonNull Connection connection) { |
| int lastConsumption = -1; |
| for (@NonNull Partition targetPartition : connection.getTargetPartitions()) { |
| int lastPass = targetPartition.getLastPass(); |
| if (lastPass > lastConsumption) { |
| lastConsumption = lastPass; |
| } |
| } |
| assert lastConsumption >= 0; |
| return lastConsumption; |
| } |
| |
| 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 NavigationEdge> getNewEdges(@NonNull NavigableEdge edge, @NonNull ClassDatum requiredClassDatum) { |
| return originalContentsAnalysis.getNewEdges(edge, requiredClassDatum); |
| } |
| |
| // private @Nullable Iterable<@NonNull NavigableEdge> getNewInverseEdges(@NonNull NavigableEdge edge, @NonNull ClassDatum requiredClassDatum) { |
| // return originalContentsAnalysis.getNewInverseEdges(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) { |
| 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) { |
| DomainUsage domainUsage = scheduleManager.getDomainUsage(classDatum); |
| SymbolNameBuilder s = new SymbolNameBuilder(); |
| if (domainUsage.isInput()) { |
| s.appendString(JOIN_INPUT_PREFIX); |
| } |
| else if (domainUsage.isOutput()) { |
| s.appendString(JOIN_OUTPUT_PREFIX); |
| } |
| else { |
| boolean isShared = false; |
| for (@NonNull Node node : sourceNodes) { |
| if (isShared(node) != null) { |
| isShared = true; |
| } |
| } |
| if (isShared) { |
| s.appendString(JOIN_SHARED_PREFIX); |
| } |
| else { |
| s.appendString(JOIN_MIDDLE_PREFIX); |
| } |
| } |
| s.appendString("_"); |
| s.appendName(classDatum.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 : QVTscheduleUtil.getOutgoingConnections(node)) { |
| if (connection.isPassed()) { |
| connections.add(connection); |
| } |
| } |
| for (@NonNull NodeConnection connection : QVTscheduleUtil.getOutgoingConnections(node)) { |
| if (connection.isUsed()) { |
| 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 Edge calledEdge : QVTscheduleUtil.getOutgoingEdges(calledNode)) { |
| if (calledEdge instanceof NavigationEdge) { |
| NavigationEdge calledNavigationEdge = (NavigationEdge)calledEdge; |
| Node nextCalledNode = calledEdge.getEdgeTarget(); |
| if (!nextCalledNode.isRealized() && !nextCalledNode.isDataType()) { // FIXME why exclude AttributeNodes? |
| Edge nextCallingEdge = callingNode.getOutgoingNavigableEdge(QVTscheduleUtil.getReferredProperty(calledNavigationEdge)); |
| if (nextCallingEdge != null) { |
| Node nextCallingNode = nextCallingEdge.getEdgeTarget(); |
| if ((nextCallingNode.isNullLiteral() != nextCalledNode.isNullLiteral())) { |
| return false; |
| } |
| if (!isCompatiblePattern(region, nextCalledNode, nextCallingNode, called2calling)) { |
| return false; |
| } |
| } |
| } |
| } |
| else { |
| // SharedEdge |
| } |
| } |
| return true; |
| } |
| |
| public boolean isHazardousRead(@Nullable StringBuilder s, @NonNull Partition partition, @NonNull NavigationEdge edge) { |
| Property property = QVTscheduleUtil.getReferredProperty(edge); |
| @SuppressWarnings("unused") String name = property.getName(); |
| Property oppositeProperty = property.getOpposite(); |
| @SuppressWarnings("unused") String oppositeName = oppositeProperty != null ? oppositeProperty.getName() : null; |
| int firstConsumption = partition.getFirstPass(); |
| int firstProduction = Integer.MAX_VALUE; |
| int lastConsumption = partition.getLastPass(); |
| int lastProduction = -1; |
| Connection connection = edge.getIncomingConnection(); |
| if (connection != null) { |
| int firstWrite = connection.getFirstPass(); |
| if (firstWrite < firstProduction) { |
| firstProduction = firstWrite; |
| } |
| int lastWrite = connection.getLastPass(); |
| if (lastWrite > lastProduction) { |
| lastProduction = lastWrite; |
| } |
| } |
| if (s != null) { |
| if (lastProduction < 0) { |
| s.append("\n unconnected"); |
| } |
| else if (lastProduction < firstConsumption) { |
| s.append("\n no-observe produce:[" + firstProduction +".." + lastProduction +"] consume:[" + firstConsumption +".." + lastConsumption +"]"); |
| } |
| else { |
| s.append("\n observe produce:[" + firstProduction +".." + lastProduction +"] consume:[" + firstConsumption +".." + lastConsumption +"]"); |
| } |
| org.eclipse.ocl.pivot.Class owningClass = property.getOwningClass(); |
| s.append(" " + (owningClass != null ? owningClass.getName() : "«cast»") + "::" + property.getName()); |
| if (oppositeProperty !=null) { |
| s.append(" <=> " + oppositeProperty.getOwningClass().getName() + "::" + oppositeProperty.getName()); |
| } |
| } |
| return lastProduction >= firstConsumption; |
| } |
| |
| public boolean isHazardousWrite(@Nullable StringBuilder s, @NonNull NavigationEdge edge) { |
| Property property = QVTscheduleUtil.getReferredProperty(edge); |
| @SuppressWarnings("unused") String name = property.getName(); |
| Property oppositeProperty = property.getOpposite(); |
| @SuppressWarnings("unused") String oppositeName = oppositeProperty != null ? oppositeProperty.getName() : null; |
| int firstConsumption = Integer.MAX_VALUE; |
| int firstProduction = Integer.MAX_VALUE; |
| int lastConsumption = -1; |
| int lastProduction = -1; |
| for (@NonNull Connection connection : QVTscheduleUtil.getOutgoingConnections(edge)) { |
| int firstRead = getFirstConsumption(connection); |
| if (firstRead < firstConsumption) { |
| firstConsumption = firstRead; |
| } |
| int lastRead = getLastConsumption(connection); |
| if (lastRead > lastConsumption) { |
| lastConsumption = lastRead; |
| } |
| int firstWrite = connection.getFirstPass(); |
| if (firstWrite < firstProduction) { |
| firstProduction = firstWrite; |
| } |
| int lastWrite = connection.getLastPass(); |
| if (lastWrite > lastProduction) { |
| lastProduction = lastWrite; |
| } |
| } |
| if (s != null) { |
| if (lastProduction < 0) { |
| s.append("\n unconnected"); |
| } |
| else if (lastProduction < firstConsumption) { |
| s.append("\n no-notify produce:[" + firstProduction +".." + lastProduction +"] consume:[" + firstConsumption +".." + lastConsumption +"]"); |
| } |
| else { |
| s.append("\n notify produce:[" + firstProduction +".." + lastProduction +"] consume:[" + firstConsumption +".." + lastConsumption +"]"); |
| } |
| s.append(" " + property.getOwningClass().getName() + "::" + property.getName()); |
| if (oppositeProperty !=null) { |
| s.append(" <=> " + oppositeProperty.getOwningClass().getName() + "::" + oppositeProperty.getName()); |
| } |
| } |
| return lastProduction >= firstConsumption; |
| } |
| |
| private @Nullable SharedEdge isShared(@NonNull Node headNode) { |
| for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(headNode)) { |
| if (outgoingEdge.isShared()) { |
| return (SharedEdge) outgoingEdge; |
| } |
| } |
| return null; |
| } |
| |
| 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); |
| } |
| } |