blob: 5fef65d04664b9e933d8bf33042717d3f1b41414 [file] [log] [blame]
/*******************************************************************************
* 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.partitioner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.qvtd.compiler.CompilerProblem;
import org.eclipse.qvtd.compiler.ProblemHandler;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Edge;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.EdgeRole;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.MappingRegion;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.MicroMappingRegion;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.NavigableEdge;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Node;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.QVTp2QVTs;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.Region;
import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
public class Partitioner
{
/* private static @NonNull Iterable<@NonNull NavigationEdge> getNavigableEdges(@NonNull Iterable<@NonNull NavigationEdge> edges) {
List<@NonNull NavigationEdge> navigableEdges = new ArrayList<>();
for (@NonNull NavigationEdge edge : edges) { // cf NavigationForestBuilder addEdge
if (edge.isSecondary()) {}
else if (edge.isRealized()) {
if (edge.getTarget().isLoaded() && edge.getSource().getClassDatumAnalysis().getDomainUsage().isMiddle()) {
navigableEdges.add(edge);
}
}
else if (!edge.isNavigable()) {}
else if (edge.isCast()) {}
else {
assert !edge.isExpression();
assert !edge.isComputation();
Node targetNode = edge.getTarget();
if (!targetNode.isNull()) {
navigableEdges.add(edge);
}
}
}
return navigableEdges;
} */
/* private static @NonNull Iterable<@NonNull Node> getTraceNodes(@NonNull Iterable<@NonNull Node> nodes) {
List<@NonNull Node> traceNodes = new ArrayList<>();
for (@NonNull Node node : nodes) {
if (node.getClassDatumAnalysis().getDomainUsage().isMiddle()) {
if (node.isRealized()) {
traceNodes.add(node);
}
}
}
return traceNodes;
} */
public static @NonNull Iterable<@NonNull MappingRegion> partition(@NonNull ProblemHandler problemHandler, @NonNull Iterable<@NonNull ? extends Region> activeRegions) {
Set<@NonNull Property> corrolaryProperties = new HashSet<>();
for (@NonNull Region region : activeRegions) {
if (region instanceof MappingRegion) {
gatherCorrolaries(corrolaryProperties, (MappingRegion)region);
}
}
List<@NonNull MappingRegion> partitionedRegions = new ArrayList<>();
for (@NonNull Region region : activeRegions) {
if (region instanceof MappingRegion) {
Partitioner partitioner = new Partitioner(problemHandler, (MappingRegion)region, corrolaryProperties);
Iterables.addAll(partitionedRegions, partitioner.partition());
}
}
return partitionedRegions;
}
private static void gatherCorrolaries(@NonNull Set<@NonNull Property> corrolaryProperties, @NonNull MappingRegion region) {
for (@NonNull Node node : region.getNodes()) {
if (!node.isTrue() && node.isPattern() && node.isRealized() && node.getClassDatumAnalysis().getDomainUsage().isMiddle()) {
for (@NonNull NavigableEdge edge : node.getNavigationEdges()) {
if (edge.isRealized() && edge.getTarget().isRealized()) {
corrolaryProperties.add(edge.getProperty());
}
}
}
}
}
protected final @NonNull ProblemHandler problemHandler;
protected final @NonNull MappingRegion region;
/**
* properties that are directly realized from a middle object provided all predicates are satisfied.
*/
protected final @NonNull Set<@NonNull Property> corrolaryProperties;
private final @NonNull List<@NonNull Edge> predicatedEdges = new ArrayList<>();
private final @NonNull List<@NonNull Node> predicatedMiddleNodes = new ArrayList<>();
private final @NonNull List<@NonNull Node> predicatedOutputNodes = new ArrayList<>();
private final @NonNull List<@NonNull Node> realizedMiddleNodes = new ArrayList<>();
private final @NonNull List<@NonNull Node> realizedOutputNodes = new ArrayList<>();
private final @NonNull Set<@NonNull NavigableEdge> navigableEdges = new HashSet<>();
private final @NonNull Set<@NonNull Edge> realizedEdges = new HashSet<>();
private final @NonNull List<@NonNull Edge> realizedOutputEdges = new ArrayList<>();
private final @NonNull List<@NonNull Node> trueNodes = new ArrayList<>();
private boolean hasLoadedNodes = false;
/**
* Dynamically growing list of edges that have been predicated by a partition.
*/
private final @NonNull Set<@NonNull Edge> alreadyPredicatedEdges = new HashSet<>();
/**
* Dynamically growing list of nodes that have been predicated by a partition.
*/
private final @NonNull Set<@NonNull Node> alreadyPredicatedNodes = new HashSet<>();
/**
* Dynamically growing list of edges that have been realized by a partition.
*/
private final @NonNull Set<@NonNull Edge> alreadyRealizedEdges = new HashSet<>();
/**
* Dynamically growing list of nodes that have been realized by a partition.
*/
private final @NonNull Set<@NonNull Node> alreadyRealizedNodes = new HashSet<>();
/**
* Dynamically growing list of true nodes that have been realized by a partition.
*/
private final @NonNull Set<@NonNull Node> alreadyTrueNodes = new HashSet<>();
private final @NonNull Map<@NonNull Edge, @NonNull List<@NonNull AbstractPartition>> debugEdge2partitions = new HashMap<>();
public Partitioner(@NonNull ProblemHandler problemHandler, @NonNull MappingRegion region, @NonNull Set<@NonNull Property> corrolaryProperties) {
// super(getTraceNodes(region.getNodes()), getNavigableEdges(region.getNavigationEdges()));
this.problemHandler = problemHandler;
this.region = region;
this.corrolaryProperties = corrolaryProperties;
analyzeNodes();
analyzeEdges();
}
public void addEdge(@NonNull Edge edge, @NonNull EdgeRole newEdgeRole, @NonNull AbstractPartition partition) {
if (newEdgeRole.isPredicated()) {
alreadyPredicatedEdges.add(edge);
}
else if (newEdgeRole.isRealized()) {
alreadyRealizedEdges.add(edge);
}
List<@NonNull AbstractPartition> partitions = debugEdge2partitions.get(edge);// TODO Auto-generated method stub
if (partitions == null) {
partitions = new ArrayList<>();
debugEdge2partitions.put(edge, partitions);
}
assert !partitions.contains(partition);
partitions.add(partition);
}
public boolean addPredicatedNode(@NonNull Node node) {
return alreadyPredicatedNodes.add(node);
}
public void addProblem(@NonNull CompilerProblem problem) {
problemHandler.addProblem(problem);
}
public boolean addRealizedNode(@NonNull Node node) {
return alreadyRealizedNodes.add(node);
}
public boolean addTrueNode(@NonNull Node node) {
return alreadyTrueNodes.add(node);
}
private void analyzeEdges() {
for (@NonNull Edge edge : region.getEdges()) {
if (!edge.isSecondary()) {
if (edge.isPredicated()) {
predicatedEdges.add(edge);
}
if (edge.isNavigation()) {
if (edge.isRealized()) {
realizedEdges.add(edge);
Node sourceNode = edge.getSource();
if (!realizedMiddleNodes.contains(sourceNode) && (sourceNode.isPredicated() || sourceNode.isRealized())) {
Node targetNode = edge.getTarget();
if (!realizedMiddleNodes.contains(targetNode) && (targetNode.isPredicated() || targetNode.isRealized())) {
realizedOutputEdges.add(edge);
}
}
if (edge.getTarget().isLoaded() && edge.getSource().getClassDatumAnalysis().getDomainUsage().isMiddle()) {
// navigableEdges.add(navigationEdge);
}
}
else if (edge.isMatched() && !edge.isCast()) {
assert !edge.isExpression();
assert !edge.isComputation();
Node targetNode = edge.getTarget();
if (!targetNode.isExplicitNull()) {
// navigableEdges.add(navigationEdge);
}
}
}
/* else if (RegionUtil.isRealizedIncludes(edge)) {
realizedEdges.add(edge);
Node sourceNode = edge.getSource();
if (!realizedMiddleNodes.contains(sourceNode) && (sourceNode.isPredicated() || sourceNode.isRealized())) {
Node targetNode = edge.getTarget();
if (!realizedMiddleNodes.contains(targetNode) && (targetNode.isPredicated() || targetNode.isRealized())) {
realizedOutputEdges.add(edge);
}
}
if (edge.getTarget().isLoaded() && edge.getSource().getClassDatumAnalysis().getDomainUsage().isMiddle()) {
// navigableEdges.add(navigationEdge);
}
} */
}
}
for (@NonNull NavigableEdge edge : region.getNavigationEdges()) {
if (!edge.isSecondary() && !edge.isRealized()) {
navigableEdges.add(edge);
}
}
}
private void analyzeNodes() {
for (@NonNull Node node : region.getNodes()) {
if (node.isTrue()) {
trueNodes.add(node);
}
else if (node.isPattern()) {
if (node.isConstant()) {
}
else if (node.isLoaded()) {
hasLoadedNodes = true;
}
else if (node.getClassDatumAnalysis().getDomainUsage().isMiddle()) {
if (node.isPredicated()) {
predicatedMiddleNodes.add(node);
}
else if (node.isRealized()) {
realizedMiddleNodes.add(node);
// for (@NonNull NavigationEdge edge : node.getNavigationEdges()) {
// Node targetNode = edge.getTarget();
// NodeRole targetNodeRole = targetNode.getNodeRole();
// if (!targetNodeRole.isPredicated() && !targetNodeRole.isRealized()) {
// tracedInputNodes.add(targetNode);
// }
// }
}
else if (!node.isExplicitNull()) {
throw new IllegalStateException("middle node must be predicated or realized : " + node);
}
}
else {
if (!node.isOperation()) {
if (node.isPredicated()) {
predicatedOutputNodes.add(node);
}
else if (node.isRealized()) {
realizedOutputNodes.add(node);
}
}
}
}
}
}
private void check() {
for (@NonNull Node node : region.getNodes()) {
if ((node.isSpeculated() || node.isRealized()) && !hasRealizedNode(node)) {
problemHandler.addProblem(region.createError("Should have realized " + node));
}
}
Set<@NonNull Edge> allPrimaryEdges = new HashSet<>();
for (@NonNull Edge edge : region.getEdges()) {
if (!edge.isSecondary()) {
allPrimaryEdges.add(edge);
if (edge.isRealized() && !hasRealizedEdge(edge)) {
problemHandler.addProblem(region.createError("Should have realized " + edge));
}
}
}
//
Set<@NonNull Node> deadNodes = computeDeadNodes(region.getNodes());
Set<@NonNull Edge> deadEdges = computeDeadEdges(deadNodes);
allPrimaryEdges.removeAll(deadEdges);
Set<@NonNull Edge> partitionedEdges = new HashSet<>(debugEdge2partitions.keySet());
if (!partitionedEdges.equals(allPrimaryEdges)) {
Set<@NonNull Edge> extraEdgesSet = Sets.newHashSet(partitionedEdges);
CompilerUtil.removeAll(extraEdgesSet, allPrimaryEdges);
for (@NonNull Edge edge : extraEdgesSet) {
problemHandler.addProblem(region.createWarning("Extra " + edge));
}
Set<@NonNull Edge> missingEdgesSet = Sets.newHashSet(allPrimaryEdges);
missingEdgesSet.removeAll(partitionedEdges);
for (@NonNull Edge edge : missingEdgesSet) {
if (!isCorrolary(edge)) {// && !isDead(edge)) {
problemHandler.addProblem(region.createWarning("Missing " + edge));
}
}
}
}
private @NonNull Set<@NonNull Edge> computeDeadEdges(@NonNull Iterable<@NonNull Node> deadNodes) {
Set<@NonNull Edge> deadEdges = new HashSet<>();
for (@NonNull Node node : deadNodes) {
deadEdges.addAll(node.getIncomingEdges());
deadEdges.addAll(node.getOutgoingEdges());
}
return deadEdges;
}
private @NonNull Set<@NonNull Node> computeDeadNodes(@NonNull Iterable<@NonNull Node> nodes) {
Set<@NonNull Node> deadNodes = new HashSet<>();
Set<@NonNull Node> moreDeadNodes = null;
for (@NonNull Node node : nodes) {
if (!node.isHead() && isDead(node, null)) {
if (moreDeadNodes == null) {
moreDeadNodes = new HashSet<>();
}
moreDeadNodes.add(node);
}
}
if (moreDeadNodes == null) {
return deadNodes;
}
while (moreDeadNodes.size() > 0) {
deadNodes.addAll(moreDeadNodes);
List<@NonNull Node> moreDeadNodesList = new ArrayList<>(moreDeadNodes);
moreDeadNodes = null;
for (@NonNull Node deadNode : moreDeadNodesList) {
for (@NonNull Edge edge : deadNode.getIncomingEdges()) {
Node sourceNode = edge.getSource();
if (!sourceNode.isHead() && isDead(sourceNode, deadNodes)) {
if (moreDeadNodes == null) {
moreDeadNodes = new HashSet<>();
}
moreDeadNodes.add(sourceNode);
}
}
}
if (moreDeadNodes == null) {
break;
}
}
return deadNodes;
}
private @NonNull MicroMappingRegion createAssignmentRegion(@NonNull Edge outputEdge, int i) {
AssignmentPartition realizedPartition = new AssignmentPartition(this, outputEdge);
MicroMappingRegion microMappingRegion = realizedPartition.createMicroMappingRegion("«edge" + i + "»", "_p" + i);
if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) {
microMappingRegion.writeDebugGraphs(null);
}
return microMappingRegion;
}
private @NonNull MicroMappingRegion createRealizedRegion() {
RealizedPartition realizedPartition = new RealizedPartition(this);
MicroMappingRegion microMappingRegion = realizedPartition.createMicroMappingRegion("«realized»", "_r0");
if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) {
microMappingRegion.writeDebugGraphs(null);
}
return microMappingRegion;
}
private @NonNull MicroMappingRegion createSpeculatedRegion() {
SpeculatedPartition speculatedPartition = new SpeculatedPartition(this);
MicroMappingRegion microMappingRegion = speculatedPartition.createMicroMappingRegion("«speculated»", "_p1");
if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) {
microMappingRegion.writeDebugGraphs(null);
}
return microMappingRegion;
}
private @NonNull MicroMappingRegion createSpeculationRegion() {
SpeculationPartition speculationPartition = new SpeculationPartition(this);
MicroMappingRegion microMappingRegion = speculationPartition.createMicroMappingRegion("«speculation»", "_p0");
if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) {
microMappingRegion.writeDebugGraphs(null);
}
return microMappingRegion;
}
public @NonNull Iterable<@NonNull Edge> getAlreadyPredicatedEdges() {
return alreadyPredicatedEdges;
}
public @NonNull Iterable<@NonNull Edge> getAlreadyRealizedEdges() {
return alreadyRealizedEdges;
}
public @NonNull Iterable<@NonNull NavigableEdge> getNavigableEdges() {
return navigableEdges;
}
public @NonNull Iterable<@NonNull Edge> getPredicatedEdges() {
return predicatedEdges;
}
public @NonNull Iterable<@NonNull Node> getPredicatedMiddleNodes() {
return predicatedMiddleNodes;
}
public @NonNull Iterable<@NonNull Node> getPredicatedOutputNodes() {
return predicatedOutputNodes;
}
public @NonNull Iterable<@NonNull Edge> getRealizedEdges() {
return realizedEdges;
}
public @NonNull Iterable<@NonNull Node> getRealizedMiddleNodes() {
return realizedMiddleNodes;
}
public @NonNull Iterable<@NonNull Node> getRealizedOutputNodes() {
return realizedOutputNodes;
}
public @NonNull MappingRegion getRegion() {
return region;
}
public @NonNull Iterable<@NonNull Node> getTrueNodes() {
return trueNodes;
}
public boolean hasPredicatedEdge(@NonNull Edge edge) {
return alreadyPredicatedEdges.contains(edge);
}
public boolean hasPredicatedNode(@NonNull Node node) {
return alreadyPredicatedNodes.contains(node);
}
public boolean hasRealizedEdge(@NonNull Edge edge) {
return alreadyRealizedEdges.contains(edge);
}
public boolean hasRealizedNode(@NonNull Node node) {
return alreadyRealizedNodes.contains(node);
}
public boolean hasTrueNode(@NonNull Node node) {
return alreadyTrueNodes.contains(node);
}
public boolean isCorrolary(@NonNull Edge edge) {
if (!edge.isNavigation()) {
return false;
}
return corrolaryProperties.contains(((NavigableEdge)edge).getProperty());
}
private boolean isDead(@NonNull Node node, @Nullable Set<@NonNull Node> knownDeadNodes) {
if (node.isHead()) {
return false;
}
for (@NonNull Edge edge : node.getIncomingEdges()) {
if (edge.isNavigation()) {
if ((knownDeadNodes == null) || !knownDeadNodes.contains(edge.getSource())) {
return false;
}
}
}
for (@NonNull Edge edge : node.getOutgoingEdges()) {
if (edge.isNavigation() || edge.isExpression()) {
if ((knownDeadNodes == null) || !knownDeadNodes.contains(edge.getTarget())) {
return false;
}
}
}
return true;
}
/* private boolean isDead(@NonNull Edge edge) {
if (!edge.isExpression()) {
return false;
}
Node node = edge.getTarget();
for (@NonNull Edge incomingEdge : node.getIncomingEdges()) {
if ((incomingEdge != edge) && incomingEdge.isMatched()) {
return false;
}
}
for (@NonNull Edge outgoingEdge : node.getOutgoingEdges()) {
if (!isDead(outgoingEdge)) {
return false;
}
}
return true;
} */
public @NonNull Iterable<@NonNull MappingRegion> partition() {
// System.out.println(" partition " + region);
List<@NonNull MappingRegion> regions = new ArrayList<>();
if (realizedMiddleNodes.isEmpty() && predicatedMiddleNodes.isEmpty()) { // FIXME separate partitioning for folded middle
// regions.add(createRealizedRegion());
}
else {
if (!hasLoadedNodes) {
regions.add(region);
}
else {
if (predicatedMiddleNodes.isEmpty()) {
regions.add(createRealizedRegion());
}
else {
regions.add(createSpeculationRegion());
regions.add(createSpeculatedRegion());
}
for (@NonNull Edge outputEdge : realizedOutputEdges) {
if (!hasRealizedEdge(outputEdge)) {
regions.add(createAssignmentRegion(outputEdge, regions.size()));
}
}
check();
}
}
return regions;
}
@Override
public String toString() {
return region.toString();
}
}