blob: 268ac484883ae90fd1f0c25cc2e648693a086dea [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.UniqueList;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.AbstractTransformationAnalysis;
import org.eclipse.qvtd.compiler.internal.qvtm2qvts.QVTm2QVTs;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.utilities.ReachabilityForest;
import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil;
import org.eclipse.qvtd.pivot.qvtschedule.BasicPartition;
import org.eclipse.qvtd.pivot.qvtschedule.BooleanLiteralNode;
import org.eclipse.qvtd.pivot.qvtschedule.ClassDatum;
import org.eclipse.qvtd.pivot.qvtschedule.Edge;
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.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.SuccessNode;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* ReachabilityPartitioningStrategy partitions in accordance with the reachability and dependencies avoiding
* any special heuristics for when/where/....
*/
public class ReachabilityPartitioningStrategy extends AbstractPartitioningStrategy
{
protected abstract static class AbstractReachabilityPartitionFactory extends AbstractSimplePartitionFactory
{
/**
* The strategy that supervises the partition and accumulates the novelties.
*/
protected final @NonNull ReachabilityPartitioningStrategy strategy;
/**
* The name for the partition.
*/
protected final @NonNull String name;
/**
* The depth and reachability of nodes in the partition.
*/
protected final @NonNull ReachabilityForest reachabilityForest;
/**
* The nodes from the partitioned region that are the pattern match entry points for this partition.
*/
protected final @NonNull Iterable<@NonNull ? extends Node> headNodes;
/**
* The nodes that must appear in the partitioned region and which do not appear in a preceding partition.
*/
protected final @Nullable Iterable<@NonNull ? extends Node> novelNodes;
/**
* The edges that must appear in the partitioned region and which do not appear in a preceding partition.
*/
protected final @Nullable Iterable<@NonNull ? extends Edge> novelEdges;
/**
* The partition factories for the partitions that precede this partition.
*/
private final @NonNull UniqueList<@NonNull AbstractReachabilityPartitionFactory> predecessors = new UniqueList<>();
/**
* The nodes from the partitioned region that are to appear in this partition.
*/
private final @NonNull List<@NonNull Node> reachableNodes = new ArrayList<>();
/**
* The edges from the partitioned region that are to appear in this partition.
*/
private final @NonNull List<@NonNull Edge> reachableEdges = new ArrayList<>();
protected AbstractReachabilityPartitionFactory(@NonNull ReachabilityPartitioningStrategy strategy, @NonNull ReachabilityForest reachabilityForest,
@NonNull Iterable<@NonNull ? extends Node> headNodes, @Nullable Iterable<@NonNull ? extends Node> novelNodes, @Nullable Iterable<@NonNull ? extends Edge> novelEdges) {
super(strategy.mappingPartitioner);
this.strategy = strategy;
strategy.setCurrentPartitionFactory(this);
this.name = computeName(ClassUtil.nonNullState(reachabilityForest.getDisambiguator()));
this.reachabilityForest = reachabilityForest;
this.headNodes = headNodes;
this.novelNodes = novelNodes;
this.novelEdges = novelEdges;
if (novelNodes != null) {
for (@NonNull Node node : novelNodes) {
addReachableNode(node);
}
}
if (novelEdges != null) {
for (@NonNull Edge edge : novelEdges) {
addReachableEdge(edge);
}
}
}
public void addPredecessor(@NonNull AbstractReachabilityPartitionFactory predecessor) {
predecessors.add(predecessor);
}
/**
* Add edge and its source/targert nodes to the partition.
*
* Returns true if edge added, false if already present.
*/
private boolean addReachableEdge(@NonNull Edge edge) {
if (reachableEdges.contains(edge)) {
return false;
}
addReachableNode(QVTscheduleUtil.getSourceNode(edge));
addReachableNode(QVTscheduleUtil.getTargetNode(edge));
reachableEdges.add(edge);
strategy.addEdge(this, edge);
return true;
}
/**
* Add node and all transitively preceding nodes to the partition.
*
* Returns true if node added, false if already present.
*/
private boolean addReachableNode(@NonNull Node node) {
if (reachableNodes.contains(node)) {
return false;
}
reachableNodes.add(node);
strategy.addNode(this, node);
for (@NonNull Node predecessor : reachabilityForest.getPredecessorsClosure(node)) {
addReachableNode(predecessor);
}
return true;
}
public boolean containsNode(@NonNull Node node) {
return reachableNodes.contains(node);
}
@Override
public @NonNull PartitionAnalysis createPartitionAnalysis(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis) {
// Iterable<@NonNull Node> headNodes = QVTscheduleUtil.getHeadNodes(region);
BasicPartition partition = createBasicPartition(name, headNodes);
for (@NonNull Node node : reachableNodes) {
Role role = resolveNodeRole(node);
addNode(partition, node, role);
}
for (@NonNull Edge edge : reachableEdges) {
if (!edge.isSecondary()) {
Role role = resolveEdgeRole(edge);
addEdge(partition, edge, role);
}
}
// Iterable<@NonNull Node> headNodes = QVTscheduleUtil.getHeadNodes(region);
// BasicPartition partition = createBasicPartition(name, headNodes);
BasicPartitionAnalysis basicPartitionAnalysis = new BasicPartitionAnalysis(partitionedTransformationAnalysis, partition, reachabilityForest, "xyzzy", "xyzzy");
// initializePartition(basicPartitionAnalysis);
if (QVTm2QVTs.DEBUG_GRAPHS.isActive()) {
scheduleManager.writeDebugGraphs(partition, null);
}
return basicPartitionAnalysis;
}
protected void initPartitionFactory() {
//
// Grow the nodes to include all not-yet reached reachable nodes.
//
for (int i = 0; i < reachableNodes.size(); i++) {
@NonNull Node sourceNode = reachableNodes.get(i);
for (@NonNull Edge edge : QVTscheduleUtil.getOutgoingEdges(sourceNode)) {
Node targetNode = QVTscheduleUtil.getTargetNode(edge);
AbstractReachabilityPartitionFactory targetPartitionFactory = strategy.basicGetPartitionFactory(targetNode);
if (targetPartitionFactory == null) {
if (edge.isLoaded()) {
addReachableNode(targetNode);
}
}
}
}
//
// Add the edges to nodes to include all not-yet reached reachable nodes.
//
for (@NonNull Node targetNode : reachableNodes) {
for (@NonNull Edge edge : QVTscheduleUtil.getIncomingEdges(targetNode)) {
Node sourceNode = QVTscheduleUtil.getSourceNode(edge);
AbstractReachabilityPartitionFactory sourcePartitionFactory = strategy.basicGetPartitionFactory(sourceNode);
if ((sourcePartitionFactory != null) && reachableNodes.contains(sourceNode)) {
addReachableEdge(edge);
if (sourcePartitionFactory != this) {
addPredecessor(sourcePartitionFactory);
}
}
}
}
}
protected @NonNull Role resolveEdgeRole(@NonNull Edge edge) {
Role edgeRole = QVTscheduleUtil.getEdgeRole(edge);
if ((edgeRole == Role.REALIZED) && (strategy.getPartitionFactory(edge) != this)) {
return Role.PREDICATED;
}
return edgeRole;
}
@Override
protected @Nullable Role resolveEdgeRole(@NonNull Role sourceNodeRole, @NonNull Edge edge, @NonNull Role targetNodeRole) {
if (mappingPartitioner.hasRealizedEdge(edge)) {
return Role.PREDICATED;
}
Role edgeRole = QVTscheduleUtil.getEdgeRole(edge);
// if (edgeRole == Role.REALIZED) {
// assert !mappingPartitioner.hasRealizedEdge(edge);
// }
return edgeRole;
}
protected @NonNull Role resolveNodeRole(@NonNull Node node) {
Role nodeRole = QVTscheduleUtil.getNodeRole(node);
if ((nodeRole == Role.REALIZED) && (strategy.getPartitionFactory(node) != this)) {
return Role.PREDICATED;
}
return nodeRole;
}
@Override
public @NonNull String toString() {
return toString(new StringBuilder(), "\n");
}
public @NonNull String toString(@NonNull StringBuilder s, @NonNull String prefix) {
s.append(name);
s.append(prefix + "\tpredecessors: ");
for (@NonNull AbstractReachabilityPartitionFactory predecessor : predecessors) {
s.append(" " + predecessor.name);
}
Iterable<@NonNull ? extends Node> novelNodes2 = novelNodes;
if (novelNodes2 != null) {
s.append(prefix + "\tnovel nodes");
for (@NonNull Node node : novelNodes2) {
s.append(prefix + "\t\t" + node);
}
}
Iterable<@NonNull ? extends Edge> novelEdges2 = novelEdges;
if (novelEdges2 != null) {
s.append(prefix + "\tnovel edges");
for (@NonNull Edge edge : novelEdges2) {
s.append(prefix + "\t\t" + edge);
}
}
s.append(prefix + "\treachable nodes");
for (@NonNull Node node : reachableNodes) {
s.append(prefix + "\t\t" + node);
}
s.append(prefix + "\treachable edges");
for (@NonNull Edge edge : reachableEdges) {
s.append(prefix + "\t\t" + edge);
}
return s.toString();
}
}
/**
* The CtorPartitionFactory creates the trace node and links to the head node(s). If there are multiple
* headnodes, inter-head edges are also added so that once the ctor partition is hoisted into the caller,
* the caller may improve performance by considering only appropriately inter-related heads.
*/
protected static class CtorPartitionFactory extends AbstractReachabilityPartitionFactory
{
protected CtorPartitionFactory(@NonNull ReachabilityPartitioningStrategy strategy, @NonNull ReachabilityForest reachabilityForest,
@NonNull Iterable<@NonNull ? extends Node> headNodes, @NonNull Iterable<@NonNull ? extends Node> novelNodes, @NonNull Iterable<@NonNull ? extends Edge> novelEdges) {
super(strategy, reachabilityForest, headNodes, novelNodes, novelEdges);
// initPartitionFactory(); // no magic growth
}
@Override
protected void initPartitionFactory() {
throw new UnsupportedOperationException(); // All done in constructor, no magic growth.
}
@Override
protected @NonNull Role resolveNodeRole(@NonNull Node node) {
Role nodeRole = node.getNodeRole();
if (nodeRole == Role.REALIZED) {
return Role.SPECULATION;
}
return super.resolveNodeRole(node);
}
}
/**
* The InitPartitionFactory checks all predicates that can be established independent of the success of concurrent partitions.
* The local success is assigned on completion.
*/
protected static class InitPartitionFactory extends AbstractReachabilityPartitionFactory
{
protected InitPartitionFactory(@NonNull ReachabilityPartitioningStrategy strategy, @NonNull ReachabilityForest reachabilityForest,
@NonNull Iterable<@NonNull ? extends Node> headNodes, @NonNull Iterable<@NonNull ? extends Node> novelNodes) {
super(strategy, reachabilityForest, headNodes, novelNodes, null);
initPartitionFactory();
}
@Override
protected @NonNull Role resolveNodeRole(@NonNull Node node) {
if (node.isTrace()) {
return Role.SPECULATED;
}
return super.resolveNodeRole(node);
}
}
/**
* The LoopPartitionFactory defines the decision stage in the execution.
*
* The preceding CtorPartitionFactory creates the trace. Then the preceding InitPartitionFactory checks all
* predicates that are dependent solely on earlier executing partitions.
*
* The LoopPartitionFactory checks the remaining non-corrolary predicates that have cyclic dependencies on
* concurrently executing partitions. Once cycle execution has been validated, success is assigned true, and
* all remaining realized nodes are created.
*
* Thereafter the RestPartitionFactory instances realize edges that are predicated solely on corrolary
* predicates.
*/
protected static class LoopPartitionFactory extends AbstractReachabilityPartitionFactory
{
protected LoopPartitionFactory(@NonNull ReachabilityPartitioningStrategy strategy, @NonNull ReachabilityForest reachabilityForest,
@NonNull Iterable<@NonNull ? extends Node> headNodes, @NonNull Iterable<@NonNull ? extends Node> novelNodes, @NonNull Iterable<@NonNull ? extends Edge> novelEdges) {
super(strategy, reachabilityForest, headNodes, novelNodes, novelEdges);
initPartitionFactory();
}
@Override
protected @NonNull Role resolveEdgeRole(@NonNull Edge edge) {
CyclicRegionAnalysis cyclicRegionAnalysis = strategy.basicGetCyclicRegionAnalysis();
if (cyclicRegionAnalysis != null) {
if ((edge.getEdgeRole() == Role.PREDICATED) && (edge instanceof SuccessEdge)) {
assert !edge.isSecondary();
SuccessEdge navigationEdge = (SuccessEdge) edge;
PropertyDatum propertyDatum = strategy.scheduleManager.getPropertyDatum(navigationEdge);
if (cyclicRegionAnalysis.isCyclic(propertyDatum)) {
Node targetNode = QVTscheduleUtil.getTargetNode(navigationEdge);
if ((targetNode instanceof BooleanLiteralNode) && ((BooleanLiteralNode)targetNode).isBooleanValue()) {
return Role.SPECULATED;
}
}
}
}
return super.resolveEdgeRole(edge);
}
@Override
protected @NonNull Role resolveNodeRole(@NonNull Node node) {
if (node.isTrace()) {
return Role.SPECULATED;
}
if (node == strategy.localSuccessNode) {
return Role.CONSTANT_SUCCESS_TRUE;
}
return super.resolveNodeRole(node);
}
}
protected static class RestPartitionFactory extends AbstractReachabilityPartitionFactory
{
protected RestPartitionFactory(@NonNull ReachabilityPartitioningStrategy strategy, @NonNull ReachabilityForest reachabilityForest,
@NonNull Iterable<@NonNull ? extends Node> headNodes, @NonNull Iterable<@NonNull ? extends Node> novelNodes, @NonNull Iterable<@NonNull ? extends Edge> novelEdges) {
super(strategy, reachabilityForest, headNodes, novelNodes, novelEdges);
initPartitionFactory();
}
@Override
protected @NonNull Role resolveNodeRole(@NonNull Node node) {
if (node instanceof SuccessNode) {
return Role.CONSTANT_SUCCESS_TRUE;
}
return super.resolveNodeRole(node);
}
}
/**
* The region to be partitioned,
*/
protected final @NonNull Region region;
/**
* The nodes in the synthesized region. Excludes the additional localSuccess node.
*/
private final @NonNull Iterable<@NonNull Node> originalNodes;
/**
* The edges in the synthesized region. Excludes the additional localSuccess edge.
*/
private final @NonNull Iterable<@NonNull Edge> originalEdges;
// FIXME Merge state into mappingPartitioner once other factories obsolete.
/**
* The ordered list of all ProtoPartition to support the Region.
*/
private final @NonNull List<@NonNull AbstractReachabilityPartitionFactory> partitionFactories = new ArrayList<>();
/**
* Mapping from each Region node to the first ProtoPartition in which the node is used.
*/
private final @NonNull Map<@NonNull Node, @NonNull AbstractReachabilityPartitionFactory> node2partitionFactory = new HashMap<>();
/**
* Mapping from each Region edge to the first ProtoPartition in which the edge is used.
*/
private final @NonNull Map<@NonNull Edge, @NonNull AbstractReachabilityPartitionFactory> edge2partitionFactory = new HashMap<>();
/**
* The node that represemts the transformation instance.
*/
// private final @Nullable Node thisNode;
/**
* The node that traces the region execution.
*
* Currently always a node. If multiple nodes are ever needed for a merge we will probably need
* to actually merge the trace and so again have just one trace node.
*/
// private final @NonNull Node traceNode;
private final @NonNull Iterable<@NonNull Node> thisAndTraceNodes;
/**
* The loop/global success node that is realized in the region.
*/
private final @NonNull SuccessNode globalSuccessNode;
/**
* The init/local success edge that is realized in the region.
*/
private @Nullable SuccessEdge localSuccessEdge = null;
private @Nullable SuccessNode localSuccessNode = null;
// private @Nullable List<@NonNull Node> constantSourceNodes;
private @NonNull List<@NonNull Node> thisAndTraceAndConstantSourceNodes = new ArrayList<>();
private @Nullable AbstractReachabilityPartitionFactory currentPartitionFactory = null;
public ReachabilityPartitioningStrategy(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis, @NonNull MappingPartitioner mappingPartitioner) {
super(partitionedTransformationAnalysis, mappingPartitioner);
assert scheduleManager.useActivators();
this.region = regionAnalysis.getRegion();
this.originalNodes = Lists.newArrayList(QVTscheduleUtil.getOwnedNodes(region));
this.originalEdges = Lists.newArrayList(QVTscheduleUtil.getOwnedEdges(region));
SuccessNode globalSuccessNode = null;
Node thisNode = null;
Node traceNode = null;
for (@NonNull Node node : originalNodes) {
if (node.isThis()) {
assert thisNode == null : "Only one this node permitted in " + region.getName();
thisNode = node;
thisAndTraceAndConstantSourceNodes.add(node);
}
else if (node.isTrace()) {
assert traceNode == null : "Only one trace node permitted in " + region.getName();
traceNode = node;
thisAndTraceAndConstantSourceNodes.add(node);
}
else if (node.isSuccess()) {
assert globalSuccessNode == null : "Only one success node permitted in " + region.getName();
globalSuccessNode = (SuccessNode)node;
}
else if (node.isConstant() && Iterables.isEmpty(node.getIncomingEdges())) {
thisAndTraceAndConstantSourceNodes.add(node);
}
}
assert globalSuccessNode != null : "No global success node in " + region.getName();
this.globalSuccessNode = globalSuccessNode;
// this.thisNode = thisNode;
assert traceNode != null : "No trace node in " + region.getName();
// this.traceNode = traceNode;
this.thisAndTraceNodes = thisNode != null ? Lists.newArrayList(thisNode, traceNode) : Collections.singletonList(traceNode);
}
private void addEdge(@NonNull AbstractReachabilityPartitionFactory partitionFactory, @NonNull Edge edge) {
if (!edge2partitionFactory.containsKey(edge)) {
edge2partitionFactory.put(edge, partitionFactory);
}
}
private void addNode(@NonNull AbstractReachabilityPartitionFactory partitionFactory, @NonNull Node node) {
if (!node2partitionFactory.containsKey(node)) {
node2partitionFactory.put(node, partitionFactory);
}
}
private @Nullable CyclicRegionAnalysis basicGetCyclicRegionAnalysis() {
return transformationAnalysis.basicGetCyclicRegionAnalysis(regionAnalysis);
}
protected @Nullable AbstractReachabilityPartitionFactory basicGetPartitionFactory(@NonNull Edge edge) {
return edge2partitionFactory.get(edge);
}
protected @Nullable AbstractReachabilityPartitionFactory basicGetPartitionFactory(@NonNull Node node) {
return node2partitionFactory.get(node);
}
@Override
protected void check() {
super.check();
for (@NonNull Node node : QVTscheduleUtil.getOwnedNodes(region)) {
if (node.isPredicated()) {
if (node2partitionFactory.get(node) == null) {
CompilerUtil.addRegionError(mappingPartitioner.getProblemHandler(), region, "Should have predicated " + node);
}
}
}
for (@NonNull Edge edge : QVTscheduleUtil.getOwnedEdges(region)) {
if (edge.isPredicated()) {
if (edge2partitionFactory.get(edge) == null) {
CompilerUtil.addRegionError(mappingPartitioner.getProblemHandler(), region, "Should have predicated " + edge);
}
}
}
}
private void createCtorPartitionFactory() {
Iterable<@NonNull Node> headNodes = QVTscheduleUtil.getHeadNodes(region);
List<@NonNull Node> novelCtorNodes = new ArrayList<>();
List<@NonNull NavigableEdge> novelCtorEdges = new ArrayList<>();
//
// Gather all head-to-head edges
//
for (@NonNull Node headNode : headNodes) {
novelCtorNodes.add(headNode);
for (@NonNull Edge novelEdge : QVTscheduleUtil.getOutgoingEdges(headNode)) {
if ((novelEdge instanceof NavigableEdge) && novelEdge.isLoaded() && Iterables.contains(headNodes, QVTscheduleUtil.getTargetNode(novelEdge))) {
novelCtorEdges.add((NavigableEdge)novelEdge);
}
}
}
//
// Gather all head-to-trace edges
//
for (@NonNull Node traceNode : thisAndTraceNodes) {
if (!novelCtorNodes.contains(traceNode)) { // dispatched head is a trace node
novelCtorNodes.add(traceNode);
for (@NonNull Edge novelEdge : QVTscheduleUtil.getOutgoingEdges(traceNode)) {
if ((novelEdge instanceof NavigableEdge) && Iterables.contains(headNodes, QVTscheduleUtil.getTargetNode(novelEdge))) {
novelCtorEdges.add((NavigableEdge)novelEdge);
}
}
}
}
if (!novelCtorEdges.isEmpty() || (novelCtorNodes.size() > 1)) { // Skip trivial trace only partition
ReachabilityForest ctorReachabilityForest = new ReachabilityForest("ctor", headNodes, novelCtorEdges);
new CtorPartitionFactory(this, ctorReachabilityForest, headNodes, novelCtorNodes, novelCtorEdges);
}
}
private void createInitPartitionFactory() {
//
// Create the InitPartitionFactory for acyclic elements that precede the cycle.
//
// FIXME an ordering of the acyclic unpartitioned regions could allow many predicate steps to
// be merged from the outset.
//
CyclicRegionAnalysis cyclicRegionAnalysis = basicGetCyclicRegionAnalysis();
//
// Gather the acyclic edges and remember if there is a cyclic edge.
//
boolean hasCyclicEdge = false;
List<@NonNull Edge> reachingInitEdges = new ArrayList<>();
for (@NonNull Edge edge : originalEdges) {
if (((basicGetPartitionFactory(edge) != null) || edge.isOld())) { // && !edge.isSecondary()) {
boolean isCyclic = false;
if (edge.isSuccess() && cyclicRegionAnalysis != null) {
PropertyDatum propertyDatum = scheduleManager.getPropertyDatum((SuccessEdge) edge);
if (cyclicRegionAnalysis.isCyclic(propertyDatum)) {
hasCyclicEdge = true;
isCyclic = true;
}
}
if (!isCyclic) {
reachingInitEdges.add(edge);
}
}
}
/* Set<@NonNull Node> oldNodes1 = new HashSet<>();
for (@NonNull Node node : allNodes) {
if (node.isOld()) {
// ClassDatum classDatum = QVTscheduleUtil.getClassDatum(node);
// if (!classDatum.isCollectionType()) { // Collection DataTypes are non 1:1 edge ends
oldNodes1.add(node);
// }
}
} */
// assert hasCyclicEdge1 == hasCyclicEdge2;
// Collections.sort(navigableInitEdges1, NameUtil.NAMEABLE_COMPARATOR);
// Collections.sort(navigableInitEdges2, NameUtil.NAMEABLE_COMPARATOR);
// assert navigableInitEdges1.equals(navigableInitEdges2);
// strategy.regionAnalysis.createLocalSuccess();
ReachabilityForest initReachabilityForest1 = new ReachabilityForest("init", thisAndTraceAndConstantSourceNodes, reachingInitEdges);
List<@NonNull Node> novelInitNodes1 = null;
for (@NonNull Node node : initReachabilityForest1.getMostReachableFirstNodes()) {
if ((basicGetPartitionFactory(node) == null) && !Iterables.contains(thisAndTraceNodes, node)) {
if (novelInitNodes1 == null) {
novelInitNodes1 = new ArrayList<>();
}
novelInitNodes1.add(node);
}
}
/* List<@NonNull Node> novelInitNodes2 = null;
for (@NonNull Node node : allNodes) { //initReachabilityForest.getMostReachableFirstNodes()) {
AbstractReachabilityPartitionFactory partitionFactory = basicGetPartitionFactory(node);
if (partitionFactory == null) {
if (novelInitNodes2 == null) {
novelInitNodes2 = new ArrayList<>();
}
novelInitNodes2.add(node);
}
} */
if (novelInitNodes1 != null) {
// assert novelInitNodes1.equals(novelInitNodes2); -- not even close
if (!hasCyclicEdge) { // Acyclic - can do realizes now
for (@NonNull Node node : originalNodes) {
if (node.isRealized() && !novelInitNodes1.contains(node) && (basicGetPartitionFactory(node) == null)) {
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(node);
if (!classDatum.isCollectionType()) { // Collection DataTypes are non 1:1 edge ends
novelInitNodes1.add(node);
}
}
}
}
if (!novelInitNodes1.contains(globalSuccessNode)) {
novelInitNodes1.add(getLocalSuccessNode());
}
new InitPartitionFactory(this, initReachabilityForest1, thisAndTraceNodes, novelInitNodes1);
}
}
private void createLoopPartitionFactory() {
//
// Determine the reaching edges for the LoopPartitionFactory.
//
List<@NonNull Edge> reachingLoopEdges = new ArrayList<>();
for (@NonNull Edge edge : originalEdges) {
boolean isReaching = false;
if (edge.isNavigable()) {
NavigationEdge navigationEdge = (NavigationEdge)edge;
if (basicGetPartitionFactory(edge) != null) {
isReaching = true; // Gather all already predicated/realized edges.
}
else if (edge.isOld() && !edge.isSecondary() && !transformationAnalysis.isCorollary(navigationEdge)) {
isReaching = true; // Gather all predicated edges that cannot be deferred to the rest partition as corrolaries.
}
}
else {
isReaching = true;
}
if (isReaching) {
reachingLoopEdges.add(edge);
}
}
if (localSuccessEdge != null) {
reachingLoopEdges.add(localSuccessEdge);
}
//
// Determine the novel nodes for the LoopPartitionFactory.
//
ReachabilityForest loopReachabilityForest = new ReachabilityForest("loop", thisAndTraceAndConstantSourceNodes, reachingLoopEdges);
List<@NonNull Node> novelLoopNodes = null;
for (@NonNull Node node : loopReachabilityForest.getMostReachableFirstNodes()) {
if (basicGetPartitionFactory(node) == null) {
if (novelLoopNodes == null) {
novelLoopNodes = new ArrayList<>();
}
novelLoopNodes.add(node);
}
}
if (novelLoopNodes != null) {
if (localSuccessNode != null) {
novelLoopNodes.add(localSuccessNode);
}
for (@NonNull Node node : originalNodes) {
if (node.isRealized() && !novelLoopNodes.contains(node) && (basicGetPartitionFactory(node) == null)) {
ClassDatum classDatum = QVTscheduleUtil.getClassDatum(node);
if (!classDatum.isCollectionType()) { // Collection DataTypes are non 1:1 edge ends
novelLoopNodes.add(node);
}
}
}
// if (!novelLoopNodes.equals(novelLoopNodes2)) {
// getClass();
// }
List<@NonNull Edge> novelLoopEdges = new ArrayList<>();
for (@NonNull Edge edge : originalEdges) {
if (edge.isRealized() && (basicGetPartitionFactory(edge) == null)) {
Node sourceNode = QVTscheduleUtil.getSourceNode(edge);
Node targetNode = QVTscheduleUtil.getTargetNode(edge);
if (((loopReachabilityForest.basicGetCost(sourceNode) != null) || Iterables.contains(novelLoopNodes, sourceNode))
&& ((loopReachabilityForest.basicGetCost(targetNode) != null) || Iterables.contains(novelLoopNodes, targetNode))) {
novelLoopEdges.add(edge);
}
}
}
new LoopPartitionFactory(this, loopReachabilityForest, thisAndTraceNodes, novelLoopNodes, novelLoopEdges);
}
}
protected void createNonPartition() {
newPartitionAnalyses.add(new NonPartitionFactory(mappingPartitioner).createPartitionAnalysis(partitionedTransformationAnalysis));
}
private void createRestPartitionFactory() {
if ("FtoN1_Family2SurnameContainment".equals(region.getName())) {
getClass();
}
//
// Create RestPartitionFactory to progress the remaining realized edges.
//
List<@NonNull Edge> novelRestEdges = null;
for (@NonNull Edge edge : originalEdges) {
if (edge.isNavigable()) {
NavigationEdge navigationEdge = (NavigationEdge) edge;
if (edge.isRealized() && (basicGetPartitionFactory(edge) == null)) {
if (novelRestEdges == null) {
novelRestEdges = new ArrayList<>();
}
novelRestEdges.add(navigationEdge);
}
}
}
if (novelRestEdges != null) {
List<@NonNull NavigableEdge> reachingRestEdges = new ArrayList<>();
for (@NonNull Edge edge : originalEdges) {
if (edge.isNavigable() && !novelRestEdges.contains(edge)) {
reachingRestEdges.add((NavigableEdge)edge);
}
}
Iterable<@NonNull Node> novelRestNodes;
SuccessEdge localSuccessEdge2 = localSuccessEdge;
if ((localSuccessEdge2 != null) && (basicGetPartitionFactory(globalSuccessNode) == null)) {
reachingRestEdges.add(localSuccessEdge2);
novelRestNodes = Lists.newArrayList(QVTscheduleUtil.getTargetNode(localSuccessEdge2), globalSuccessNode);
}
else {
novelRestNodes = Collections.singletonList(globalSuccessNode);
}
ReachabilityForest restReachabilityForest = new ReachabilityForest("rest", thisAndTraceAndConstantSourceNodes, reachingRestEdges);
new RestPartitionFactory(this, restReachabilityForest, thisAndTraceNodes, novelRestNodes, novelRestEdges);
}
}
private @NonNull SuccessNode getLocalSuccessNode() {
SuccessNode localSuccessNode2 = localSuccessNode;
if (localSuccessNode2 == null) {
this.localSuccessEdge = regionAnalysis.createLocalSuccess();
this.localSuccessNode = localSuccessNode2 = (SuccessNode) QVTscheduleUtil.getTargetNode(localSuccessEdge);
}
return localSuccessNode2;
}
private @NonNull AbstractReachabilityPartitionFactory getPartitionFactory(@NonNull Edge edge) {
AbstractReachabilityPartitionFactory partitionFactory = edge2partitionFactory.get(edge);
return ClassUtil.nonNullState(partitionFactory);
}
private @NonNull AbstractReachabilityPartitionFactory getPartitionFactory(@NonNull Node node) {
AbstractReachabilityPartitionFactory partitionFactory = node2partitionFactory.get(node);
return ClassUtil.nonNullState(partitionFactory);
}
public @NonNull Region getRegion() {
return region;
}
public @NonNull AbstractTransformationAnalysis getTransformationAnalysis() {
return transformationAnalysis;
}
@Override
public @NonNull Iterable<@NonNull PartitionAnalysis> partition() {
String name = regionAnalysis.getName();
if ("mapNavigationOrAttributeCallExp_Helper_qvtr".equals(name)) {
getClass();
}
//
// Create the PartitionFactories.
//
createCtorPartitionFactory();
createInitPartitionFactory();
createLoopPartitionFactory();
createRestPartitionFactory();
//
// Create the partitions.
//
for (@NonNull AbstractReachabilityPartitionFactory partitionFactory : partitionFactories) {
newPartitionAnalyses.add(partitionFactory.createPartitionAnalysis(partitionedTransformationAnalysis));
}
check();
return newPartitionAnalyses;
}
private void setCurrentPartitionFactory(@NonNull AbstractReachabilityPartitionFactory currentPartitionFactory) {
if (this.currentPartitionFactory != null) {
currentPartitionFactory.addPredecessor(this.currentPartitionFactory);
}
this.currentPartitionFactory = currentPartitionFactory;
partitionFactories.add(currentPartitionFactory);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append(mappingPartitioner.getName());
for (@NonNull AbstractReachabilityPartitionFactory partitionFactory : partitionFactories) {
s.append("\n\t");
partitionFactory.toString(s, "\n\t");
}
return s.toString();
}
@Override
protected boolean useActivators() {
return true;
}
}