Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEd Willink2016-07-12 13:14:40 -0400
committerEd Willink2016-07-14 15:13:04 -0400
commit78431844c0a44cb120ef103774d78d39084f63ce (patch)
treeb68b493258fa90847907f571a1b236d50df201e3
parent645b7c599ecc6b3fe6a9dd5341f857b455ce0414 (diff)
downloadorg.eclipse.qvtd-ewillink/496420.tar.gz
org.eclipse.qvtd-ewillink/496420.tar.xz
org.eclipse.qvtd-ewillink/496420.zip
[486722] Introduce Stages to organize solutionewillink/496420
-rw-r--r--plugins/org.eclipse.qvtd.compiler/.options2
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractGroup.java8
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractStage.java153
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/BodyStage.java233
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/CompoundGroup.java13
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadStage.java37
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadedStage.java140
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/LoopStage.java69
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SimpleGroup.java6
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Split.java92
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Splitter.java11
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterAnalysis.java4
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterUtil.java95
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Stage.java28
-rw-r--r--tests/org.eclipse.qvtd.xtext.qvtcore.tests/src/org/eclipse/qvtd/xtext/qvtcore/tests/QVTcCompilerTests.java4
-rw-r--r--tests/org.eclipse.qvtd.xtext.qvtrelation.tests/src/org/eclipse/qvtd/xtext/qvtrelation/tests/QVTrCompilerTests.java2
16 files changed, 832 insertions, 65 deletions
diff --git a/plugins/org.eclipse.qvtd.compiler/.options b/plugins/org.eclipse.qvtd.compiler/.options
index 0ab4ff602..c236032c6 100644
--- a/plugins/org.eclipse.qvtd.compiler/.options
+++ b/plugins/org.eclipse.qvtd.compiler/.options
@@ -53,6 +53,8 @@ org.eclipse.qvtd.compiler/qvtp2qvts/regionTraversal=false
org.eclipse.qvtd.compiler/qvtp2qvts/split/groups=false
# Turn on tracing of the result of splitting multi-headed regions
org.eclipse.qvtd.compiler/qvtp2qvts/split/result=false
+# Turn on tracing of the stages of splitting multi-headed regions
+org.eclipse.qvtd.compiler/qvtp2qvts/split/stages=false
# Turn on tracing of the polled properties computation
org.eclipse.qvtd.compiler/qvts2qvti/polledProperties=false
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractGroup.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractGroup.java
index 5e5f53040..55466211c 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractGroup.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractGroup.java
@@ -70,7 +70,7 @@ abstract class AbstractGroup implements Group
protected AbstractGroup(@NonNull SplitterAnalysis splitter, @NonNull List<@NonNull Node> headNodes) {
this.splitter = splitter;
this.name = SplitterUtil.computeMultiHeadNodeName(headNodes);
- this.reachableNodes = SplitterUtil.computeReachableNodes(headNodes);
+ this.reachableNodes = SplitterUtil.computeNavigableNodes(headNodes);
}
public void addPredecessor(@NonNull Edge edge, @NonNull List<@NonNull AbstractGroup> predecessorGroups) {
@@ -103,9 +103,9 @@ abstract class AbstractGroup implements Group
successorGroups.put(successorGroup, edge);
}
- public void buildSplit(@NonNull Split split) {
+ public void buildSplit(@NonNull Split split, @Nullable SimpleGroup sourceSimpleGroup) {
for (Map.Entry<@NonNull AbstractGroup, @NonNull Edge> entry : successorGroups.entrySet()) {
- entry.getKey().buildSplit(split, entry.getValue());
+ entry.getKey().buildSplit(split, sourceSimpleGroup, entry.getValue());
}
}
@@ -127,7 +127,7 @@ abstract class AbstractGroup implements Group
}
}
- protected abstract void buildSplit(@NonNull Split subregion, @Nullable Edge edge);
+ protected abstract void buildSplit(@NonNull Split subregion, @Nullable SimpleGroup sourceSimpleGroup, @Nullable Edge edge);
public abstract @NonNull Iterable<@NonNull SimpleGroup> getInternalSimpleGroups();
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractStage.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractStage.java
new file mode 100644
index 000000000..af4cdec98
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/AbstractStage.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.utilities.NameUtil;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+/**
+ * A Stage specifies part of a multi-headed region split. It has a headNode to represent its primary content
+ * and an edge that defines the entry to the stage, null fir the first stage.
+ */
+abstract class AbstractStage implements Stage
+{
+ /**
+ * The Splitter supervising the region splitting.
+ */
+ protected final @NonNull SplitterAnalysis splitter;
+
+ protected AbstractStage(@NonNull SplitterAnalysis splitter) {
+ this.splitter = splitter;
+ }
+
+ protected abstract @NonNull String buildContents(@NonNull StringBuilder s);
+
+ protected void build(@NonNull StringBuilder s, @NonNull String title, @NonNull Iterable<@NonNull Node> nodes) {
+ if (!Iterables.isEmpty(nodes)) {
+ s.append("\n");
+ SplitterUtil.indent(s, 2);
+ s.append(title);
+ for (@NonNull Node node : nodes) {
+ s.append("\n");
+ SplitterUtil.indent(s, 3);
+ if (node.isHead()) {
+ s.append("*");
+ }
+ s.append(node.getName());
+ s.append(" : ");
+ s.append(node);
+ }
+ }
+ }
+
+ /**
+ * Verify that addition of nodes to accumulator contributes exclusively new content.
+ */
+ protected void checkAccumulate(@NonNull Set<@NonNull Node> accumulator, @NonNull Iterable<@NonNull Node> nodes) {
+ for (@NonNull Node node : nodes) {
+ boolean wasAdded = accumulator.add(node);
+ assert wasAdded;
+ }
+ }
+
+ /**
+ * Verify that the eaccumulator contributes exclusively new content.
+ */
+ protected void checkAccumulated(@NonNull Set<@NonNull Node> actualNodes, @NonNull Iterable<@NonNull Node> expectedNodes) {
+ Set<@NonNull Node> expectedNodesSet = Sets.newHashSet(expectedNodes);
+ if (!actualNodes.equals(expectedNodesSet)) {
+ StringBuilder s = new StringBuilder();
+ Set<@NonNull Node> extraNodesSet = Sets.newHashSet(actualNodes);
+ SplitterUtil.removeAll(extraNodesSet, expectedNodes);
+ for (@NonNull Node node : extraNodesSet) {
+ s.append("\nextra: ");
+ s.append(node);
+ }
+ Set<@NonNull Node> missingNodesSet = Sets.newHashSet(expectedNodes);
+ missingNodesSet.removeAll(actualNodes);
+ for (@NonNull Node node : missingNodesSet) {
+ s.append("\nmissing: ");
+ s.append(node);
+ }
+ assert false : "Bad nodes for " + this + s.toString();
+ }
+ }
+
+ /**
+ * The dead nodes are non-realized nodes that have outgoing edges only to dead nodes or head nodes.
+ */
+ protected @NonNull Iterable<@NonNull Node> computeDeadNodes(@NonNull Iterable<@NonNull Node> reachableNodes) {
+ Set<@NonNull Node> deadNodes = new HashSet<>();
+ Set<@NonNull Node> tryNodes = Sets.newHashSet(reachableNodes);
+ while (!tryNodes.isEmpty()) {
+ Set<@NonNull Node> retryNodes = new HashSet<>();
+ for (@NonNull Node node : tryNodes) {
+ if (!isLive(node, deadNodes)) {
+ deadNodes.add(node);
+ for (@NonNull Edge edge : node.getIncomingEdges()) {
+ if (!edge.isRealized()) {
+ Node sourceNode = edge.getSource();
+ if (!sourceNode.isRealized()) {
+ retryNodes.add(sourceNode);
+ }
+ }
+ }
+ }
+ }
+ retryNodes.removeAll(deadNodes);
+ tryNodes = retryNodes;
+ }
+ List<@NonNull Node> nodeList = new ArrayList<>(deadNodes);
+ Collections.sort(nodeList, NameUtil.NAMEABLE_COMPARATOR);
+ return nodeList;
+ }
+
+ @Override
+ public void debug() {
+ if (Splitter.STAGES.isActive()) {
+ StringBuilder s = new StringBuilder();
+ toString(s, 1);
+ String name = buildContents(s);
+ Splitter.STAGES.println(splitter.toString() + ":" + name + "\n" + s.toString());
+ }
+ }
+
+ @Override
+ public @Nullable Node getIteratedNode() {
+ return null;
+ }
+
+ @Override
+ public @Nullable Node getIteratorNode() {
+ return null;
+ }
+
+ protected abstract boolean isLive(@NonNull Node node, @NonNull Set<@NonNull Node> deadNodes);
+
+ @Override
+ public @NonNull String toString() {
+ StringBuilder s = new StringBuilder();
+ toString(s, 0);
+ return s.toString();
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/BodyStage.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/BodyStage.java
new file mode 100644
index 000000000..7bf62259f
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/BodyStage.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.utilities.NameUtil;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Region;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * A BodyStage specifies the final part of a multi-headed region split.
+ * It has headNodes to represent its inputs.
+ */
+class BodyStage extends AbstractStage
+{
+ private static void computeOperationSources(Set<@NonNull Node> nodes, @NonNull Node node) {
+ if (nodes.add(node)) {
+ for (@NonNull Edge edge : node.getIncomingEdges()) {
+ if (edge.isArgument()) {
+ computeOperationSources(nodes, edge.getSource());
+ }
+ }
+ }
+ }
+
+ // private final @NonNull List<@NonNull Stage> stages;
+ // private final @NonNull Iterable<@NonNull Node> visibleIteratedNodes;
+ private final @NonNull Iterable<@NonNull Node> visibleIteratorNodes;
+ private final @NonNull Iterable<@NonNull Node> realizedNodes;
+ private final @NonNull Iterable<@NonNull Node> allHeadNodes;
+ private final @NonNull Iterable<@NonNull Node> directlyRequiredNodes;
+ private final @NonNull Iterable<@NonNull Node> deadNodes;
+ private final @NonNull Iterable<@NonNull Node> indirectlyRequiredNodes;
+
+ public BodyStage(@NonNull SplitterAnalysis splitter, @NonNull Iterable<@NonNull Stage> stages) {
+ super(splitter);
+ // this.stages = Lists.newArrayList(stages);
+ // this.visibleIteratedNodes = computedVisibleIteratedNodes(stages);
+ this.visibleIteratorNodes = computedVisibleIteratorNodes(stages);
+ this.realizedNodes = computeRealizedNodes();
+ //
+ // Determine the nodes needed to realize the realized nodes.
+ //
+ Iterable<@NonNull Node> requiredNodes = computeRequiredNodes();
+ this.allHeadNodes = computeAllHeadNodes(requiredNodes);
+ this.directlyRequiredNodes = computeDirectlyRequiredNodes(requiredNodes);
+ Iterable<@NonNull Node> allNodes = computeAllNodes();
+ this.deadNodes = computeDeadNodes(allNodes);
+ this.indirectlyRequiredNodes = computeIndirectlyRequiredNodes(allNodes);
+ }
+
+ @Override
+ protected @NonNull String buildContents(@NonNull StringBuilder s) {
+ // build(s, "visible iterated nodes", visibleIteratedNodes);
+ // build(s, "visible iterator nodes", visibleIteratorNodes);
+ build(s, "all head nodes", allHeadNodes);
+ build(s, "realized nodes", realizedNodes);
+ build(s, "directly required nodes", directlyRequiredNodes);
+ build(s, "indirectly required nodes", indirectlyRequiredNodes);
+ build(s, "dead nodes", deadNodes);
+ return "body";
+ }
+
+ @Override
+ public void check() {
+ Set<@NonNull Node> accumulator = new HashSet<>();
+ checkAccumulate(accumulator, allHeadNodes);
+ checkAccumulate(accumulator, realizedNodes);
+ checkAccumulate(accumulator, directlyRequiredNodes);
+ checkAccumulate(accumulator, indirectlyRequiredNodes);
+ checkAccumulate(accumulator, deadNodes);
+ checkAccumulated(accumulator, splitter.getRegion().getNodes());
+ }
+
+ /**
+ * The original head nodes and the intermediate heads for computed iterators are directly detectable
+ * as traced inputs of the realized trace object. Intermediate heads for inverse navigated iterators
+ * are detectable from the visible iterators.
+ */
+ protected @NonNull Iterable<@NonNull Node> computeAllHeadNodes(@NonNull Iterable<@NonNull Node> requiredNodes) {
+ Set<@NonNull Node> nodeSet = Sets.newHashSet(visibleIteratorNodes);
+ for (@NonNull Node node : requiredNodes) {
+ if (node.isHead()) {
+ nodeSet.add(node);
+ }
+ }
+ List<@NonNull Node> nodes = Lists.newArrayList(nodeSet);
+ Collections.sort(nodes, NameUtil.NAMEABLE_COMPARATOR);
+ return nodes;
+ }
+
+ protected @NonNull Iterable<@NonNull Node> computeAllNodes() {
+ Set<@NonNull Node> navigableNodes = SplitterUtil.computeNavigableNodes(allHeadNodes);
+ Set<@NonNull Node> computableNodes = SplitterUtil.computeComputableTargetNodes(navigableNodes);
+ Set<@NonNull Node> allNodes = Sets.newHashSet(navigableNodes);
+ allNodes.addAll(computableNodes);
+ return allNodes;
+ }
+
+ /**
+ * The directly required nodes are the required nodes that are not head nodes.
+ */
+ protected @NonNull Iterable<@NonNull Node> computeDirectlyRequiredNodes(@NonNull Iterable<@NonNull Node> requiredNodes) {
+ List<@NonNull Node> nodes = new ArrayList<>();
+ for (@NonNull Node node : requiredNodes) {
+ if (!node.isHead()) {
+ nodes.add(node);
+ }
+ }
+ Collections.sort(nodes, NameUtil.NAMEABLE_COMPARATOR);
+ return nodes;
+ }
+
+ protected @NonNull Iterable<@NonNull Node> computeIndirectlyRequiredNodes(@NonNull Iterable<@NonNull Node> allNodes) {
+ Set<@NonNull Node> nodeSet = Sets.newHashSet(allNodes);
+ SplitterUtil.removeAll(nodeSet, allHeadNodes);
+ SplitterUtil.removeAll(nodeSet, deadNodes);
+ SplitterUtil.removeAll(nodeSet, directlyRequiredNodes);
+ SplitterUtil.removeAll(nodeSet, realizedNodes);
+ List<@NonNull Node> nodesList = Lists.newArrayList(nodeSet);
+ Collections.sort(nodesList, NameUtil.NAMEABLE_COMPARATOR);
+ return nodesList;
+ }
+
+ protected @NonNull Iterable<@NonNull Node> computeReachableNodes(@NonNull Iterable<@NonNull Node> requiredNodes) {
+ Set<@NonNull Node> reachableNodes = SplitterUtil.computeNavigableNodes(allHeadNodes);
+ // assert reachableNodes.containsAll(requiredNodes);
+ for (@NonNull Node node : requiredNodes) {
+ reachableNodes.remove(node);
+ }
+ List<@NonNull Node> reachableNodesList = Lists.newArrayList(reachableNodes);
+ Collections.sort(reachableNodesList, NameUtil.NAMEABLE_COMPARATOR);
+ return reachableNodesList;
+ }
+
+ protected @NonNull Iterable<@NonNull Node> computeRealizedNodes() {
+ Region region = splitter.getRegion();
+ List<@NonNull Node> nodes = Lists.newArrayList(region.getRealizedNodes());
+ Collections.sort(nodes, NameUtil.NAMEABLE_COMPARATOR);
+ return nodes;
+ }
+
+ /**
+ * The required nodes are the sources of all realized navigation edges, and the transitive source
+ * of all computations terminating in a relaized node.
+ */
+ protected @NonNull Iterable<@NonNull Node> computeRequiredNodes() {
+ Set<@NonNull Node> requiredNodeSet = new HashSet<>();
+ for (@NonNull Node node : realizedNodes) {
+ if (node.isOperation()) {
+ computeOperationSources(requiredNodeSet, node);
+ }
+ }
+ Region region = splitter.getRegion();
+ for (@NonNull Edge edge : region.getRealizedEdges()) {
+ requiredNodeSet.add(edge.getSource());
+ requiredNodeSet.add(edge.getTarget());
+ }
+ for (@NonNull Node node : realizedNodes) {
+ requiredNodeSet.remove(node);
+ }
+ List<@NonNull Node> nodes = Lists.newArrayList(requiredNodeSet);
+ Collections.sort(nodes, NameUtil.NAMEABLE_COMPARATOR);
+ return nodes;
+ }
+
+ protected @NonNull Iterable<@NonNull Node> computedVisibleIteratedNodes(Iterable<@NonNull Stage> stages) {
+ List<@NonNull Node> nodes = new ArrayList<>();
+ for (@NonNull Stage stage : stages) {
+ Node node = stage.getIteratedNode();
+ if (node != null) {
+ nodes.add(node);
+ }
+ }
+ Collections.sort(nodes, NameUtil.NAMEABLE_COMPARATOR);
+ return nodes;
+ }
+
+ protected @NonNull Iterable<@NonNull Node> computedVisibleIteratorNodes(Iterable<@NonNull Stage> stages) {
+ List<@NonNull Node> nodes = new ArrayList<>();
+ for (@NonNull Stage stage : stages) {
+ Node node = stage.getIteratorNode();
+ if (node != null) {
+ nodes.add(node);
+ }
+ }
+ Collections.sort(nodes, NameUtil.NAMEABLE_COMPARATOR);
+ return nodes;
+ }
+
+ @Override
+ protected boolean isLive(@NonNull Node node, @NonNull Set<@NonNull Node> deadNodes) {
+ if (node.isRealized()) {
+ return true; // Realized node is needed
+ }
+ for (@NonNull Edge edge : node.getIncomingEdges()) {
+ if (edge.isRealized()) {
+ return true; // Target of a realized edge is needed
+ }
+ }
+ for (@NonNull Edge edge : node.getOutgoingEdges()) {
+ Node targetNode = edge.getTarget();
+ if (!deadNodes.contains(targetNode) && !targetNode.isHead()) {
+ return true; // Source of a live computation of a non-head is needed.
+ }
+ }
+ return false ;
+ }
+
+ @Override
+ public void toString(@NonNull StringBuilder s, int depth) {
+ SplitterUtil.indent(s, depth);
+ s.append("body");
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/CompoundGroup.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/CompoundGroup.java
index 398fa17b2..6df347825 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/CompoundGroup.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/CompoundGroup.java
@@ -81,20 +81,21 @@ class CompoundGroup extends AbstractGroup
}
@Override
- public void buildSplit(@NonNull Split split, @Nullable Edge edge) {
- split.addSimpleGroup(getEntryGroup(), edge);
+ public void buildSplit(@NonNull Split split, @Nullable SimpleGroup sourceSimpleGroup, @Nullable Edge edge) {
+ SimpleGroup entryGroup = getEntryGroup();
+ split.addStage(sourceSimpleGroup, edge, entryGroup);
for (@NonNull Boundary boundary : orderedBoundaries) {
- split.addSimpleGroup(boundary.getTargetGroup(), boundary.getEdge());
+ split.addStage(boundary.getSourceGroup(), boundary.getEdge(), boundary.getTargetGroup());
}
- buildSplit(split);
+ buildSplit(split, entryGroup);
}
/**
* Create a Boundary for each edge whose source end is also in externalSimpleGroups and whose target end is only in internalSimpleGroups.
*/
protected @NonNull Iterable<@NonNull SimpleGroup> computeExternalBoundaries(@NonNull Iterable<@NonNull SimpleGroup> externalSimpleGroups) {
- Set<@NonNull Node> externalReachableNodes = SplitterUtil.computeReachableNodes(SplitterUtil.computeHeadNodes(externalSimpleGroups));
- Set<@NonNull Node> externalComputableNodes = SplitterUtil.computeComputableNodes(externalReachableNodes);
+ Set<@NonNull Node> externalReachableNodes = SplitterUtil.computeNavigableNodes(SplitterUtil.computeHeadNodes(externalSimpleGroups));
+ Set<@NonNull Node> externalComputableNodes = SplitterUtil.computeComputableTargetNodes(externalReachableNodes);
List<@NonNull SimpleGroup> externalInternalSimpleGroups = new ArrayList<>();
for (@NonNull SimpleGroup internalSimpleGroup : internalSimpleGroups) {
Iterable<@NonNull Node> internalReachableNodes = internalSimpleGroup.getReachableNodes();
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadStage.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadStage.java
new file mode 100644
index 000000000..b5d55bad1
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadStage.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * The HeadStage specifies the first part of a multi-headed region split. It has a headNode to represent its primary content.
+ */
+class HeadStage extends HeadedStage
+{
+ public HeadStage(@NonNull SplitterAnalysis splitter, @NonNull SimpleGroup targetSimpleGroup) {
+ super(splitter, null, null, targetSimpleGroup);
+ }
+
+ @Override
+ protected boolean isLive(@NonNull Node node, @NonNull Set<@NonNull Node> deadNodes) {
+ Iterable<@NonNull SimpleGroup> reachableSimpleGroups = splitter.basicGetReachableSimpleGroups(node);
+ if ((reachableSimpleGroups != null) && (Iterables.size(reachableSimpleGroups) > 1)) {
+ return true; // Group intersections must be live.
+ }
+ return super.isLive(node, deadNodes);
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadedStage.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadedStage.java
new file mode 100644
index 000000000..b13c6fa52
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/HeadedStage.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.utilities.NameUtil;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * A Stage specifies part of a multi-headed region split. It has a headNode to represent its primary content
+ * and an edge that defines the entry to the stage, null fir the first stage.
+ */
+abstract class HeadedStage extends AbstractStage
+{
+ protected final @Nullable HeadedStage sourceStage;
+ protected final @Nullable Edge edge;
+ protected final @NonNull SimpleGroup targetSimpleGroup;
+
+ protected final @NonNull Iterable<@NonNull Node> headNodes;
+ protected final @NonNull List<@NonNull Node> navigableNodes;
+ protected final @NonNull List<@NonNull Node> computableNodes;
+ protected final @NonNull Iterable<@NonNull Node> deadNodes;
+
+ /**
+ * Transitive closure of all successors.
+ */
+ protected final @NonNull Set<@NonNull AbstractStage> successors = new HashSet<>();
+
+ protected HeadedStage(@NonNull SplitterAnalysis splitter, @Nullable HeadedStage sourceStage, @Nullable Edge edge, @NonNull SimpleGroup targetSimpleGroup) {
+ super(splitter);
+ this.sourceStage = sourceStage;
+ this.edge = edge;
+ this.targetSimpleGroup = targetSimpleGroup;
+ this.headNodes = targetSimpleGroup.getHeadNodes();
+ Iterable<@NonNull Node> allNavigableNodes = targetSimpleGroup.getReachableNodes();
+ Set<@NonNull Node> allComputableNodes = SplitterUtil.computeComputableTargetNodes(allNavigableNodes);
+ Set<@NonNull Node> allNodes = Sets.newHashSet(allNavigableNodes);
+ allNodes.addAll(allComputableNodes);
+ this.deadNodes = computeDeadNodes(allNodes);
+ this.navigableNodes = Lists.newArrayList(allNavigableNodes);
+ this.computableNodes = Lists.newArrayList(allComputableNodes);
+ int oldSize = this.computableNodes.size();
+ this.computableNodes.removeAll(navigableNodes);
+ int newSize = this.computableNodes.size();
+ assert oldSize-newSize == navigableNodes.size();
+ SplitterUtil.removeAll(navigableNodes, headNodes);
+ SplitterUtil.removeAll(navigableNodes, deadNodes);
+ SplitterUtil.removeAll(computableNodes, deadNodes);
+ Collections.sort(navigableNodes, NameUtil.NAMEABLE_COMPARATOR);
+ Collections.sort(computableNodes, NameUtil.NAMEABLE_COMPARATOR);
+ }
+
+ public void addSuccessor(@NonNull HeadedStage successorStage) {
+ if (successors.add(successorStage)) {
+ if (sourceStage != null) {
+ sourceStage.addSuccessor(successorStage);
+ }
+ }
+ }
+
+ @Override
+ protected @NonNull String buildContents(@NonNull StringBuilder s) {
+ build(s, "head nodes", headNodes);
+ build(s, "navigable nodes", navigableNodes);
+ build(s, "computable nodes", computableNodes);
+ build(s, "dead nodes", deadNodes);
+ return targetSimpleGroup.getName();
+ }
+
+ @Override
+ public void check() {
+ Set<@NonNull Node> accumulator = new HashSet<>();
+ checkAccumulate(accumulator, headNodes);
+ checkAccumulate(accumulator, navigableNodes);
+ checkAccumulate(accumulator, computableNodes);
+ checkAccumulate(accumulator, deadNodes);
+ Set<@NonNull Node> navigableNodes = SplitterUtil.computeNavigableNodes(headNodes);
+ Set<@NonNull Node> computableNodes = SplitterUtil.computeComputableTargetNodes(navigableNodes);
+ assert computableNodes.containsAll(navigableNodes);
+ Set<@NonNull Node> allNodes = new HashSet<>(navigableNodes);
+ allNodes.addAll(computableNodes);
+ checkAccumulated(accumulator, allNodes);
+ }
+
+ @Override
+ protected boolean isLive(@NonNull Node node, @NonNull Set<@NonNull Node> deadNodes) {
+ if (node.isHead()) {
+ return true;
+ }
+ if (node.isRealized()) {
+ return false;
+ }
+ int bidirectionals = 0;
+ for (@NonNull Edge edge : node.getOutgoingEdges()) {
+ if (!edge.isRealized() && !deadNodes.contains(edge.getTarget())) {
+ if (!SplitterUtil.isBidirectional(edge)) {
+ return true;
+ }
+ bidirectionals++;
+ }
+ }
+ return bidirectionals > 1;
+ }
+
+ @Override
+ public void toString(@NonNull StringBuilder s, int depth) {
+ Edge edge2 = edge;
+ if (edge2 != null) {
+ SplitterUtil.indent(s, depth);
+ s.append(edge2.isComputation() ? "forward-edge : " : "reverse-edge : ");
+ s.append(edge2.getName());
+ s.append(" : ");
+ s.append(edge2);
+ s.append("\n");
+ }
+ SplitterUtil.indent(s, depth);
+ s.append("simple-group : ");
+ s.append(targetSimpleGroup.getName());
+ s.append(" : ");
+ s.append(targetSimpleGroup.getHeadNode());
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/LoopStage.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/LoopStage.java
new file mode 100644
index 000000000..5551356df
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/LoopStage.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+
+/**
+ * A LoopStage specifies an intermediate looping part of a multi-headed region split.
+ * It has a headNode to represent its primary content and an edge that defines the entry to the stage.
+ * LoopStages may be folded into the HeadStage.
+ */
+class LoopStage extends HeadedStage
+{
+ public LoopStage(@NonNull SplitterAnalysis splitter, @Nullable HeadedStage sourceStage, @NonNull Edge edge, @NonNull SimpleGroup targetSimpleGroup) {
+ super(splitter, sourceStage, edge, targetSimpleGroup);
+ }
+
+ @Override
+ protected @NonNull String buildContents(@NonNull StringBuilder s) {
+ Node iteratedNode = getIteratedNode();
+ Node iteratorNode = getIteratorNode();
+ build(s, "iteration domain", Collections.singletonList(iteratedNode));
+ build(s, iteratorNode.isPredicated() ? "hazardous-iterator" : "safe-iterator", Collections.singletonList(iteratorNode));
+ return super.buildContents(s);
+ }
+
+ @Override
+ public void check() {} // FIXME use inherited check
+
+ public @NonNull Edge getEdge() {
+ assert edge != null;
+ return edge;
+ }
+
+ @Override
+ public @NonNull Node getIteratedNode() {
+ Edge edge = getEdge();
+ return edge.isComputation() ? edge.getSource() : edge.getTarget();
+ }
+
+ @Override
+ public @NonNull Node getIteratorNode() {
+ Edge edge = getEdge();
+ return edge.isComputation() ? edge.getTarget() : edge.getSource();
+ }
+
+ @Override
+ protected boolean isLive(@NonNull Node node, @NonNull Set<@NonNull Node> deadNodes) {
+ Edge edge = getEdge();
+ if ((node == edge.getSource()) || (node == edge.getTarget())) {
+ return true;
+ }
+ return super.isLive(node, deadNodes);
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SimpleGroup.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SimpleGroup.java
index 34ced495d..686a8db01 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SimpleGroup.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SimpleGroup.java
@@ -36,9 +36,9 @@ class SimpleGroup extends AbstractGroup
public void computeMutualOrdering(@NonNull Iterable<@NonNull SimpleGroup> externalSimpleGroups) {}
@Override
- protected void buildSplit(@NonNull Split split, @Nullable Edge edge) {
- split.addSimpleGroup(this, edge);
- buildSplit(split);
+ protected void buildSplit(@NonNull Split split, @Nullable SimpleGroup sourceSimpleGroup, @Nullable Edge edge) {
+ split.addStage(sourceSimpleGroup, edge, this);
+ buildSplit(split, this);
}
public @NonNull Node getHeadNode() {
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Split.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Split.java
index 3d846b055..29e325772 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Split.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Split.java
@@ -10,11 +10,14 @@
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
-import java.util.LinkedHashMap;
+import java.util.ArrayList;
+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.qvtd.compiler.internal.qvtp2qvts.Edge;
-import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
/**
* A Split captures the result of the analysis that enables a multi-headed region to be split.
@@ -22,35 +25,76 @@ import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
public class Split
{
/**
- * The head nodes of each region in navigation order and the edge that enables each head to be
- * reached from earlier heads. The first head has a null edge.
+ * The Splitter supervising the region splitting.
+ */
+ protected final @NonNull SplitterAnalysis splitter;
+
+ /**
+ * The Stages that specify a multi-headed region split.
*/
- protected final @NonNull LinkedHashMap<@NonNull Node, @Nullable Edge> headNode2edge = new LinkedHashMap<>();
+ private final @NonNull List<@NonNull Stage> stages = new ArrayList<>();
+
+ /**
+ * Mapping from each head node to the Stage that specifies it.
+ */
+ private final @NonNull Map<@NonNull SimpleGroup, @NonNull HeadedStage> simpleGroup2stage = new HashMap<>();
+
+ public Split(@NonNull SplitterAnalysis splitter) {
+ this.splitter = splitter;
+ }
+
+ public void addBodyStage() {
+ Stage bodyStage = new BodyStage(splitter, stages);
+ stages.add(bodyStage);
+ }
+
+ public @NonNull Stage addStage(@Nullable SimpleGroup sourceSimpleGroup, @Nullable Edge edge, @NonNull SimpleGroup targetSimpleGroup) {
+ assert !simpleGroup2stage.containsKey(targetSimpleGroup);
+ HeadedStage sourceStage;
+ HeadedStage targetStage;
+ if (edge == null) {
+ assert sourceSimpleGroup == null;
+ sourceStage = null;
+ targetStage = new HeadStage(splitter, targetSimpleGroup);
+ }
+ else {
+ assert sourceSimpleGroup != null;
+ sourceStage = /*sourceSimpleGroup != null ?*/ simpleGroup2stage.get(sourceSimpleGroup);// : null;
+ targetStage = new LoopStage(splitter, sourceStage, edge, targetSimpleGroup);
+ }
+ stages.add(targetStage);
+ simpleGroup2stage.put(targetSimpleGroup, targetStage);
+ if (sourceStage != null) {
+ sourceStage.addSuccessor(targetStage);
+ }
+ return targetStage;
+ }
+
+ public void check() {
+ for (@NonNull Stage stage : stages) {
+ stage.debug();
+ stage.check();
+ }
+ }
+
+ public void debug() {
+ for (@NonNull Stage stage : stages) {
+ stage.debug();
+ }
+ }
- public void addSimpleGroup(@NonNull SimpleGroup simpleGroup, @Nullable Edge edge) {
- Node headNode = simpleGroup.getHeadNode();
- assert !headNode2edge.containsKey(headNode);
- headNode2edge.put(headNode, edge);
+ @Override
+ public @NonNull String toString() {
+ StringBuilder s = new StringBuilder();
+ toString(s, 0);
+ return s.toString();
}
public void toString(@NonNull StringBuilder s, int depth) {
SplitterUtil.indent(s, depth);
- for (@NonNull Node node : headNode2edge.keySet()) {
- Edge edge = headNode2edge.get(node);
- if (edge != null) {
- s.append("\n");
- SplitterUtil.indent(s, depth+1);
- s.append(edge.isComputation() ? "forward-edge : " : "reverse-edge : ");
- s.append(edge.getName());
- s.append(" : ");
- s.append(edge);
- }
+ for (@NonNull Stage stage : stages) {
s.append("\n");
- SplitterUtil.indent(s, depth+1);
- s.append("head-node : ");
- s.append(node.getName());
- s.append(" : ");
- s.append(node);
+ stage.toString(s, depth+1);
}
}
} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Splitter.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Splitter.java
index 5de4afe4b..a8c4fac08 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Splitter.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Splitter.java
@@ -48,6 +48,7 @@ public class Splitter extends SplitterAnalysis
// public static final @NonNull TracingOption ANALYSIS = new TracingOption(CompilerConstants.PLUGIN_ID, "qvtp2qvts/split/analysis");
public static final @NonNull TracingOption GROUPS = new TracingOption(CompilerConstants.PLUGIN_ID, "qvtp2qvts/split/groups");
public static final @NonNull TracingOption RESULT = new TracingOption(CompilerConstants.PLUGIN_ID, "qvtp2qvts/split/result");
+ public static final @NonNull TracingOption STAGES = new TracingOption(CompilerConstants.PLUGIN_ID, "qvtp2qvts/split/stages");
/**
* Map from each simple group to the mutually navigable group that contains it.
@@ -169,10 +170,11 @@ public class Splitter extends SplitterAnalysis
* region and the edges that traverse them.
*/
protected Split computeSplit(@NonNull Iterable<@NonNull AbstractGroup> rootGroups) {
- Split split = new Split();
+ Split split = new Split(this);
for (@NonNull AbstractGroup rootGroup : rootGroups) {
- rootGroup.buildSplit(split, null);
+ rootGroup.buildSplit(split, null, null);
}
+ split.addBodyStage();
return split;
}
@@ -292,10 +294,9 @@ public class Splitter extends SplitterAnalysis
Split split = computeSplit(rootGroups);
//
if (RESULT.isActive()) {
- StringBuilder s = new StringBuilder();
- split.toString(s, 0);
- RESULT.println(region + s.toString());
+ RESULT.println(region + split.toString());
}
+ split.check();
return split;
}
}
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterAnalysis.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterAnalysis.java
index 9a9ecb9fc..0914eda06 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterAnalysis.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterAnalysis.java
@@ -108,6 +108,10 @@ class SplitterAnalysis
return ClassUtil.nonNullState(reachableNode2simpleGroups.get(node));
}
+ public @NonNull Region getRegion() {
+ return region;
+ }
+
@Override
public @NonNull String toString() {
return region.getName();
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterUtil.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterUtil.java
index 5d3042980..42fadec73 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterUtil.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/SplitterUtil.java
@@ -11,6 +11,7 @@
package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -26,20 +27,56 @@ import com.google.common.collect.Lists;
class SplitterUtil
{
- public static @NonNull Set<@NonNull Node> computeComputableNodes(@NonNull Iterable<@NonNull Node> nodes) {
- Set<@NonNull Node> computableNodes = new HashSet<>();
- for (@NonNull Node node : nodes) {
- computeComputableNodes(computableNodes, node);
+ /**
+ * Return the nodes (including seedNodes) that can be reached by computation from seedNodes.
+ */
+ public static @NonNull Set<@NonNull Node> computeComputableTargetNodes(@NonNull Iterable<@NonNull Node> seedNodes) {
+ Set<@NonNull Node> computableTargetNodes = new HashSet<>();
+ Set<@NonNull Node> unresolvedOperationNodes = new HashSet<>();
+ for (@NonNull Node node : seedNodes) {
+ computeComputableTargetNodes(computableTargetNodes, node, unresolvedOperationNodes);
}
- return Collections.unmodifiableSet(computableNodes);
+ while (!unresolvedOperationNodes.isEmpty()) {
+ boolean resolvedOne = false;
+ for (@NonNull Node unresolvedNode : new ArrayList<>(unresolvedOperationNodes)) {
+ boolean allReachable = true;
+ for (@NonNull Edge edge : unresolvedNode.getIncomingEdges()) {
+ if (edge.isArgument() && !computableTargetNodes.contains(edge.getSource())) {
+ allReachable = false;
+ }
+ }
+ if (allReachable) {
+ resolvedOne = true;
+ unresolvedOperationNodes.remove(unresolvedNode);
+ computeComputableTargetNodes(computableTargetNodes, unresolvedNode, unresolvedOperationNodes);
+ }
+ }
+ if (!resolvedOne) {
+ break;
+ }
+ }
+ return Collections.unmodifiableSet(computableTargetNodes);
}
- private static void computeComputableNodes(@NonNull Set<@NonNull Node> computableNodes, @NonNull Node sourceNode) {
- if (computableNodes.add(sourceNode)) {
+ private static void computeComputableTargetNodes(@NonNull Set<@NonNull Node> computableTargetNodes, @NonNull Node sourceNode, @NonNull Set<@NonNull Node> unresolvedOperationNodes) {
+ if (computableTargetNodes.add(sourceNode)) {
for (@NonNull Edge edge : sourceNode.getOutgoingEdges()) {
- if (edge.isComputation()) {
+ if (edge.isComputation() || (edge.isNavigation() && !edge.isNavigable())) {
Node targetNode = edge.getTarget();
- computeComputableNodes(computableNodes, targetNode);
+ if (targetNode.isRealized() && targetNode.isOperation()) {
+ unresolvedOperationNodes.add(targetNode); // Keys require an all-input check.
+ }
+ else { // FIXME ?? exclude computation of many that is consumed as not-many
+ computeComputableTargetNodes(computableTargetNodes, targetNode, unresolvedOperationNodes);
+ }
+ }
+ }
+ for (@NonNull Edge edge : sourceNode.getIncomingEdges()) { // FIXME gather constant inputs to avoid assert fail
+ if (edge.isComputation()){ // || (edge.isConstant())) {
+ Node node = edge.getSource();
+ if (node.isConstant()) {
+ computeComputableTargetNodes(computableTargetNodes, node, unresolvedOperationNodes);
+ }
}
}
}
@@ -72,39 +109,53 @@ class SplitterUtil
return s.toString();
}
-
- /* public static @NonNull Set<@NonNull Node> computeReachableNodes(@NonNull Iterable<@NonNull SimpleGroup> simpleGroups) {
- Set<@NonNull Node> reachableNodes = new HashSet<>();
- for (@NonNull SimpleGroup simpleGroup : simpleGroups) {
- Iterables.addAll(reachableNodes, simpleGroup.getReachableNodes());
- }
- return Collections.unmodifiableSet(reachableNodes);
- } */
-
- public static @NonNull Set<@NonNull Node> computeReachableNodes(@NonNull Iterable<@NonNull Node> headNodes) {
+ /**
+ * Return the nodes (including headNodes) that can be reached by to-one navigation from headNodes.
+ */
+ public static @NonNull Set<@NonNull Node> computeNavigableNodes(@NonNull Iterable<@NonNull Node> headNodes) {
Set<@NonNull Node> reachableNodes = new HashSet<>();
for (@NonNull Node headNode : headNodes) {
- computeReachableNodes(reachableNodes, headNode);
+ computeNavigableNodes(reachableNodes, headNode);
}
return reachableNodes;
}
- private static @NonNull Set<@NonNull Node> computeReachableNodes(@NonNull Set<@NonNull Node> reachableNodes, @NonNull Node sourceNode) {
+ private static @NonNull Set<@NonNull Node> computeNavigableNodes(@NonNull Set<@NonNull Node> reachableNodes, @NonNull Node sourceNode) {
if (reachableNodes.add(sourceNode)) {
for (@NonNull NavigationEdge edge : sourceNode.getNavigationEdges()) {
assert edge.getSource() == sourceNode;
if (!edge.isRealized() && edge.isNavigable()) {
Node targetNode = edge.getTarget();
- computeReachableNodes(reachableNodes, targetNode);
+ computeNavigableNodes(reachableNodes, targetNode);
}
}
}
return reachableNodes;
}
+ public static boolean isBidirectional(@NonNull Edge edge) {
+ if (!edge.isNavigation()) {
+ return false;
+ }
+ Node sourceNode = edge.getSource();
+ Node targetNode = edge.getTarget();
+ for (@NonNull Edge edge2 : targetNode.getOutgoingEdges()) {
+ if (edge2.isNavigation() && (edge2.getTarget() == sourceNode)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static void indent(@NonNull StringBuilder s, int depth) {
for (int i = 0; i < depth; i++) {
s.append(" ");
}
}
+
+ public static <T> void removeAll(@NonNull Collection<T> removeFrom, @NonNull Iterable<T> elementsToTemove) {
+ for (T element : elementsToTemove) {
+ removeFrom.remove(element);
+ }
+ }
} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Stage.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Stage.java
new file mode 100644
index 000000000..cc36a3c9c
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvti/splitter/Stage.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Willink Transformations and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * E.D.Willink - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.qvtd.compiler.internal.qvts2qvti.splitter;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+
+/**
+ * A Stage specifies part of a multi-headed region split. It has a headNode to represent its primary content
+ * and an edge that defines the entry to the stage, null fir the first stage.
+ */
+interface Stage
+{
+ void check();
+ void debug();
+ @Nullable Node getIteratedNode();
+ @Nullable Node getIteratorNode();
+ void toString(@NonNull StringBuilder s, int depth);
+} \ No newline at end of file
diff --git a/tests/org.eclipse.qvtd.xtext.qvtcore.tests/src/org/eclipse/qvtd/xtext/qvtcore/tests/QVTcCompilerTests.java b/tests/org.eclipse.qvtd.xtext.qvtcore.tests/src/org/eclipse/qvtd/xtext/qvtcore/tests/QVTcCompilerTests.java
index 24c0377ac..ff9cf3a0c 100644
--- a/tests/org.eclipse.qvtd.xtext.qvtcore.tests/src/org/eclipse/qvtd/xtext/qvtcore/tests/QVTcCompilerTests.java
+++ b/tests/org.eclipse.qvtd.xtext.qvtcore.tests/src/org/eclipse/qvtd/xtext/qvtcore/tests/QVTcCompilerTests.java
@@ -233,7 +233,7 @@ public class QVTcCompilerTests extends LoadTestCase
}
else {
Resource inputResource = getResourceSet().getResource(modelURI, true);
- generatedExecutor.getTransformer().addRootObjects(modelName, ClassUtil.nonNullState(inputResource.getContents()));
+ generatedExecutor.getTransformer().addRootObjects(modelName, ClassUtil.nullFree(inputResource.getContents()));
}
}
@@ -431,6 +431,8 @@ public class QVTcCompilerTests extends LoadTestCase
// Scheduler.REGION_DEPTH.setState(true);
// Scheduler.REGION_ORDER.setState(true);
// Scheduler.REGION_TRAVERSAL.setState(true);
+ Splitter.RESULT.setState(true);
+ Splitter.STAGES.setState(true);
// QVTs2QVTiVisitor.POLLED_PROPERTIES.setState(true);
Splitter.RESULT.setState(true);
MyQVT myQVT = new MyQVT("uml2rdbms", Simpleuml2rdbmsPackage.eINSTANCE, SimpleumlPackage.eINSTANCE, SimplerdbmsPackage.eINSTANCE);
diff --git a/tests/org.eclipse.qvtd.xtext.qvtrelation.tests/src/org/eclipse/qvtd/xtext/qvtrelation/tests/QVTrCompilerTests.java b/tests/org.eclipse.qvtd.xtext.qvtrelation.tests/src/org/eclipse/qvtd/xtext/qvtrelation/tests/QVTrCompilerTests.java
index a042edd6b..44af94c7d 100644
--- a/tests/org.eclipse.qvtd.xtext.qvtrelation.tests/src/org/eclipse/qvtd/xtext/qvtrelation/tests/QVTrCompilerTests.java
+++ b/tests/org.eclipse.qvtd.xtext.qvtrelation.tests/src/org/eclipse/qvtd/xtext/qvtrelation/tests/QVTrCompilerTests.java
@@ -428,6 +428,7 @@ public class QVTrCompilerTests extends LoadTestCase
@Test
public void testQVTrCompiler_HierarchicalStateMachine2FlatStateMachine_CG() throws Exception {
Splitter.RESULT.setState(true);
+ Splitter.STAGES.setState(true);
// Scheduler.DEBUG_GRAPHS.setState(true);
// AbstractTransformer.EXCEPTIONS.setState(true);
// AbstractTransformer.INVOCATIONS.setState(true);
@@ -489,6 +490,7 @@ public class QVTrCompilerTests extends LoadTestCase
public void testQVTrCompiler_SeqToStm_CG() throws Exception {
// Splitter.GROUPS.setState(true);
Splitter.RESULT.setState(true);
+ Splitter.STAGES.setState(true);
// AbstractTransformer.EXCEPTIONS.setState(true);
// AbstractTransformer.INVOCATIONS.setState(true);
// QVTm2QVTp.PARTITIONING.setState(true);

Back to the top