| /******************************************************************************* |
| * 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 java.util.Set; |
| |
| 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; |
| } |
| |
| private void addReachableEdgeAndOpposite(@NonNull Edge reachingEdge) { |
| addReachableEdge(reachingEdge); |
| if (reachingEdge.isNavigable()) { |
| Edge oppositeEdge = ((NavigationEdge)reachingEdge).getOppositeEdge(); |
| if (oppositeEdge != null) { |
| addReachableEdge(oppositeEdge); |
| } |
| } |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Create the partition and populate it with the edges and nodes identified by the reaachableEdges and reachableNodes. |
| */ |
| @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 void initPartitionFactoryAsJustReachables() { |
| // |
| // Grow the edges to include all necessary reaching edges. |
| // |
| for (int i = 0; i < reachableNodes.size(); i++) { |
| Node node = reachableNodes.get(i); |
| for (@NonNull Edge reachingEdge : reachabilityForest.getReachingEdges(node)) { |
| addReachableEdgeAndOpposite(reachingEdge); |
| } |
| } |
| for (int i = 0; i < reachableEdges.size(); i++) { |
| Edge edge = reachableEdges.get(i); |
| for (@NonNull Edge reachingEdge : reachabilityForest.getReachingEdges(QVTscheduleUtil.getSourceNode(edge))) { |
| addReachableEdgeAndOpposite(reachingEdge); |
| } |
| for (@NonNull Edge reachingEdge : reachabilityForest.getReachingEdges(QVTscheduleUtil.getTargetNode(edge))) { |
| addReachableEdgeAndOpposite(reachingEdge); |
| } |
| } |
| } |
| |
| 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 void initPartitionFactory() { |
| initPartitionFactoryAsJustReachables(); |
| } |
| |
| @Override |
| protected @NonNull Role resolveNodeRole(@NonNull Node node) { |
| if (node instanceof SuccessNode) { |
| return Role.CONSTANT_SUCCESS_TRUE; |
| } |
| return super.resolveNodeRole(node); |
| } |
| } |
| |
| protected static class XtraPartitionFactory extends AbstractReachabilityPartitionFactory |
| { |
| protected XtraPartitionFactory(@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 void initPartitionFactory() { |
| initPartitionFactoryAsJustReachables(); |
| } |
| |
| @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; |
| |
| /** |
| * The realized success of the dispatch node, if any. |
| */ |
| private @Nullable Edge dispatchSuccessEdge = 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; |
| Node dispatchNode = 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.isDispatch()) { |
| assert dispatchNode == null : "Only one dispatch node permitted in " + region.getName(); |
| dispatchNode = 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); |
| if (dispatchNode != null) { |
| for (@NonNull Edge edge : dispatchNode.getOutgoingEdges()) { |
| if (edge.isSuccess() && edge.isRealized()) { |
| this.dispatchSuccessEdge = edge; |
| } |
| } |
| } |
| } |
| |
| 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 @Nullable Set<@NonNull Node> 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. |
| // |
| List<@NonNull Edge> reachingInitEdges = new ArrayList<>(); |
| UniqueList<@NonNull Edge> cyclicInitEdges = null; |
| List<@NonNull Edge> coCyclicInitEdges = null; |
| UniqueList<@NonNull Node> coCyclicInitNodes = null; |
| for (@NonNull Edge edge : originalEdges) { |
| if (basicGetPartitionFactory(edge) != null) { |
| reachingInitEdges.add(edge); |
| } |
| else if (edge.isOld()) { // && !edge.isSecondary()) { |
| boolean isCyclic = false; |
| if (edge.isNavigable() && cyclicRegionAnalysis != null) { |
| PropertyDatum propertyDatum = scheduleManager.getPropertyDatum((NavigationEdge) edge); |
| if (cyclicRegionAnalysis.isCyclic(propertyDatum)) { |
| isCyclic = true; |
| if (cyclicInitEdges == null) { |
| cyclicInitEdges = new UniqueList<>(); |
| } |
| cyclicInitEdges.add(edge); |
| if (edge.isSuccess()) { // FIXME and TRUE |
| Node invokedNode = QVTscheduleUtil.getSourceNode(edge); |
| for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(invokedNode)) { |
| if ((outgoingEdge != edge) && outgoingEdge.isPredicated() && outgoingEdge.isNavigable()) { |
| List<@NonNull Region> corollaryOf = transformationAnalysis.getCorollaryOf((NavigationEdge) outgoingEdge); |
| if (corollaryOf != null) { |
| if (coCyclicInitEdges == null) { |
| coCyclicInitEdges = new ArrayList<>(); |
| } |
| assert !coCyclicInitEdges.contains(outgoingEdge); |
| coCyclicInitEdges.add(outgoingEdge); |
| // NavigationEdge oppositeEdge = ((NavigationEdge)outgoingEdge).getOppositeEdge(); |
| // assert (oppositeEdge != null) && !coCyclicInitEdges.contains(oppositeEdge); |
| // coCyclicInitEdges.add(oppositeEdge); |
| Node corollaryNode = QVTscheduleUtil.getTargetNode(outgoingEdge); |
| if (coCyclicInitNodes == null) { |
| coCyclicInitNodes = new UniqueList<>(); |
| } |
| assert !coCyclicInitNodes.contains(corollaryNode); |
| coCyclicInitNodes.add(corollaryNode); |
| } |
| } |
| } |
| } |
| } |
| } |
| if (!isCyclic) { |
| reachingInitEdges.add(edge); |
| } |
| } |
| } |
| if (coCyclicInitEdges != null) { |
| reachingInitEdges.removeAll(coCyclicInitEdges); |
| } |
| UniqueList<@NonNull Node> cyclicInitNodes = null; |
| UniqueList<@NonNull Node> reachingInitNodes = null; |
| if (cyclicInitEdges != null) { |
| reachingInitNodes = new UniqueList<>(thisAndTraceAndConstantSourceNodes); |
| cyclicInitNodes = new UniqueList<>(); |
| int iMax = cyclicInitEdges.size(); |
| for (int i = 0; i < iMax; i++) { // Domain grows but extra entries are processed recursively within loop. |
| Edge cyclicEdge = cyclicInitEdges.get(i); |
| pruneInitCyclicEdge(cyclicInitNodes, cyclicInitEdges, cyclicEdge); |
| } |
| reachingInitEdges.removeAll(cyclicInitEdges); |
| reachingInitNodes.removeAll(cyclicInitNodes); |
| } |
| /* 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", reachingInitNodes != null ? reachingInitNodes : thisAndTraceAndConstantSourceNodes, reachingInitEdges); |
| List<@NonNull Node> novelInitNodes1 = null; |
| for (@NonNull Node node : initReachabilityForest1.getMostReachableFirstNodes()) { |
| if ((basicGetPartitionFactory(node) == null) && !Iterables.contains(thisAndTraceNodes, node) && ((coCyclicInitNodes == null) || !coCyclicInitNodes.contains(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 (cyclicInitEdges == null) { // 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); |
| } |
| return coCyclicInitNodes; |
| } |
| |
| /** |
| * Grow the cyclicNodes/cyclicEdges area to include all nodes/edges that should be omitted from the init partition as a consequence |
| * of omitting the cyclicEdge. |
| */ |
| private void pruneInitCyclicEdge(@NonNull Set<@NonNull Node> cyclicNodes, @NonNull Set<@NonNull Edge> cyclicEdges, @NonNull Edge cyclicEdge) { |
| Node targetNode = QVTscheduleUtil.getTargetNode(cyclicEdge); |
| pruneInitCyclicTargetNode(cyclicNodes, cyclicEdges, targetNode); |
| /* if (cyclicNodes.add(targetNode)) { |
| if (cyclicEdge.isComputation()) { |
| cyclicEdges.add(cyclicEdge); |
| Node sourceNode = QVTscheduleUtil.getSourceNode(cyclicEdge); |
| if (cyclicEdges.containsAll(sourceNode.getOutgoingEdges())) { |
| // if (cyclicNodes.add(sourceNode)) { |
| for (@NonNull Edge incomingEdge : QVTscheduleUtil.getIncomingEdges(targetNode)) { |
| pruneInitCyclicEdge(cyclicNodes, cyclicEdges, incomingEdge); |
| } |
| // } |
| } |
| } |
| else if (cyclicEdges.containsAll(targetNode.getIncomingEdges())) { |
| for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(targetNode)) { |
| pruneInitCyclicEdge(cyclicNodes, cyclicEdges, outgoingEdge); |
| } |
| } |
| } */ |
| } |
| /* private void pruneInitCyclicSourceNode(@NonNull Set<@NonNull Node> cyclicNodes, @NonNull Set<@NonNull Edge> cyclicEdges, @NonNull Node cyclicNode) { |
| if (cyclicNodes.add(cyclicNode)) { |
| if (cyclicEdges.containsAll(cyclicNode.getIncomingEdges())) { // Follow irrevocably cyclic nodes towards their targets |
| for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(cyclicNode)) { |
| cyclicEdges.add(outgoingEdge); |
| pruneInitCyclicNode(cyclicNodes, cyclicEdges, QVTscheduleUtil.getTargetNode(outgoingEdge)); |
| } |
| } |
| if (cyclicNode.isExpression()) { // Follow orphan expressions towards their sources |
| boolean hasNavigation = false; |
| for (@NonNull Edge incomingEdge : QVTscheduleUtil.getIncomingEdges(cyclicNode)) { |
| if (incomingEdge.isNavigation()) { |
| hasNavigation = true; |
| break; |
| } |
| } |
| if (!hasNavigation) { |
| for (@NonNull Edge incomingEdge : QVTscheduleUtil.getIncomingEdges(cyclicNode)) { |
| cyclicEdges.add(incomingEdge); |
| pruneInitCyclicNode(cyclicNodes, cyclicEdges, QVTscheduleUtil.getSourceNode(incomingEdge)); |
| } |
| } |
| } |
| } |
| } */ |
| private void pruneInitCyclicTargetNode(@NonNull Set<@NonNull Node> cyclicNodes, @NonNull Set<@NonNull Edge> cyclicEdges, @NonNull Node cyclicNode) { |
| assert !cyclicNodes.contains(cyclicNode); |
| if (cyclicEdges.containsAll(cyclicNode.getIncomingEdges())) { // Follow irrevocably cyclic nodes towards their targets |
| cyclicNodes.add(cyclicNode); |
| for (@NonNull Edge outgoingEdge : QVTscheduleUtil.getOutgoingEdges(cyclicNode)) { |
| cyclicEdges.add(outgoingEdge); |
| Node targetNode = QVTscheduleUtil.getTargetNode(outgoingEdge); |
| if (!cyclicNodes.contains(targetNode)) { |
| pruneInitCyclicTargetNode(cyclicNodes, cyclicEdges, targetNode); |
| } |
| } |
| } |
| if (cyclicNode.isExpression()) { // Follow orphan expressions towards their sources |
| boolean hasNavigation = false; |
| Iterable<@NonNull Edge> incomingEdges = QVTscheduleUtil.getIncomingEdges(cyclicNode); |
| for (@NonNull Edge incomingEdge : incomingEdges) { |
| if (incomingEdge.isNavigation()) { |
| hasNavigation = true; |
| break; |
| } |
| } |
| if (!hasNavigation) { |
| cyclicNodes.add(cyclicNode); |
| for (@NonNull Edge incomingEdge : incomingEdges) { |
| cyclicEdges.add(incomingEdge); |
| Node sourceNode = QVTscheduleUtil.getSourceNode(incomingEdge); |
| if (!cyclicNodes.contains(sourceNode)) { |
| pruneInitCyclicTargetNode(cyclicNodes, cyclicEdges, sourceNode); |
| } |
| } |
| } |
| } |
| } |
| |
| private void createLoopPartitionFactory() { |
| // |
| // Determine the reaching edges for the LoopPartitionFactory. |
| // |
| List<@NonNull Edge> reachingLoopEdges = new ArrayList<>(); |
| for (@NonNull Edge edge : originalEdges) { |
| if (basicGetPartitionFactory(edge) != null) { |
| reachingLoopEdges.add(edge); |
| } |
| else if (edge.isOld() && (!edge.isNavigable() || !transformationAnalysis.isCorollary((NavigationEdge) edge))) { |
| reachingLoopEdges.add(edge); // Gather all predicated edges that cannot be deferred to the rest partition as corrolaries. |
| } |
| } |
| if (localSuccessEdge != null) { |
| reachingLoopEdges.add(localSuccessEdge); |
| if (dispatchSuccessEdge != null) { |
| reachingLoopEdges.add(dispatchSuccessEdge); |
| } |
| } |
| // |
| // 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 @Nullable Iterable<@NonNull Node> createRestPartitionFactory(@Nullable Set<@NonNull Node> coCyclicInitNodes) { |
| if ("mapIfExp_qvtr".equals(region.getName())) { |
| getClass(); |
| } |
| // |
| // Create RestPartitionFactory to progress the remaining realized edges. |
| // |
| List<@NonNull Edge> novelRestEdges = null; |
| List<@NonNull Node> extraRestNodes = null; |
| for (@NonNull Edge edge : originalEdges) { |
| if (edge.isNavigable()) { |
| NavigationEdge navigationEdge = (NavigationEdge) edge; |
| if (edge.isRealized() && (basicGetPartitionFactory(edge) == null)) { |
| boolean assignIt = false; |
| Node realizedTarget = QVTscheduleUtil.getTargetNode(navigationEdge); |
| if (!realizedTarget.isPredicated() || (basicGetPartitionFactory(realizedTarget) != null)) { |
| assignIt = true; |
| } |
| else if ((coCyclicInitNodes != null) && coCyclicInitNodes.contains(realizedTarget)) { |
| assignIt = true; |
| } |
| else { |
| if (extraRestNodes == null) { |
| extraRestNodes = new ArrayList<>(); |
| } |
| extraRestNodes.add(realizedTarget); |
| } |
| if (assignIt) { |
| 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); |
| } |
| return extraRestNodes; |
| } |
| |
| private void createXtraPartitionFactory(@NonNull Iterable<@NonNull Node> xtraRestNodes) { |
| if ("mapVariableExp_referredVariable_Helper_qvtr".equals(region.getName())) { |
| getClass(); |
| } |
| // |
| // Create XtraPartitionFactory to progress the remaining realized edges. |
| // |
| List<@NonNull Edge> novelXtraEdges = new ArrayList<>(); |
| List<@NonNull Edge> reachingXtraEdges = new ArrayList<>(); |
| for (@NonNull Edge edge : originalEdges) { |
| if (basicGetPartitionFactory(edge) == null) { |
| novelXtraEdges.add(edge); |
| if (edge.isOld()) { |
| reachingXtraEdges.add(edge); |
| } |
| } |
| else { |
| reachingXtraEdges.add(edge); |
| } |
| } |
| List<@NonNull Node> novelXtraNodes = new ArrayList<>(); |
| novelXtraNodes.add(globalSuccessNode); |
| for (@NonNull Node node : originalNodes) { |
| if (basicGetPartitionFactory(node) == null) { |
| novelXtraNodes.add(node); |
| } |
| } |
| ReachabilityForest xtraReachabilityForest = new ReachabilityForest("xtra", thisAndTraceAndConstantSourceNodes, reachingXtraEdges); |
| new XtraPartitionFactory(this, xtraReachabilityForest, thisAndTraceNodes, novelXtraNodes, novelXtraEdges); |
| } |
| |
| 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 ("mapIfExp_qvtr".equals(name)) { |
| getClass(); |
| } |
| // |
| // Create the PartitionFactories. |
| // |
| createCtorPartitionFactory(); |
| @Nullable Set<@NonNull Node> coCyclicInitNodes = createInitPartitionFactory(); |
| createLoopPartitionFactory(); |
| @Nullable Iterable<@NonNull Node> extraRestNodes = createRestPartitionFactory(coCyclicInitNodes); |
| if (extraRestNodes != null) { |
| createXtraPartitionFactory(extraRestNodes); |
| } |
| // |
| // 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; |
| } |
| } |