Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEd Willink2016-11-28 12:05:44 +0000
committerEd Willink2016-11-28 12:09:22 +0000
commitd9033c9b3ed82ecf4515bb87ee530c21e9fb7591 (patch)
tree6c84858ab14415171787aec322a81f95fbd79454
parent3628ab7d08f5ac571354610ecae158e3c566acbf (diff)
downloadorg.eclipse.qvtd-asanchez/thesis.tar.gz
org.eclipse.qvtd-asanchez/thesis.tar.xz
org.eclipse.qvtd-asanchez/thesis.zip
[507096] Rewrite EarlyMerger using Edge/Node/RegionMergerasanchez/thesis
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/AbstractMappingRegion.java7
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/MicroMappingRegion.java1
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/QVTp2QVTs.java4
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionMerger.java269
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionUtil.java26
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EarlyMerger.java369
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EdgeMerger.java143
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/NodeMerger.java161
-rw-r--r--plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/RegionMerger.java271
-rw-r--r--plugins/org.eclipse.qvtd.pivot.qvtbase/annotations/com/google/common/collect/Sets.eea3
10 files changed, 813 insertions, 441 deletions
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/AbstractMappingRegion.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/AbstractMappingRegion.java
index 8438bb6c9..eda7efca3 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/AbstractMappingRegion.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/AbstractMappingRegion.java
@@ -256,7 +256,7 @@ public abstract class AbstractMappingRegion extends AbstractRegion implements Ma
protected @NonNull List<@NonNull Node> computeHeadNodes() {
//
// A head node is reachable from very few nodes, typically just itself, occasionally from a small group of mutually bidirectional nodes,
- // so we search for the least reachable nodes taking care to avoid hazrads from the source-to-target / target-source asymmetry.
+ // so we search for the least reachable nodes taking care to avoid hazards from the source-to-target / target-source asymmetry.
//
List<@NonNull Node> navigableNodes = new ArrayList<>();
for (@NonNull Node node : getNodes()) {
@@ -290,8 +290,9 @@ public abstract class AbstractMappingRegion extends AbstractRegion implements Ma
Node targetNode = navigationEdge.getTarget();
if (targetNode.isMatched() && targetNode.isClass() && !targetNode.isExplicitNull()) {
Set<@NonNull Node> sourceClosure = targetFromSourceClosure.get(targetNode);
- assert sourceClosure != null;
- sourceClosure.add(sourceNode);
+ if (sourceClosure != null) {
+ sourceClosure.add(sourceNode);
+ }
}
}
}
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/MicroMappingRegion.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/MicroMappingRegion.java
index e9a3ba3ee..c951526f6 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/MicroMappingRegion.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/MicroMappingRegion.java
@@ -25,6 +25,7 @@ public class MicroMappingRegion extends AbstractMappingRegion
public MicroMappingRegion(@NonNull MappingRegion mappingRegion, @NonNull String prefix, @NonNull String suffix) {
super(mappingRegion.getMultiRegion());
+ assert !(mappingRegion instanceof MicroMappingRegion);
this.mappingRegion = mappingRegion;
this.prefix = prefix;
this.suffix = suffix;
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/QVTp2QVTs.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/QVTp2QVTs.java
index f9757089d..eb3fc08ff 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/QVTp2QVTs.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/QVTp2QVTs.java
@@ -141,14 +141,14 @@ public class QVTp2QVTs extends SchedulerConstants
mappingRegion.writeDebugGraphs(null);
}
}
- List<@NonNull BasicMappingRegion> orderedRegions = new ArrayList<>();
+ List<@NonNull MappingRegion> orderedRegions = new ArrayList<>();
for (@NonNull Mapping mapping : orderedMappings) {
BasicMappingRegion mappingRegion = mapping2mappingRegion.get(mapping);
assert mappingRegion != null;
orderedRegions.add(mappingRegion);
// mappingRegion.resolveRecursion();
}
- List<@NonNull Region> activeRegions = new ArrayList<>(EarlyMerger.earlyRegionMerge(orderedRegions));
+ List<@NonNull Region> activeRegions = new ArrayList<>(EarlyMerger.merge(orderedRegions));
// for (@NonNull Region activeRegion : activeRegions) {
// ((AbstractRegion)activeRegion).resolveRecursion();
// }
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionMerger.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionMerger.java
deleted file mode 100644
index 7dbc4f660..000000000
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionMerger.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*******************************************************************************
- * 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.qvtp2qvts;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.ocl.pivot.Property;
-import org.eclipse.ocl.pivot.TypedElement;
-import org.eclipse.ocl.pivot.utilities.ClassUtil;
-import org.eclipse.qvtd.compiler.internal.qvtp2qvts.impl.VariableNodeImpl;
-
-public class RegionMerger extends AbstractVisitor<@Nullable Visitable>
-{
- public static @NonNull MappingRegion createMergedRegion(@NonNull MappingRegion primaryRegion, @NonNull MappingRegion secondaryRegion, @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode) {
- RegionMerger regionMerger = new RegionMerger(primaryRegion, secondaryRegion, secondaryNode2primaryNode);
- Visitable mergedRegion = secondaryRegion.accept(regionMerger);
- assert mergedRegion != null;
- return (MappingRegion) mergedRegion;
- }
-
- protected final @NonNull MappingRegion primaryRegion;
- protected final @NonNull MappingRegion secondaryRegion;
- protected final @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode;
- protected final @NonNull MappingRegion mergedRegion;
- protected final @NonNull Map<@NonNull Node, @NonNull Node> oldNode2mergedNode = new HashMap<>();
- protected final @NonNull Map<@NonNull Edge, @NonNull List<@NonNull Edge>> oldEdge2oldEdges = new HashMap<>();
- private final @NonNull Map<@NonNull Edge, @NonNull Edge> debugOldEdge2mergedEdge = new HashMap<>();
-
- protected RegionMerger(@NonNull MappingRegion primaryRegion, @NonNull MappingRegion secondaryRegion, @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode) {
- this.primaryRegion = primaryRegion;
- this.secondaryRegion = secondaryRegion;
- this.secondaryNode2primaryNode = secondaryNode2primaryNode;
- List<@NonNull String> names = new ArrayList<>();
- names.add(ClassUtil.nonNullState(primaryRegion.getName()));
- names.add(ClassUtil.nonNullState(secondaryRegion.getName()));
- Collections.sort(names);
- StringBuilder s = new StringBuilder();
- // s.append(getClass().getSimpleName());
- for (String name : names) {
- if (s.length() > 0) {
- s.append("\\n");
- }
- s.append(name);
- }
- this.mergedRegion = new NamedMappingRegion(primaryRegion.getMultiRegion(), s.toString());
- }
-
- protected void accumulateEdge(@NonNull Map<@NonNull Node, @NonNull Map<@NonNull Node, @NonNull List<@NonNull List<@NonNull Edge>>>> mergedSourceNode2mergedTargetNode2listOfOldEdges, @NonNull Edge oldEdge) {
- // List<@NonNull Edge> oldOldEdges2 = oldEdge2oldEdges.get(oldEdge);
- // assert oldOldEdges2 == null;
- Node mergedSourceNode = oldNode2mergedNode.get(oldEdge.getSource());
- Node mergedTargetNode = oldNode2mergedNode.get(oldEdge.getTarget());
- assert (mergedSourceNode != null) && (mergedTargetNode != null);
- Map<@NonNull Node, @NonNull List<@NonNull List<@NonNull Edge>>> mergedTargetNode2listOfOldEdges = mergedSourceNode2mergedTargetNode2listOfOldEdges.get(mergedSourceNode);
- if (mergedTargetNode2listOfOldEdges == null) {
- mergedTargetNode2listOfOldEdges = new HashMap<>();
- mergedSourceNode2mergedTargetNode2listOfOldEdges.put(mergedSourceNode, mergedTargetNode2listOfOldEdges);
- }
- List<@NonNull List<@NonNull Edge>> listOfOldEdges = mergedTargetNode2listOfOldEdges.get(mergedTargetNode);
- if (listOfOldEdges == null) {
- listOfOldEdges = new ArrayList<>();
- mergedTargetNode2listOfOldEdges.put(mergedTargetNode, listOfOldEdges);
- }
- List<@NonNull Edge> matchingOldEdges = null;
- for (@NonNull List<@NonNull Edge> oldEdges : listOfOldEdges) {
- if (sameEdge(oldEdge, oldEdges)) {
- matchingOldEdges = oldEdges;
- break;
- }
- }
- if (matchingOldEdges == null) {
- matchingOldEdges = new ArrayList<>();
- listOfOldEdges.add(matchingOldEdges);
- }
- matchingOldEdges.add(oldEdge);
- List<@NonNull Edge> oldOldEdges = oldEdge2oldEdges.put(oldEdge, matchingOldEdges);
- assert oldOldEdges == null;
- }
-
- private void checkEdges(@NonNull Region oldRegion) {
- for (@NonNull Edge oldEdge : oldRegion.getEdges()) {
- assert oldEdge.getRegion() == oldRegion;
- if (!oldEdge.isRecursion() && !oldEdge.isSecondary()) { // FIXME Remove this irregularity
- List<@NonNull Edge> oldEdges = oldEdge2oldEdges.get(oldEdge);
- assert oldEdges != null;
- assert oldEdges.contains(oldEdge);
- Edge mergedEdge = debugOldEdge2mergedEdge.get(oldEdge);
- assert mergedEdge != null;
- assert mergedEdge.getRegion() == mergedRegion;
- }
- }
- }
-
- private void checkNodes(@NonNull Region oldRegion) {
- for (@NonNull Node oldNode : oldRegion.getNodes()) {
- assert oldNode.getRegion() == oldRegion;
- Node mergedNode = oldNode2mergedNode.get(oldNode);
- assert mergedNode != null;
- assert mergedNode.getRegion() == mergedRegion;
- }
- }
-
- protected void createMergedEdge(@NonNull Iterable<@NonNull Edge> oldEdges) {
- Edge mergedEdge = null;
- for (@NonNull Edge oldEdge : oldEdges) {
- mergedEdge = (Edge)oldEdge.accept(this);
- break;
- }
- if (mergedEdge != null) {
- for (@NonNull Edge oldEdge : oldEdges) {
- debugOldEdge2mergedEdge.put(oldEdge, mergedEdge);
- }
- }
- }
-
- private boolean sameEdge(@NonNull Edge newEdge, @NonNull Iterable<@NonNull Edge> oldEdges) {
- if (newEdge instanceof NavigableEdge) {
- Property newProperty = ((NavigableEdge)newEdge).getProperty();
- for (@NonNull Edge oldEdge : oldEdges) {
- if (oldEdge instanceof NavigableEdge) {
- Property oldProperty = ((NavigableEdge)oldEdge).getProperty();
- if (oldProperty == newProperty) {
- return true;
- }
- }
- }
- }
- else {
- Class<? extends @NonNull Edge> newClass = newEdge.getClass();
- for (@NonNull Edge oldEdge : oldEdges) {
- Class<? extends @NonNull Edge> oldClass = oldEdge.getClass();
- if (oldClass == newClass) {
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public @NonNull Visitable visitEdge(@NonNull Edge edge) {
- Node mergedSourceNode = oldNode2mergedNode.get(edge.getSource());
- Node mergedTargetNode = oldNode2mergedNode.get(edge.getTarget());
- assert (mergedSourceNode != null) && (mergedTargetNode != null);
- EdgeRole edgeRole = null;
- List<@NonNull Edge> oldEdges = oldEdge2oldEdges.get(edge);
- assert oldEdges != null;
- for (@NonNull Edge oldEdge : oldEdges) {
- EdgeRole edgeRole2 = oldEdge.getEdgeRole();
- edgeRole = edgeRole != null ? RegionUtil.mergeToMoreKnownPhase(edgeRole, edgeRole2) : edgeRole2;
- }
- assert edgeRole != null;
- return edge.createEdge(edgeRole, mergedSourceNode, mergedTargetNode);
- }
-
- @Override
- public @NonNull MappingRegion visitMappingRegion(@NonNull MappingRegion mappingRegion) {
- Set<@NonNull Node> toBeMergedNodes = new HashSet<>(primaryRegion.getNodes());
- toBeMergedNodes.removeAll(secondaryNode2primaryNode.values());
- toBeMergedNodes.addAll(secondaryRegion.getNodes());
- for (@NonNull Node toBeMergedNode : toBeMergedNodes) {
- toBeMergedNode.accept(this);
- }
- //
- //
- //
- Map<@NonNull Node, @NonNull Map<@NonNull Node, @NonNull List<@NonNull List<@NonNull Edge>>>> mergedSourceNode2mergedTargetNode2listOfOldEdges = new HashMap<>();
- for (@NonNull Edge oldEdge : primaryRegion.getEdges()) {
- if (!oldEdge.isRecursion()) { // FIXME Remove this irregularity
- accumulateEdge(mergedSourceNode2mergedTargetNode2listOfOldEdges, oldEdge);
- }
- }
- for (@NonNull Edge oldEdge : secondaryRegion.getEdges()) {
- if (!oldEdge.isRecursion()) { // FIXME Remove this irregularity
- accumulateEdge(mergedSourceNode2mergedTargetNode2listOfOldEdges, oldEdge);
- }
- }
- for (@NonNull Map<@NonNull Node, @NonNull List<@NonNull List<@NonNull Edge>>> mergedTargetNode2listOfOldEdges : mergedSourceNode2mergedTargetNode2listOfOldEdges.values()) {
- for (@NonNull List<@NonNull List<@NonNull Edge>> listOfOldEdges : mergedTargetNode2listOfOldEdges.values()) {
- assert listOfOldEdges != null;
- for (@NonNull List<@NonNull Edge> oldEdges : listOfOldEdges) {
- createMergedEdge(oldEdges);
- }
- }
- }
- checkNodes(primaryRegion);
- checkNodes(secondaryRegion);
- checkEdges(primaryRegion);
- checkEdges(secondaryRegion);
- return mergedRegion;
- }
-
- @Override
- public @Nullable Visitable visitNavigableEdge(@NonNull NavigableEdge navigationEdge) {
- if (navigationEdge.isSecondary()) {
- return null;
- }
- Node mergedSourceNode = oldNode2mergedNode.get(navigationEdge.getSource());
- Node mergedTargetNode = oldNode2mergedNode.get(navigationEdge.getTarget());
- assert (mergedSourceNode != null) && (mergedTargetNode != null);
- EdgeRole edgeRole = null;
- List<@NonNull Edge> oldEdges = oldEdge2oldEdges.get(navigationEdge);
- assert oldEdges != null;
- for (@NonNull Edge oldEdge : oldEdges) {
- EdgeRole edgeRole2 = oldEdge.getEdgeRole();
- edgeRole = edgeRole != null ? RegionUtil.mergeToMoreKnownPhase(edgeRole, edgeRole2) : edgeRole2;
- }
- assert edgeRole != null;
- return navigationEdge.createEdge(edgeRole, mergedSourceNode, mergedTargetNode);
- }
-
- @Override
- public @NonNull Node visitNode(@NonNull Node node) {
- NodeRole nodeRole = node.getNodeRole();
- Node primaryNode = secondaryNode2primaryNode.get(node);
- if (primaryNode != null) {
- nodeRole = nodeRole.merge(primaryNode.getNodeRole());
- }
- Node mergedNode = node.createNode(nodeRole, mergedRegion);
- oldNode2mergedNode.put(node, mergedNode);
- for (@NonNull TypedElement typedElement : node.getTypedElements()) {
- mergedNode.addTypedElement(typedElement);
- }
- if (primaryNode != null) {
- oldNode2mergedNode.put(primaryNode, mergedNode);
- for (@NonNull TypedElement typedElement : primaryNode.getTypedElements()) {
- mergedNode.addTypedElement(typedElement);
- }
- }
- return mergedNode;
- }
-
- @Override
- public @NonNull Node visitVariableNode(@NonNull VariableNodeImpl variableNode) {
- NodeRole nodeRole = variableNode.getNodeRole();
- Node primaryNode = secondaryNode2primaryNode.get(variableNode);
- if (primaryNode != null) {
- nodeRole = nodeRole.merge(primaryNode.getNodeRole());
- }
- Node mergedNode = variableNode.createNode(nodeRole, mergedRegion);
- oldNode2mergedNode.put(variableNode, mergedNode);
- for (@NonNull TypedElement typedElement : variableNode.getTypedElements()) {
- mergedNode.addTypedElement(typedElement);
- }
- if (primaryNode != null) {
- oldNode2mergedNode.put(primaryNode, mergedNode);
- for (@NonNull TypedElement typedElement : primaryNode.getTypedElements()) {
- mergedNode.addTypedElement(typedElement);
- }
- }
- return mergedNode;
- }
-}
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionUtil.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionUtil.java
index 06a157673..898d5171e 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionUtil.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvtp2qvts/RegionUtil.java
@@ -365,19 +365,19 @@ public class RegionUtil
}
public static @NonNull Map<@NonNull CompleteClass, @NonNull List<@NonNull Node>> getCompleteClass2Nodes(@NonNull Region region) {
- Map<@NonNull CompleteClass, @NonNull List<@NonNull Node>> completeClass2node = new HashMap<>();
+ Map<@NonNull CompleteClass, @NonNull List<@NonNull Node>> completeClass2nodes = new HashMap<>();
for (@NonNull Node node : region.getNodes()) {
CompleteClass completeClass = node.getCompleteClass();
- List<@NonNull Node> mergedNodes = completeClass2node.get(completeClass);
+ List<@NonNull Node> mergedNodes = completeClass2nodes.get(completeClass);
if (mergedNodes == null) {
mergedNodes = new ArrayList<>();
- completeClass2node.put(completeClass, mergedNodes);
+ completeClass2nodes.put(completeClass, mergedNodes);
}
if (!mergedNodes.contains(node)) {
mergedNodes.add(node);
}
}
- return completeClass2node;
+ return completeClass2nodes;
}
public static Role.@NonNull Phase getOperationNodePhase(@NonNull Region region, @NonNull TypedElement typedElement, @NonNull Node... argNodes) {
@@ -576,4 +576,22 @@ public class RegionUtil
}
throw new UnsupportedOperationException();
}
+
+ public static Node.@NonNull Utility mergeToStrongerUtility(Node.@NonNull Utility nodeUtility1, Node.@NonNull Utility nodeUtility2) {
+ if ((nodeUtility1 == Node.Utility.STRONGLY_MATCHED) || (nodeUtility2 == Node.Utility.STRONGLY_MATCHED)) {
+ return Node.Utility.STRONGLY_MATCHED;
+ }
+ else if ((nodeUtility1 == Node.Utility.WEAKLY_MATCHED) || (nodeUtility2 == Node.Utility.WEAKLY_MATCHED)) {
+ return Node.Utility.WEAKLY_MATCHED;
+ }
+ else if ((nodeUtility1 == Node.Utility.CONDITIONAL) || (nodeUtility2 == Node.Utility.CONDITIONAL)) {
+ return Node.Utility.CONDITIONAL;
+ }
+ else if ((nodeUtility1 == Node.Utility.DEPENDENCY) || (nodeUtility2 == Node.Utility.DEPENDENCY)) {
+ return Node.Utility.DEPENDENCY;
+ }
+ else {
+ return Node.Utility.DEAD;
+ }
+ }
} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EarlyMerger.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EarlyMerger.java
index e7154fa2d..67486f568 100644
--- a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EarlyMerger.java
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EarlyMerger.java
@@ -21,28 +21,53 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Property;
+import org.eclipse.ocl.pivot.Type;
+import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.NameUtil;
-import org.eclipse.qvtd.compiler.internal.qvtp2qvts.BasicMappingRegion;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.MappingRegion;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NavigableEdge;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
-import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NodeRole;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.QVTp2QVTs;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Region;
-import org.eclipse.qvtd.compiler.internal.qvtp2qvts.RegionMerger;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.RegionUtil;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.analysis.ClassDatumAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvts.Region2Depth;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+/**
+ * EarlyMerger replaces one list of MappingRegions by another in which each set of regions that can be merged
+ * without knowledge of the schedule is replaced by an equivalent merged region.
+ */
public class EarlyMerger
{
- public static @Nullable Map<@NonNull Node, @NonNull Node> canMerge(@NonNull Region primaryRegion, @NonNull Region secondaryRegion, @NonNull Region2Depth region2depths, boolean isLateMerge) {
+ /**
+ * Replace those inputRegions that may be merged by merged regions.
+ *
+ * inputRegions should be naturally ordered to ensure that non-recursive dependencies are inherently satisfied.
+ *
+ * Returns the inputRegions after replacement of merges.
+ */
+ public static @NonNull List<@NonNull MappingRegion> merge(@NonNull Iterable<@NonNull MappingRegion> inputRegions) {
+ EarlyMerger earlyMerger = new EarlyMerger(inputRegions);
+ return earlyMerger.merge();
+ }
+
+ protected final @NonNull LinkedHashSet<@NonNull MappingRegion> residualInputRegions;
+ protected final @NonNull List<@NonNull MappingRegion> outputRegions = new ArrayList<>();
+ protected final @NonNull Region2Depth region2depths = new Region2Depth();
+
+ protected EarlyMerger(@NonNull Iterable<@NonNull MappingRegion> inputRegions) {
+ this.residualInputRegions = Sets.newLinkedHashSet(inputRegions);
+ }
+
+ protected @Nullable Map<@NonNull Node, @NonNull Node> canMerge(@NonNull Region primaryRegion, @NonNull Region secondaryRegion, @NonNull Region2Depth region2depths, boolean isLateMerge) {
Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode = null;
Map<@NonNull CompleteClass, @NonNull List<@NonNull Node>> completeClass2nodes = RegionUtil.getCompleteClass2Nodes(primaryRegion);
//
@@ -61,9 +86,6 @@ public class EarlyMerger
return null;
}
secondaryNode2primaryNode = new HashMap<>();
- if ("if".equals(secondaryHeadNode.getName())) {
- secondaryHeadNode.getName();
- }
secondaryNode2primaryNode.put(secondaryHeadNode, primaryHeadNode);
}
if (secondaryNode2primaryNode == null) {
@@ -94,9 +116,6 @@ public class EarlyMerger
for (@NonNull Node primaryNode : primary2secondary.keySet()) {
Node equivalentNode = primary2secondary.get(primaryNode);
assert equivalentNode != null;
- if ("if".equals(equivalentNode.getName())) {
- equivalentNode.getName();
- }
secondaryNode2primaryNode.put(equivalentNode, primaryNode);
}
break;
@@ -108,7 +127,7 @@ public class EarlyMerger
}
return secondaryNode2primaryNode;
}
- private static boolean canMergeInternal(@NonNull Region secondaryRegion, @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode, @NonNull Region2Depth region2depths, boolean isLateMerge) {
+ protected boolean canMergeInternal(@NonNull Region secondaryRegion, @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode, @NonNull Region2Depth region2depths, boolean isLateMerge) {
Set<@NonNull Node> secondaryNodes = new HashSet<>(secondaryNode2primaryNode.keySet());
List<@NonNull Node> secondaryNodesList = new ArrayList<>(secondaryNodes);
for (int i = 0; i < secondaryNodesList.size(); i++) {
@@ -120,12 +139,12 @@ public class EarlyMerger
}
}
for (@NonNull NavigableEdge secondaryEdge : secondarySourceNode.getNavigationEdges()) {
- Node secondaryTargetNode = secondaryEdge.getTarget();
+ Node secondaryTargetNode = RegionUtil.getCastTarget(secondaryEdge.getTarget());
Node primaryTargetNode = null;
if (primarySourceNode != null) {
Edge primaryEdge = primarySourceNode.getNavigationEdge(secondaryEdge.getProperty());
if (primaryEdge != null) {
- primaryTargetNode = primaryEdge.getTarget();
+ primaryTargetNode = RegionUtil.getCastTarget(primaryEdge.getTarget());
// primaryTargetNode = primaryTargetNode.getCastEquivalentNode();
// secondaryTargetNode = secondaryTargetNode.getCastEquivalentNode();
if (primaryTargetNode.getCompleteClass() != secondaryTargetNode.getCompleteClass()) { // FIXME conforms
@@ -149,9 +168,6 @@ public class EarlyMerger
if (primaryTargetNode != null) {
Node primaryTargetNode2 = secondaryNode2primaryNode.get(secondaryTargetNode);
if (primaryTargetNode2 == null) {
- if ("if".equals(secondaryTargetNode.getName())) {
- secondaryTargetNode.getName();
- }
secondaryNode2primaryNode.put(secondaryTargetNode, primaryTargetNode);
}
}
@@ -224,130 +240,64 @@ public class EarlyMerger
}
/**
- * Replace those orderedRegions that may be aggregated as part of a GuardedRegion decision tree by GuardedRegions.
- * orderedRegions should be naturally ordered to ensure that non-recursive dependencies are inherently satisfied.
- *
- * Returns the orderedRegions plus the new aggregates less those aggregated.
- */
- public static @NonNull List<@NonNull MappingRegion> earlyRegionMerge(@NonNull List<@NonNull BasicMappingRegion> orderedRegions) {
- Region2Depth region2depths = new Region2Depth();
- List<@NonNull MappingRegion> outputRegions = new ArrayList<>();
- LinkedHashSet<@NonNull MappingRegion> residualInputRegions = new LinkedHashSet<>(orderedRegions); // order preserving fast random removal
- while (!residualInputRegions.isEmpty()) {
- @NonNull MappingRegion candidateRegion = residualInputRegions.iterator().next();
- boolean isMerged = false;
- if (isEarlyMergePrimaryCandidate(candidateRegion)) {
- List<@NonNull MappingRegion> secondaryRegions = selectSecondaryRegions(candidateRegion);
- if (secondaryRegions != null) {
- MappingRegion mergedRegion = candidateRegion;
- for (@NonNull MappingRegion secondaryRegion : secondaryRegions) {
- assert secondaryRegion != null;
- if (residualInputRegions.contains(secondaryRegion)) {
- Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode = EarlyMerger.canMerge(mergedRegion, secondaryRegion, region2depths, false);
- if (secondaryNode2primaryNode != null) {
- boolean isSharedHead = isSharedHead(mergedRegion, secondaryRegion);
- if (!isSharedHead || (EarlyMerger.canMerge(secondaryRegion, mergedRegion, region2depths, false) != null)) {
- residualInputRegions.remove(mergedRegion);
- residualInputRegions.remove(secondaryRegion);
- mergedRegion = RegionMerger.createMergedRegion(mergedRegion, secondaryRegion, secondaryNode2primaryNode);
- region2depths.addRegion(mergedRegion);
- }
- }
- }
- }
- if (mergedRegion != candidateRegion) {
- // mergedRegion.resolveRecursion();
- if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) {
- mergedRegion.writeDebugGraphs("2-merged");
- }
- // GuardedRegion guardedRegion = createGuardedRegion(mergedRegion, mergeableRegions);
- // outputRegions.add(guardedRegion);
- outputRegions.add(mergedRegion);
- isMerged = true;
- }
- }
- }
- if (!isMerged) {
- outputRegions.add(candidateRegion);
- }
- residualInputRegions.remove(candidateRegion);
- }
- return outputRegions;
- }
-
- /**
- * Return the nodes with region at which a suitably matching head of another region might be merged.
+ * Return the nodes within region at which a suitably matching head of another region might be merged.
* The nodes must be bi-directionally one to one to respect 1:N trace node relationships.
*/
- protected static @NonNull Iterable<@NonNull Node> getMergeableNodes(@NonNull MappingRegion region) {
- Set<@NonNull Node> mergeableNodes = new HashSet<>();
+ protected @NonNull Iterable<@NonNull Node> getHostNodes(@NonNull MappingRegion region) {
+ Set<@NonNull Node> hostNodes = new HashSet<>();
for (@NonNull Node node : region.getHeadNodes()) {
- getMergeableNodes(mergeableNodes, node);
- }
- return mergeableNodes;
- }
- private static void getMergeableNodes(@NonNull Set<@NonNull Node> mergeableNodes, @NonNull Node node) {
- if (isMergeable(node) && mergeableNodes.add(node)) {
- for (@NonNull NavigableEdge edge : node.getNavigationEdges()) {
- if (edge.getOppositeEdge() != null) {
- getMergeableNodes(mergeableNodes, edge.getTarget());
- }
- }
+ getHostNodesAccumulator(hostNodes, node);
}
+ return hostNodes;
}
- private static boolean isMergeable(@NonNull Node node) { // FIXME this is legacy creep
- NodeRole nodeRole = node.getNodeRole();
- if (nodeRole.isRealized() || nodeRole.isSpeculation()) {
- return false;
- }
- if (!node.isClass()) {
- return false;
+
+ protected void getHostNodesAccumulator(@NonNull Set<@NonNull Node> hostNodes, @NonNull Node node) {
+ // if (node.isNew()) {
+ // return;
+ // }
+ if (!node.isClass()) { // Simplify - this obviates many of the below
+ return;
}
if (node.isExplicitNull()) {
- return true;
+ return;
}
if (node.isOperation()) {
- return true;
+ return;
+ }
+ if (!node.isRequired()) {
+ return;
}
if (node.isTrue()) {
- return true;
+ return;
}
- if (node.isPattern()) {
- return true;
+ if (!node.isPattern()) {
+ return;
}
- return false;
- }
-
- /**
- * The primary region in a GuardedRegion must be single-headed. It may be multiply-produced, e.g. recursed.
- */
- private static boolean isEarlyMergePrimaryCandidate(@NonNull Region mappingRegion) {
- List<@NonNull Node> headNodes = mappingRegion.getHeadNodes();
- return headNodes.size() == 1;
- }
-
- /**
- * The secondary region in a GuardedRegion must be single-headed and at least one its head nodes must be a class in use within
- * the primary region. It may be multiply-produced, e.g. recursed.
- */
- private static boolean isEarlyMergeSecondaryCandidate(@NonNull Region primaryRegion,
- @NonNull Region secondaryRegion, @NonNull Set<ClassDatumAnalysis> toOneReachableClasses) {
- List<@NonNull Node> secondaryHeadNodes = secondaryRegion.getHeadNodes();
- if (secondaryHeadNodes.size() == 1) {
- Node classNode = secondaryHeadNodes.get(0);
- ClassDatumAnalysis classDatumAnalysis = classNode.getClassDatumAnalysis();
- if (toOneReachableClasses.contains(classDatumAnalysis)) {
- return true;
+ if (!hostNodes.add(node)) {
+ return;
+ }
+ for (@NonNull NavigableEdge edge : node.getNavigationEdges()) {
+ if (/*!edge.isSecondary() &&*/ edge.isUnconditional()) { // ?? why isSecondary ?? why not isLoaded ??
+ Property property = edge.getProperty();
+ if (edge.isNew()) {
+ if (isToZeroOrOne(property) && isToZeroOrOne(property.getOpposite())) {
+ getHostNodesAccumulator(hostNodes, edge.getTarget());
+ }
+ }
+ else {
+ if (isToOne(property) && isToOne(property.getOpposite())) {
+ getHostNodesAccumulator(hostNodes, edge.getTarget());
+ }
+ }
}
}
- return false;
}
/**
* Return true if the operation nodes for primaryNode and secondaryNode are equivalent
* assigning equivalences to equivalentNodes.
*/
- private static boolean isEquivalent(@NonNull Node primaryNode, @NonNull Node secondaryNode, @NonNull Map<@NonNull Node, @NonNull Node> primary2secondary) {
+ protected boolean isEquivalent(@NonNull Node primaryNode, @NonNull Node secondaryNode, @NonNull Map<@NonNull Node, @NonNull Node> primary2secondary) {
Node node = primary2secondary.get(primaryNode);
if (node != null) {
return node == secondaryNode;
@@ -380,12 +330,37 @@ public class EarlyMerger
}
/**
+ * The primary region of a merge must be single-headed. It may be multiply-produced, e.g. recursed.
+ *
+ * FIXME Is there any need for this restriction.
+ */
+ protected boolean isPrimaryCandidate(@NonNull Region mappingRegion) {
+ List<@NonNull Node> headNodes = mappingRegion.getHeadNodes();
+ return headNodes.size() == 1;
+ }
+
+ /**
+ * The secondary region of a merge must be single-headed and its head node must correspond to
+ * a non-null unconditional class within the primary region. It may be multiply-produced, e.g. recursed.
+ */
+ protected boolean isSecondaryCandidate(@NonNull Region primaryRegion,
+ @NonNull Region secondaryRegion, @NonNull Set<@NonNull ClassDatumAnalysis> toOneReachableClasses) {
+ List<@NonNull Node> secondaryHeadNodes = secondaryRegion.getHeadNodes();
+ if (secondaryHeadNodes.size() != 1) {
+ return false;
+ }
+ Node classNode = secondaryHeadNodes.get(0);
+ ClassDatumAnalysis classDatumAnalysis = classNode.getClassDatumAnalysis();
+ return toOneReachableClasses.contains(classDatumAnalysis);
+ }
+
+ /**
* Return true if any primaryRegion head coincides with a secondaryRegion head.
*/
- private static boolean isSharedHead(@NonNull Region primaryRegion, @NonNull Region secondaryRegion) {
- for (Node primaryHead : primaryRegion.getHeadNodes()) {
+ protected boolean isSharedHead(@NonNull Region primaryRegion, @NonNull Region secondaryRegion) {
+ for (@NonNull Node primaryHead : primaryRegion.getHeadNodes()) {
ClassDatumAnalysis primaryClassDatumAnalysis = primaryHead.getClassDatumAnalysis();
- for (Node secondaryHead : secondaryRegion.getHeadNodes()) {
+ for (@NonNull Node secondaryHead : secondaryRegion.getHeadNodes()) {
if (primaryClassDatumAnalysis == secondaryHead.getClassDatumAnalysis()) {
return true;
}
@@ -394,10 +369,94 @@ public class EarlyMerger
return false;
}
+ private boolean isToOne(@Nullable TypedElement typedElement) {
+ if (typedElement == null) {
+ return false;
+ }
+ if (!typedElement.isIsRequired()) {
+ return false;
+ }
+ Type type = typedElement.getType();
+ if (type instanceof CollectionType) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ private boolean isToZeroOrOne(@Nullable TypedElement typedElement) {
+ if (typedElement == null) {
+ return false;
+ }
+ Type type = typedElement.getType();
+ if (type instanceof CollectionType) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Replace those orderedRegions that may be aggregated as part of a GuardedRegion decision tree by GuardedRegions.
+ * orderedRegions should be naturally ordered to ensure that non-recursive dependencies are inherently satisfied.
+ *
+ * Returns the orderedRegions plus the new aggregates less those aggregated.
+ */
+ protected @NonNull List<@NonNull MappingRegion> merge() {
+ while (!residualInputRegions.isEmpty()) { // Subject to nested removal
+ @NonNull MappingRegion candidateRegion = residualInputRegions.iterator().next();
+ MappingRegion mergedRegion = null;
+ if (isPrimaryCandidate(candidateRegion)) {
+ Iterable<@NonNull MappingRegion> secondaryRegions = selectSecondaryRegions(candidateRegion);
+ if (secondaryRegions != null) {
+ mergedRegion = merge(candidateRegion, secondaryRegions);
+ }
+ }
+ if (mergedRegion == null) {
+ outputRegions.add(candidateRegion);
+ }
+ else {
+ outputRegions.add(mergedRegion);
+ if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) {
+ mergedRegion.writeDebugGraphs("2-merged");
+ }
+ }
+ residualInputRegions.remove(candidateRegion);
+ }
+ return outputRegions;
+ }
+
+ protected @Nullable MappingRegion merge(@NonNull MappingRegion candidateRegion, @NonNull Iterable<@NonNull MappingRegion> secondaryRegions) {
+ MappingRegion primaryRegion = candidateRegion;
+ MappingRegion mergedRegion = null;
+ for (@NonNull MappingRegion secondaryRegion : secondaryRegions) {
+ if (residualInputRegions.contains(secondaryRegion)) {
+ Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode = canMerge(primaryRegion, secondaryRegion, region2depths, false);
+ if (secondaryNode2primaryNode != null) {
+ boolean isSharedHead = isSharedHead(primaryRegion, secondaryRegion);
+ if (!isSharedHead || (canMerge(secondaryRegion, primaryRegion, region2depths, false) != null)) {
+ residualInputRegions.remove(mergedRegion);
+ residualInputRegions.remove(secondaryRegion);
+ RegionMerger regionMerger = new RegionMerger(primaryRegion);
+ regionMerger.addSecondaryRegion(secondaryRegion, secondaryNode2primaryNode);
+ regionMerger.prune();
+ mergedRegion = regionMerger.create();
+ regionMerger.check(mergedRegion);
+ region2depths.addRegion(mergedRegion);
+ primaryRegion = mergedRegion;
+ }
+ }
+ }
+ }
+ return mergedRegion;
+ }
+
/**
* Chose the headNode from a group of peer nodes that has the most non-implicit properties targeting its peers.
*/
- protected static @NonNull Node selectBestHeadNode(@NonNull List<@NonNull Node> headNodes) {
+ protected @NonNull Node selectBestHeadNode(@NonNull List<@NonNull Node> headNodes) {
int size = headNodes.size();
assert size >= 1;
if (size == 1) {
@@ -429,7 +488,7 @@ public class EarlyMerger
return bestHeadNode;
}
- private static @Nullable Node selectMergedHeadNode(@NonNull Node headNode, @NonNull List<@NonNull Node> mergedNodes) {
+ protected @Nullable Node selectMergedHeadNode(@NonNull Node headNode, @NonNull List<@NonNull Node> mergedNodes) {
if (mergedNodes.size() == 1) {
Node mergedNode = selectBestHeadNode(mergedNodes);
if (mergedNode.isIterator()) {
@@ -466,54 +525,38 @@ public class EarlyMerger
/**
* Return a list of single-headed to-one navigable regions whose head is transitively to-one reachable from the primaryRegion's head.
*/
- private static @Nullable List<@NonNull MappingRegion> selectSecondaryRegions(@NonNull MappingRegion primaryRegion) {
+ protected @Nullable Iterable<@NonNull MappingRegion> selectSecondaryRegions(@NonNull MappingRegion primaryRegion) {
//
- // All regions that consume one of the primary nodes.
+ // Find the classes that could be consumed by a secondary region head, and the number
+ // of possible consuming contexts.
//
- Set<@NonNull MappingRegion> allConsumingRegions = new HashSet<>();
- allConsumingRegions.add(primaryRegion);
+ Map<@NonNull ClassDatumAnalysis, @NonNull Integer> hostClass2count = new HashMap<>();
+ for (@NonNull Node hostNode : getHostNodes(primaryRegion)) {
+ ClassDatumAnalysis hostClassDatumAnalysis = hostNode.getClassDatumAnalysis();
+ Integer count = hostClass2count.get(hostClassDatumAnalysis);
+ hostClass2count.put(hostClassDatumAnalysis, count != null ? count+1 : 1);
+ }
//
- // All classes reachable from the primary head.
+ // Find the secondary regions for single possibility host classes.
//
- Set<org.eclipse.qvtd.compiler.internal.qvtp2qvts.analysis.ClassDatumAnalysis> toOneReachableClasses = new HashSet<>();
- List<@NonNull MappingRegion> secondaryRegions = null;
- List<@NonNull MappingRegion> allConsumingRegionsList = new ArrayList<>(allConsumingRegions); // CME-proof iterable List shadowing a mutating Set
- for (int i = 0; i < allConsumingRegionsList.size(); i++) {
- @NonNull MappingRegion secondaryRegion = allConsumingRegionsList.get(i);
- if ((i == 0) || isEarlyMergeSecondaryCandidate(primaryRegion, secondaryRegion, toOneReachableClasses)) {
- if (i > 0) {
- if (secondaryRegions == null) {
- secondaryRegions = new ArrayList<>();
- }
- secondaryRegions.add(secondaryRegion);
- }
- for (@NonNull Node predicatedNode : getMergeableNodes(secondaryRegion)) {
- if (predicatedNode.isClass()) { // Ignore nulls, attributes
- ClassDatumAnalysis predicatedClassDatumAnalysis = predicatedNode.getClassDatumAnalysis();
- if (toOneReachableClasses.add(predicatedClassDatumAnalysis)) {
- for (@NonNull MappingRegion consumingRegion : predicatedClassDatumAnalysis.getConsumingRegions()) {
- if (allConsumingRegions.add(consumingRegion)) {
- allConsumingRegionsList.add(consumingRegion);
- }
- }
- }
- }
- }
- for (@NonNull Node newNode : secondaryRegion.getNewNodes()) {
- if (newNode.isClass()) { // Ignore nulls, attributes
- ClassDatumAnalysis consumingClassDatumAnalysis = newNode.getClassDatumAnalysis();
- if (toOneReachableClasses.add(consumingClassDatumAnalysis)) {
- for (@NonNull MappingRegion consumingRegion : consumingClassDatumAnalysis.getConsumingRegions()) {
- if (allConsumingRegions.add(consumingRegion)) {
- allConsumingRegionsList.add(consumingRegion);
- }
+ Set<@NonNull MappingRegion> secondaryRegions = new HashSet<>();
+ for (Map.Entry<@NonNull ClassDatumAnalysis, @NonNull Integer> entry : hostClass2count.entrySet()) {
+ if (entry.getValue() == 1) {
+ ClassDatumAnalysis primaryClassDatumAnalysis = entry.getKey();
+ for (@NonNull MappingRegion secondaryRegion : primaryClassDatumAnalysis.getConsumingRegions()) {
+ if (secondaryRegion != primaryRegion) {
+ for (@NonNull Node secondaryHeadNode : secondaryRegion.getHeadNodes()) {
+ if (secondaryHeadNode.getClassDatumAnalysis() == primaryClassDatumAnalysis) {
+ secondaryRegions.add(secondaryRegion);
+ break;
}
}
}
}
}
}
- assert allConsumingRegionsList.size() == allConsumingRegions.size(); // Check same changes to CME-proof shadow
- return secondaryRegions;
+ List<@NonNull MappingRegion> sortedSecondaryRegions = new ArrayList<>(secondaryRegions);
+ Collections.sort(sortedSecondaryRegions,NameUtil.NAMEABLE_COMPARATOR);
+ return sortedSecondaryRegions;
}
}
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EdgeMerger.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EdgeMerger.java
new file mode 100644
index 000000000..a6e738ce4
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/EdgeMerger.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * 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.qvts2qvts.merger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.ocl.pivot.Property;
+import org.eclipse.ocl.pivot.utilities.ClassUtil;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.EdgeRole;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NavigableEdge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.RegionUtil;
+
+/**
+ * An EdgeMerger gathers the contributions for an edge in a merged region and supports
+ * optimization of the to be created merged region.
+ */
+class EdgeMerger
+{
+ protected final @NonNull RegionMerger regionMerger;
+ protected final @NonNull NodeMerger mergedSourceNodeMerger;
+ protected final @NonNull NodeMerger mergedTargetNodeMerger;
+ protected final @NonNull List<@NonNull Edge> oldEdges = new ArrayList<>();
+ private @NonNull EdgeRole edgeRole;
+ private @Nullable Edge newEdge = null;
+
+ public EdgeMerger(@NonNull RegionMerger regionMerger, @NonNull Edge oldEdge) {
+ assert !oldEdge.isSecondary();
+ this.regionMerger = regionMerger;
+ mergedSourceNodeMerger = regionMerger.getNodeMerger(oldEdge.getSource());
+ mergedTargetNodeMerger = regionMerger.getNodeMerger(oldEdge.getTarget());
+ oldEdges.add(oldEdge);
+ edgeRole = oldEdge.getEdgeRole();
+ regionMerger.mapOldEdge(oldEdge, this);
+ mergedSourceNodeMerger.addOutgoingEdgeMerger(this, mergedTargetNodeMerger);
+ mergedTargetNodeMerger.addIncomingEdgeMerger(this, mergedSourceNodeMerger);
+ }
+
+ public void addOldEdge(@NonNull Edge oldEdge) {
+ assert !oldEdge.isSecondary();
+ assert !oldEdges.contains(oldEdge);
+ oldEdges.add(oldEdge);
+ edgeRole = RegionUtil.mergeToMoreKnownPhase(edgeRole, oldEdge.getEdgeRole());
+ regionMerger.mapOldEdge(oldEdge, this);
+ }
+
+ public @Nullable Edge createNewEdge(@NonNull Node sourceNodeMerger, @NonNull Node targetNodeMerger) {
+ Edge newEdge2 = newEdge;
+ assert newEdge2 == null;
+ for (@NonNull Edge oldEdge : oldEdges) {
+ newEdge2 = newEdge = oldEdge.createEdge(edgeRole, sourceNodeMerger, targetNodeMerger);
+ break;
+ }
+ if (newEdge2 == null) {
+ return null;
+ }
+ return newEdge2;
+ }
+
+ public void destroy() {
+ mergedSourceNodeMerger.removeOutgoingEdgeMerger(this, mergedTargetNodeMerger);
+ mergedTargetNodeMerger.removeIncomingEdgeMerger(this, mergedSourceNodeMerger);
+ for (@NonNull Edge oldEdge : oldEdges) {
+ regionMerger.unmapOldEdge(oldEdge, this);
+ }
+ }
+
+ public @NonNull Edge getNewEdge() {
+ return ClassUtil.nonNullState(newEdge);
+ }
+
+ public @NonNull Iterable<@NonNull Edge> getOldEdges() {
+ return oldEdges;
+ }
+
+ public @NonNull Node getOldSource() {
+ return oldEdges.get(0).getSource();
+ }
+
+ public @NonNull Node getOldTarget() {
+ return oldEdges.get(0).getTarget();
+ }
+
+ public @NonNull NodeMerger getSource() {
+ return mergedSourceNodeMerger;
+ }
+
+ public @NonNull NodeMerger getTarget() {
+ return mergedTargetNodeMerger;
+ }
+
+ private boolean isCast() {
+ return oldEdges.get(0).isCast();
+ }
+
+ public boolean isFoldable() {
+ return isCast() && mergedTargetNodeMerger.isNew() && !mergedSourceNodeMerger.isNew();
+ }
+
+ /**
+ * Return an oldEdge that is the same as newEdge or null if none.
+ */
+ public @Nullable Edge sameEdge(@NonNull Edge newEdge) {
+ if (newEdge instanceof NavigableEdge) {
+ Property newProperty = ((NavigableEdge)newEdge).getProperty();
+ for (@NonNull Edge oldEdge : oldEdges) {
+ if (oldEdge instanceof NavigableEdge) {
+ Property oldProperty = ((NavigableEdge)oldEdge).getProperty();
+ if (oldProperty == newProperty) {
+ return oldEdge;
+ }
+ }
+ }
+ }
+ else {
+ Class<? extends @NonNull Edge> newClass = newEdge.getClass();
+ for (@NonNull Edge oldEdge : oldEdges) {
+ Class<? extends @NonNull Edge> oldClass = oldEdge.getClass();
+ if (oldClass == newClass) {
+ return oldEdge;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return newEdge != null? newEdge.toString() : oldEdges.get(0).toString();
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/NodeMerger.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/NodeMerger.java
new file mode 100644
index 000000000..7481172a8
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/NodeMerger.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * 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.qvts2qvts.merger;
+
+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.ocl.pivot.TypedElement;
+import org.eclipse.ocl.pivot.utilities.ClassUtil;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.MappingRegion;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NodeRole;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.RegionUtil;
+
+/**
+ * A NodeMerger gathers the contributions for a node in a merged region and supports
+ * optimization of the to be created merged region.
+ */
+class NodeMerger
+{
+ protected final @NonNull RegionMerger regionMerger;
+ protected final @NonNull List<@NonNull Node> oldNodes = new ArrayList<>();
+ private @NonNull NodeRole nodeRole;
+ private Node.@NonNull Utility nodeUtility;
+ private @NonNull Map<@NonNull NodeMerger, @NonNull List<@NonNull EdgeMerger>> sourceNodeMerger2edgeMergers = new HashMap<>();
+ private @NonNull Map<@NonNull NodeMerger, @NonNull List<@NonNull EdgeMerger>> targetNodeMerger2edgeMergers = new HashMap<>();
+ private @Nullable Node newNode = null;
+
+ public NodeMerger(@NonNull RegionMerger regionMerger, @NonNull Node oldNode) {
+ this.regionMerger = regionMerger;
+ oldNodes.add(oldNode);
+ nodeRole = oldNode.getNodeRole();
+ nodeUtility = oldNode.getUtility();
+ regionMerger.mapOldNode(oldNode, this);
+ }
+
+ public void addIncomingEdgeMerger(@NonNull EdgeMerger edgeMerger, @NonNull NodeMerger sourceNodeMerger) {
+ List<@NonNull EdgeMerger> edgeMergers = getIncomingEdgeMergers(sourceNodeMerger);
+ assert !edgeMergers.contains(edgeMerger);
+ edgeMergers.add(edgeMerger);
+ }
+
+ public void addOutgoingEdgeMerger(@NonNull EdgeMerger edgeMerger, @NonNull NodeMerger targetNodeMerger) {
+ List<@NonNull EdgeMerger> edgeMergers = getOutgoingEdgeMergers(targetNodeMerger);
+ assert !edgeMergers.contains(edgeMerger);
+ edgeMergers.add(edgeMerger);
+ }
+
+ public void addOldNode(@NonNull Node oldNode) {
+ assert !oldNodes.contains(oldNode);
+ oldNodes.add(oldNode);
+ nodeRole = RegionUtil.mergeToMoreKnownPhase(nodeRole, oldNode.getNodeRole());
+ nodeUtility = RegionUtil.mergeToStrongerUtility(nodeUtility, oldNode.getUtility());
+ regionMerger.mapOldNode(oldNode, this);
+ }
+
+ public @Nullable Node createNewNode(@NonNull MappingRegion newRegion) {
+ Node newNode2 = newNode;
+ assert newNode2 == null;
+ for (@NonNull Node oldNode : oldNodes) {
+ newNode2 = newNode = oldNode.createNode(nodeRole, newRegion);
+ newNode2.setUtility(nodeUtility);
+ break;
+ }
+ if (newNode2 == null) {
+ return null;
+ }
+ for (@NonNull Node oldNode : oldNodes) {
+ // oldNode2mergedNode.put(oldNode, mergedNode);
+ for (@NonNull TypedElement typedElement : oldNode.getTypedElements()) {
+ newNode2.addTypedElement(typedElement);
+ }
+ }
+ return newNode2;
+ }
+
+ public void destroy() {
+ for (@NonNull List<@NonNull EdgeMerger> incomingEdgeMergers : sourceNodeMerger2edgeMergers.values()) {
+ for (int i = incomingEdgeMergers.size(); --i >= 0; ) { // Down count to avoid CME
+ incomingEdgeMergers.get(i).destroy();
+ }
+ }
+ for (@NonNull List<@NonNull EdgeMerger> outgoingEdgeMergers : targetNodeMerger2edgeMergers.values()) {
+ for (int i = outgoingEdgeMergers.size(); --i >= 0; ) { // Down count to avoid CME
+ outgoingEdgeMergers.get(i).destroy();
+ }
+ }
+ for (@NonNull Node oldNode : oldNodes) {
+ regionMerger.unmapOldNode(oldNode, this);
+ }
+ }
+
+ public void gatherFoldableEdges(@NonNull List<@NonNull EdgeMerger> foldableEdgeMergers) {
+ for (@NonNull List<@NonNull EdgeMerger> outgoingEdgeMergers : targetNodeMerger2edgeMergers.values()) {
+ for (@NonNull EdgeMerger edgeMerger : outgoingEdgeMergers) {
+ if (edgeMerger.isFoldable()) {
+ foldableEdgeMergers.add(edgeMerger);
+ }
+ }
+ }
+ }
+
+ public @NonNull List<@NonNull EdgeMerger> getIncomingEdgeMergers(@NonNull NodeMerger sourceNodeMerger) {
+ List<@NonNull EdgeMerger> edgeMergers = sourceNodeMerger2edgeMergers.get(sourceNodeMerger);
+ if (edgeMergers == null) {
+ edgeMergers = new ArrayList<>();
+ sourceNodeMerger2edgeMergers.put(sourceNodeMerger, edgeMergers);
+ }
+ return edgeMergers;
+ }
+
+ public @NonNull Node getNewNode() {
+ return ClassUtil.nonNullState(newNode);
+ }
+
+ public @NonNull Iterable<@NonNull Node> getOldNodes() {
+ return oldNodes;
+ }
+
+ public @NonNull List<@NonNull EdgeMerger> getOutgoingEdgeMergers(@NonNull NodeMerger targetNodeMerger) {
+ List<@NonNull EdgeMerger> edgeMergers = targetNodeMerger2edgeMergers.get(targetNodeMerger);
+ if (edgeMergers == null) {
+ edgeMergers = new ArrayList<>();
+ targetNodeMerger2edgeMergers.put(targetNodeMerger, edgeMergers);
+ }
+ return edgeMergers;
+ }
+
+ public boolean isNew() {
+ return nodeRole.isNew();
+ }
+
+ public void removeIncomingEdgeMerger(@NonNull EdgeMerger edgeMerger, @NonNull NodeMerger sourceNodeMerger) {
+ List<@NonNull EdgeMerger> edgeMergers = getIncomingEdgeMergers(sourceNodeMerger);
+ boolean wasRemoved = edgeMergers.remove(edgeMerger);
+ assert wasRemoved;
+ }
+
+ public void removeOutgoingEdgeMerger(@NonNull EdgeMerger edgeMerger, @NonNull NodeMerger targetNodeMerger) {
+ List<@NonNull EdgeMerger> edgeMergers = getOutgoingEdgeMergers(targetNodeMerger);
+ boolean wasRemoved = edgeMergers.remove(edgeMerger);
+ assert wasRemoved;
+ }
+
+ @Override
+ public @NonNull String toString() {
+ return newNode != null? newNode.toString() : oldNodes.get(0).toString();
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/RegionMerger.java b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/RegionMerger.java
new file mode 100644
index 000000000..e922f628f
--- /dev/null
+++ b/plugins/org.eclipse.qvtd.compiler/src/org/eclipse/qvtd/compiler/internal/qvts2qvts/merger/RegionMerger.java
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ * 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.qvts2qvts.merger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.utilities.ClassUtil;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.MappingRegion;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.MicroMappingRegion;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NamedMappingRegion;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
+import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Region;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * RegionMerger provides the ability to merge one or more secondary regions into a primary region to
+ * create a new merged region.
+ *
+ * RegionMerger is used by classes such as EarlyMerger and LateMerger that orchestrate what may be merged safely.
+ */
+class RegionMerger // implements Region
+{
+ /**
+ * The primary original region contributing to the merged region.
+ */
+ protected final @NonNull MappingRegion primaryRegion;
+
+ /**
+ * Additional secondary region contributing to the merged region.
+ */
+ protected final @NonNull List<@NonNull MappingRegion> secondaryRegions = new ArrayList<>();
+
+ /**
+ * Nodes to be merged from a secondary region mapped to a corresponding primary region node.
+ */
+ protected final @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode = new HashMap<>();
+
+ /**
+ * Mapping from each primary and secondary region node to the corresponding merged node.
+ */
+ protected final @NonNull Map<@NonNull Node, @NonNull NodeMerger> oldNode2nodeMerger = new HashMap<>();
+
+ /**
+ * Mapping from each primary and secondary region edge to the corresponding merged edge.
+ */
+ private final @NonNull Map<@NonNull Edge, @NonNull EdgeMerger> oldEdge2edgeMerger = new HashMap<>();
+
+ /**
+ * The original edges that are not represented in the result.
+ */
+ private /*@LazyNonNull*/ Set<@NonNull Edge> debugPrunedEdges = null;
+
+ protected RegionMerger(@NonNull MappingRegion primaryRegion) {
+ this.primaryRegion = primaryRegion;
+ assert !(primaryRegion instanceof MicroMappingRegion);
+ //
+ for (@NonNull Node primaryNode : primaryRegion.getNodes()) {
+ new NodeMerger(this, primaryNode);
+ }
+ //
+ for (@NonNull Edge primaryEdge : primaryRegion.getEdges()) {
+ if (!primaryEdge.isSecondary()) {
+ new EdgeMerger(this, primaryEdge);
+ }
+ }
+ }
+
+ private void addPrunedEdge(@NonNull Edge prunedEdge) {
+ Set<@NonNull Edge> debugPrunedEdges2 = debugPrunedEdges;
+ if (debugPrunedEdges2 == null) {
+ debugPrunedEdges2 = debugPrunedEdges = new HashSet<>();
+ }
+ boolean wasAdded = debugPrunedEdges2.add(prunedEdge);
+ assert wasAdded;
+ }
+
+ protected void addSecondaryEdge(@NonNull Edge secondaryEdge) {
+ NodeMerger sourceNodeMerger = getNodeMerger(secondaryEdge.getSource());
+ NodeMerger targetNodeMerger = getNodeMerger(secondaryEdge.getTarget());
+ if (sourceNodeMerger != targetNodeMerger) {
+ boolean isMerged = false;
+ for (@NonNull EdgeMerger edgeMerger : sourceNodeMerger.getOutgoingEdgeMergers(targetNodeMerger)) {
+ if (edgeMerger.sameEdge(secondaryEdge) != null) {
+ edgeMerger.addOldEdge(secondaryEdge);
+ isMerged = true;
+ break;
+ }
+ }
+ if (!isMerged) {
+ new EdgeMerger(this, secondaryEdge);
+ }
+ }
+ else {
+ addPrunedEdge(secondaryEdge);
+ }
+ }
+
+ public void addSecondaryRegion(@NonNull MappingRegion secondaryRegion, @NonNull Map<@NonNull Node, @NonNull Node> secondaryNode2primaryNode) {
+ assert !(secondaryRegion instanceof MicroMappingRegion);
+ assert !secondaryRegions.contains(secondaryRegion);
+ secondaryRegions.add(secondaryRegion);
+ this.secondaryNode2primaryNode.putAll(secondaryNode2primaryNode);
+ //
+ for (@NonNull Node secondaryNode : secondaryRegion.getNodes()) {
+ Node primaryNode = secondaryNode2primaryNode.get(secondaryNode);
+ if (primaryNode != null) {
+ NodeMerger nodeMerger = oldNode2nodeMerger.get(primaryNode);
+ assert nodeMerger != null;
+ nodeMerger.addOldNode(secondaryNode);
+ }
+ else {
+ new NodeMerger(this, secondaryNode);
+ }
+ }
+ //
+ for (@NonNull Edge secondaryEdge : secondaryRegion.getEdges()) {
+ if (!secondaryEdge.isSecondary()) {
+ addSecondaryEdge(secondaryEdge);
+ }
+ }
+ }
+
+ public void check(@NonNull MappingRegion newRegion) {
+ checkNodes(newRegion, primaryRegion);
+ for (@NonNull Region secondaryRegion : secondaryRegions) {
+ checkNodes(newRegion, secondaryRegion);
+ }
+ checkEdges(newRegion, primaryRegion);
+ for (@NonNull Region secondaryRegion : secondaryRegions) {
+ checkEdges(newRegion, secondaryRegion);
+ }
+ }
+
+ protected void checkEdges(@NonNull MappingRegion newRegion, @NonNull Region oldRegion) {
+ for (@NonNull Edge oldEdge : oldRegion.getEdges()) {
+ assert oldEdge.getRegion() == oldRegion;
+ if (!oldEdge.isRecursion() && !oldEdge.isSecondary()) { // FIXME Remove this irregularity
+ EdgeMerger edgeMerger = oldEdge2edgeMerger.get(oldEdge);
+ if (edgeMerger != null) {
+ assert Iterables.contains(edgeMerger.getOldEdges(), oldEdge);
+ assert edgeMerger.getNewEdge().getRegion() == newRegion;
+ }
+ else {
+ assert debugPrunedEdges.contains(oldEdge);
+ }
+ }
+ }
+ }
+
+ protected void checkNodes(@NonNull MappingRegion newRegion, @NonNull Region oldRegion) {
+ for (@NonNull Node oldNode : oldRegion.getNodes()) {
+ assert oldNode.getRegion() == oldRegion;
+ Node nodeMerger = getNodeMerger(oldNode).getNewNode();
+ assert nodeMerger.getRegion() == newRegion;
+ }
+ }
+
+ public @NonNull MappingRegion create() {
+ @NonNull String newName = createNewName();
+ MappingRegion newRegion = createNewRegion(newName);
+ createNewNodes(newRegion);
+ createNewEdges();
+ return newRegion;
+ }
+
+ protected @NonNull String createNewName() {
+ List<@NonNull String> names = new ArrayList<>();
+ names.add(primaryRegion.getName());
+ for (@NonNull MappingRegion secondaryRegion : secondaryRegions) {
+ names.add(secondaryRegion.getName());
+ }
+ Collections.sort(names);
+ StringBuilder s = new StringBuilder();
+ for (@NonNull String name : names) {
+ if (s.length() > 0) {
+ s.append("\\n");
+ }
+ s.append(name);
+ }
+ return s.toString();
+ }
+
+ protected void createNewEdges() {
+ for (@NonNull EdgeMerger edgeMerger : new HashSet<>(oldEdge2edgeMerger.values())) {
+ Node sourceNodeMerger = getNodeMerger(edgeMerger.getOldSource()).getNewNode();
+ Node targetNodeMerger = getNodeMerger(edgeMerger.getOldTarget()).getNewNode();
+ edgeMerger.createNewEdge(sourceNodeMerger, targetNodeMerger);
+ }
+ }
+
+ protected void createNewNodes(@NonNull MappingRegion newRegion) {
+ for (@NonNull NodeMerger nodeMerger : new HashSet<>(oldNode2nodeMerger.values())) {
+ nodeMerger.createNewNode(newRegion);
+ }
+ }
+
+ protected @NonNull MappingRegion createNewRegion(@NonNull String newName) {
+ return new NamedMappingRegion(primaryRegion.getMultiRegion(), newName);
+ }
+
+ protected @NonNull EdgeMerger getEdgeMerger(@NonNull Edge oldEdge) {
+ return ClassUtil.nonNullState(oldEdge2edgeMerger.get(oldEdge));
+ }
+
+ protected @NonNull NodeMerger getNodeMerger(@NonNull Node oldNode) {
+ return ClassUtil.nonNullState(oldNode2nodeMerger.get(oldNode));
+ }
+
+ protected void mapOldEdge(@NonNull Edge oldEdge, @NonNull EdgeMerger edgeMerger) {
+ EdgeMerger oldMergedEdge = oldEdge2edgeMerger.put(oldEdge, edgeMerger);
+ assert oldMergedEdge == null;
+ }
+
+ protected void mapOldNode(@NonNull Node oldNode, @NonNull NodeMerger nodeMerger) {
+ NodeMerger oldMergedNode = oldNode2nodeMerger.put(oldNode, nodeMerger);
+ assert oldMergedNode == null;
+ }
+
+ public void prune() {
+ List<@NonNull EdgeMerger> foldableEdgeMergers = new ArrayList<>();
+ for (@NonNull NodeMerger nodeMerger : new HashSet<>(oldNode2nodeMerger.values())) {
+ nodeMerger.gatherFoldableEdges(foldableEdgeMergers);
+ }
+ for (@NonNull EdgeMerger foldableEdgeMerger : foldableEdgeMergers) {
+ NodeMerger sourceNodeMerger = foldableEdgeMerger.getSource();
+ NodeMerger targetNodeMerger = foldableEdgeMerger.getTarget();
+ sourceNodeMerger.destroy();
+ for (@NonNull Node oldNode : sourceNodeMerger.getOldNodes()) {
+ targetNodeMerger.addOldNode(oldNode);
+ for (@NonNull Edge oldEdge : oldNode.getIncomingEdges()) {
+ addSecondaryEdge(oldEdge);
+ }
+ for (@NonNull Edge oldEdge : oldNode.getOutgoingEdges()) {
+ addSecondaryEdge(oldEdge);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return createNewName();
+ }
+
+ protected void unmapOldEdge(@NonNull Edge oldEdge, @NonNull EdgeMerger edgeMerger) {
+ EdgeMerger oldEdgeMerger = oldEdge2edgeMerger.remove(oldEdge);
+ assert oldEdgeMerger == edgeMerger;
+ }
+
+ protected void unmapOldNode(@NonNull Node oldNode, @NonNull NodeMerger nodeMerger) {
+ NodeMerger oldNodeMerger = oldNode2nodeMerger.remove(oldNode);
+ assert oldNodeMerger == nodeMerger;
+ }
+}
diff --git a/plugins/org.eclipse.qvtd.pivot.qvtbase/annotations/com/google/common/collect/Sets.eea b/plugins/org.eclipse.qvtd.pivot.qvtbase/annotations/com/google/common/collect/Sets.eea
index cf083f768..a93c33d6a 100644
--- a/plugins/org.eclipse.qvtd.pivot.qvtbase/annotations/com/google/common/collect/Sets.eea
+++ b/plugins/org.eclipse.qvtd.pivot.qvtbase/annotations/com/google/common/collect/Sets.eea
@@ -5,3 +5,6 @@ newHashSet
newHashSet
<E:Ljava/lang/Object;>([TE;)Ljava/util/HashSet<TE;>;
<E:Ljava/lang/Object;>([TE;)L1java/util/HashSet<TE;>;
+newLinkedHashSet
+ <E:Ljava/lang/Object;>(Ljava/lang/Iterable<+TE;>;)Ljava/util/LinkedHashSet<TE;>;
+ <E:Ljava/lang/Object;>(Ljava/lang/Iterable<+TE;>;)L1java/util/LinkedHashSet<TE;>;

Back to the top