blob: 14b5c5b5482377fa9db88b64e11896593da7cb0b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 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.Collection;
import java.util.Collections;
import java.util.Comparator;
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.CompleteClass;
import org.eclipse.ocl.pivot.DataType;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.StringUtil;
import org.eclipse.qvtd.compiler.CompilerProblem;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.analysis.ClassDatumAnalysis;
import org.eclipse.qvtd.compiler.internal.qvts2qvti.QVTs2QVTiVisitor;
import org.eclipse.qvtd.compiler.internal.utilities.SymbolNameBuilder;
import org.eclipse.qvtd.compiler.internal.utilities.ToDOT;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder.GraphEdge;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder.GraphNode;
import org.eclipse.qvtd.pivot.qvtbase.utilities.StandardLibraryHelper;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
public abstract class AbstractRegion implements Region, ToDOT.ToDOTable
{
public static class EarliestRegionComparator implements Comparator<@NonNull Region>
{
public static final @NonNull EarliestRegionComparator INSTANCE = new EarliestRegionComparator();
public static @NonNull List<@NonNull Region> sort(@NonNull Iterable<@NonNull Region> regions) {
List<@NonNull Region> sortedRegions = new ArrayList<>();
Iterables.addAll(sortedRegions, regions);
Collections.sort(sortedRegions, INSTANCE);
return sortedRegions;
}
@Override
public int compare(@NonNull Region o1, @NonNull Region o2) {
int i1 = o1.getInvocationIndex();
int i2 = o2.getInvocationIndex();
return i1 - i2;
}
}
public static final class EdgeSourceFunction implements Function<@NonNull Edge, @NonNull Node>
{
public static final @NonNull EdgeSourceFunction INSTANCE = new EdgeSourceFunction();
@Override
public @NonNull Node apply(@NonNull Edge edge) {
return edge.getSource();
}
}
public static final class EdgeTargetFunction implements Function<@NonNull Edge, @NonNull Node>
{
public static final @NonNull EdgeTargetFunction INSTANCE = new EdgeTargetFunction();
@Override
public @NonNull Node apply(@NonNull Edge edge) {
return edge.getTarget();
}
}
public static final class IsAssignedNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsAssignedNodePredicate INSTANCE = new IsAssignedNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isRealized() || node.isSpeculation();
}
}
public static final class IsAssignmentEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsAssignmentEdgePredicate INSTANCE = new IsAssignmentEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isRealized();
}
}
public static final class IsCallableRegionPredicate implements Predicate<@NonNull Region>
{
public static final @NonNull IsCallableRegionPredicate INSTANCE = new IsCallableRegionPredicate();
@Override
public boolean apply(@NonNull Region region) {
return !region.isOperationRegion();
}
}
public static final class IsCastEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsCastEdgePredicate INSTANCE = new IsCastEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isCast();
}
}
public static final class IsComposedNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsComposedNodePredicate INSTANCE = new IsComposedNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isComposed();
}
}
public static final class IsComputationEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsComputationEdgePredicate INSTANCE = new IsComputationEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isComputation();
}
}
/* public static final class IsComputedPredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsComputedPredicate INSTANCE = new IsComputedPredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isRealized();
}
} */
public static final class IsExpressionEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsExpressionEdgePredicate INSTANCE = new IsExpressionEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isExpression();
}
}
public static final class IsNavigableNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsNavigableNodePredicate INSTANCE = new IsNavigableNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isMatched();
}
}
public static final class IsNavigationEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsNavigationEdgePredicate INSTANCE = new IsNavigationEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isNavigation();
}
}
public static final class IsNewNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsNewNodePredicate INSTANCE = new IsNewNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isRealized() || node.isSpeculation();
}
}
public static final class IsOldNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsOldNodePredicate INSTANCE = new IsOldNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isConstant() || node.isLoaded() || node.isPredicated() || node.isSpeculated();
}
}
public static final class IsPatternNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsPatternNodePredicate INSTANCE = new IsPatternNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isPattern();
}
}
public static final class IsPredicatedEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsPredicatedEdgePredicate INSTANCE = new IsPredicatedEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isPredicated();
}
}
public static final class IsPredicatedNavigationEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsPredicatedNavigationEdgePredicate INSTANCE = new IsPredicatedNavigationEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isPredicated() && edge.isNavigation();
}
}
public static final class IsRealizedEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsRealizedEdgePredicate INSTANCE = new IsRealizedEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isRealized();
}
}
public static final class IsRealizedNavigationEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsRealizedNavigationEdgePredicate INSTANCE = new IsRealizedNavigationEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isRealized() && edge.isNavigation();
}
}
public static final class IsRecursionEdgePredicate implements Predicate<@NonNull Edge>
{
public static final @NonNull IsRecursionEdgePredicate INSTANCE = new IsRecursionEdgePredicate();
@Override
public boolean apply(@NonNull Edge edge) {
return edge.isRecursion();
}
}
public static final class IsTrueNodePredicate implements Predicate<@NonNull Node>
{
public static final @NonNull IsTrueNodePredicate INSTANCE = new IsTrueNodePredicate();
@Override
public boolean apply(@NonNull Node node) {
return node.isTrue();
}
}
protected final @NonNull MultiRegion multiRegion;
private @Nullable ScheduledRegion invokingRegion = null;
/**
* All the edges defined in this region, but not those in nested regions.
*/
private final @NonNull List<@NonNull Edge> edges = new ArrayList<>();
/**
* All the nodes defined in this region, but not those in nested regions.
*/
private final @NonNull List<@NonNull Node> nodes = new ArrayList<>();
/**
* The indexes in the overall schedule at which this region can be executed. The first index is the index at which ALL
* invocations occur. Subsequent indexes are when a referenced value may become available enabling a deferred execution.
*/
private final @NonNull List<@NonNull Integer> indexes = new ArrayList<>();
/**
* Ordered list of regions that call this region
*/
private final @NonNull List<@NonNull Region> callableParents = new ArrayList<>();
/**
* Ordered list of regions that this region calls. May exclude some children whose dependencies are unsatisfied.
* May include non-children whose dependencies are satisfied by earlier child calls.
*/
private final @NonNull List<@NonNull Region> callableChildren = new ArrayList<>();
/**
* The per-typed model predicated navigable edges for which an execution may be attempted before assignment.
*/
private @Nullable Map<@NonNull TypedModel, @NonNull Set<@NonNull NavigableEdge>> typedModel2checkedEdges = null;
/**
* The per-typed model realized navigable edges for which an execution may be attempted elsewhere before assignment here.
*/
private @Nullable Map<@NonNull TypedModel, @NonNull Set<@NonNull NavigableEdge>> typedModel2enforcedEdges = null;
private @Nullable String symbolName = null;
@SuppressWarnings("unused") // Used in the debugger
private final @NonNull ToDOT toDot = new ToDOT(this){};
/**
* The connections hosted by this region and passed to child regions.
*/
private @NonNull List<@NonNull NodeConnection> rootConnections = new ArrayList<>();
/**
* The connections propagated as middle guards from a hosted by a parent region and to one or more child regions.
*/
private @NonNull List<@NonNull NodeConnection> intermediateConnections = new ArrayList<>();
/**
* Set true if this region recurses on its outputs.
*/
// private boolean isCyclic = false;
protected AbstractRegion(@NonNull MultiRegion multiRegion) {
this.multiRegion = multiRegion;
multiRegion.addRegion(this);
}
/* protected void addBindingEdges(@NonNull Map<Node, Node> invokingBindings) {
for (Map.Entry<Node, Node> entry : invokingBindings.entrySet()) {
@SuppressWarnings("null")@NonNull Node invokingNode = entry.getValue();
@SuppressWarnings("null")@NonNull Node invokedNode = entry.getKey();
EdgeRole.BINDING.createEdge(this, invokingNode, invokedNode);
}
} */
@Override
public void addCallToChild(@NonNull Region region) {
callableChildren.add(region);
((AbstractRegion)region).callableParents.add(this);
}
@Override
public void addEdge(@NonNull Edge edge) {
assert !edges.contains(edge);
for (@NonNull Edge oldEdge : edges) {
if (oldEdge.getEdgeRole() == edge.getEdgeRole()) {
// assert (edge.getSource() != oldEdge.getSource()) || (edge.getTarget() != oldEdge.getTarget());
}
}
edges.add(edge);
}
private void addCheckedEdge(@NonNull NavigableEdge predicatedEdge) {
assert predicatedEdge.isPredicated();
assert predicatedEdge.getRegion() == this;
Map<@NonNull TypedModel, @NonNull Set<@NonNull NavigableEdge>> typedModel2checkedEdges2 = typedModel2checkedEdges;
assert typedModel2checkedEdges2 != null;
TypedModel typedModel = predicatedEdge.getSource().getClassDatumAnalysis().getTypedModel();
Set<@NonNull NavigableEdge> checkedEdges = typedModel2checkedEdges2.get(typedModel);
if (checkedEdges == null) {
checkedEdges = new HashSet<>();
typedModel2checkedEdges2.put(typedModel, checkedEdges);
}
checkedEdges.add(predicatedEdge);
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" checked " + predicatedEdge.getProperty() +
" at " + getIndexRangeText() + " in " + typedModel + " for " + this);
}
@Override
public void addEnforcedEdge(@NonNull NavigableEdge realizedEdge) {
assert realizedEdge.isRealized();
assert realizedEdge.getRegion() == this;
Map<@NonNull TypedModel, @NonNull Set<@NonNull NavigableEdge>> typedModel2enforcedEdges2 = typedModel2enforcedEdges;
assert typedModel2enforcedEdges2 != null;
TypedModel typedModel = realizedEdge.getSource().getClassDatumAnalysis().getTypedModel();
Set<@NonNull NavigableEdge> enforcedEdges = typedModel2enforcedEdges2.get(typedModel);
if (enforcedEdges == null) {
enforcedEdges = new HashSet<>();
typedModel2enforcedEdges2.put(typedModel, enforcedEdges);
}
enforcedEdges.add(realizedEdge);
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" enforced " + realizedEdge.getProperty() +
" at " + getIndexRangeText() +
" in " + realizedEdge.getSource().getClassDatumAnalysis().getTypedModel() + " for " + this);
}
@Override
public boolean addIndex(int index) {
for (int i = 0; i < indexes.size(); i++) {
Integer anIndex = indexes.get(i);
if (index == anIndex) {
return false;
}
if (index < anIndex) {
indexes.add(i, index);
return true;
}
}
indexes.add(index);
return true;
}
@Override
public void addIntermediateConnection(@NonNull NodeConnection connection) {
assert !intermediateConnections.contains(connection);
intermediateConnections.add(connection);
}
@Override
public void addRootConnection(@NonNull NodeConnection connection) {
assert !rootConnections.contains(connection);
rootConnections.add(connection);
}
@Override
public void addNode(@NonNull Node node) {
assert !nodes.contains(node);
nodes.add(node);
}
@Override
public void addVariableNode(@NonNull VariableDeclaration typedElement, @NonNull Node simpleNode) {}
@Override
public void appendNode(@NonNull GraphStringBuilder s, @NonNull String nodeName) {
String name = getSymbolName() + "\\n " + getName();
String indexText = getIndexText();
if (indexText != null) {
name = name + "\\n " + indexText;
}
s.setLabel(name);
String shape = getShape();
if (shape != null) {
s.setShape(shape);
}
String style = getStyle();
if (style != null) {
s.setStyle(style);
}
s.setColor(getColor());
// s.setPenwidth(getPenwidth());
s.appendAttributedNode(nodeName);
// if (isHead) {
// s.append("}");
// }
}
protected @Nullable String basicGetSymbolName() {
return symbolName;
}
@Override
public void buildPredicatedNavigationEdgesIndex(@NonNull Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2predicatedEdges) {
for (@NonNull NavigableEdge predicatedEdge : getPredicatedNavigationEdges()) {
if (!predicatedEdge.isCast()) {
Property property = predicatedEdge.getProperty();
Node predicatedNode = predicatedEdge.getSource();
TypedModel typedModel = predicatedNode.getClassDatumAnalysis().getTypedModel();
Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>> property2predicatedEdges = typedModel2property2predicatedEdges.get(typedModel);
if (property2predicatedEdges == null) {
property2predicatedEdges = new HashMap<>();
typedModel2property2predicatedEdges.put(typedModel, property2predicatedEdges);
}
List<@NonNull NavigableEdge> predicatedEdges = property2predicatedEdges.get(property);
if (predicatedEdges == null) {
predicatedEdges = new ArrayList<>();
property2predicatedEdges.put(property, predicatedEdges);
}
predicatedEdges.add(predicatedEdge);
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" " + typedModel + " predicated for " + property);
}
}
typedModel2checkedEdges = new HashMap<>();
}
@Override
public void buildRealizedNavigationEdgesIndex(@NonNull Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2realizedEdges) {
for (@NonNull NavigableEdge realizedEdge : getRealizedNavigationEdges()) {
Property property = realizedEdge.getProperty();
Node realizedNode = realizedEdge.getSource();
TypedModel typedModel = realizedNode.getClassDatumAnalysis().getTypedModel();
Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>> property2realizedEdges = typedModel2property2realizedEdges.get(typedModel);
if (property2realizedEdges == null) {
property2realizedEdges = new HashMap<>();
typedModel2property2realizedEdges.put(typedModel, property2realizedEdges);
}
List<@NonNull NavigableEdge> realizedEdges = property2realizedEdges.get(property);
if (realizedEdges == null) {
realizedEdges = new ArrayList<>();
property2realizedEdges.put(property, realizedEdges);
}
realizedEdges.add(realizedEdge);
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" " + typedModel + " realized for " + property);
}
typedModel2enforcedEdges = new HashMap<>();
}
/**
* Return true if a navigable path from startNode following the edges of protoPath,
* re-using edges and nodes where possible could be created. REturn false if such
* a path would violate a null parent requirement.
*/
protected boolean canCreatePath(@NonNull Node startNode, @NonNull List<@NonNull NavigableEdge> protoPath) {
// Map<Edge, Edge> path = new HashMap<>();
// Region region = startNode.getRegion();
Node sourceNode = startNode;
for (@NonNull NavigableEdge protoEdge : protoPath) {
NavigableEdge edge = sourceNode.getNavigationEdge(protoEdge.getProperty());
if (edge != null) {
Node protoTarget = protoEdge.getTarget();
Node target = edge.getTarget();
if (target.isExplicitNull() != (protoTarget.isExplicitNull())) {
return false;
}
sourceNode = target;
}
}
return true;
}
@Override
public void checkIncomingConnections() {
/* for (@NonNull Node node : getNodes()) {
NodeConnection incomingConnection = node.getIncomingConnection();
int incomingConnectionsSize = incomingConnection != null ? 1 : 0;
if ((node.getNodeRole() == Nodes.COMPOSING) && node.getRegion().isRootCompositionRegion()) {
assert incomingConnectionsSize == 0;
}
else if (node.isTrue()) {
assert incomingConnectionsSize == 0;
}
else if (node.isHead()) {
// assert incomingConnectionsSize == 1;
if (incomingConnectionsSize != 1) {
System.out.println("Inconsistent other incoming connections for " + node + " in " + this);
}
}
else if (node.isAttributeNode()) {
// assert incomingConnectionsSize == 0; // ?? should we connect these
}
else if (node.isComposed()) {
assert incomingConnectionsSize == 0;
}
else if (node.isNull()) {
assert incomingConnectionsSize == 0;
}
else if (node.isOperation()) {
assert incomingConnectionsSize == 0;
}
else if (node.isRealized()) {
assert incomingConnectionsSize == 0;
}
else if (node.isGuard()) {
;//assert incomingConnectionsSize == 0;
}
else if (node.isLoaded()) {
;//assert incomingConnectionsSize == 0;
}
else {
if (incomingConnectionsSize != 1) {
System.out.println("Inconsistent other incoming connections for " + node + " in " + this);
}
}
} */
}
@Override
public void computeCheckedOrEnforcedEdges(@NonNull Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2predicatedEdges,
@NonNull Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2realizedEdges) {
// CompleteModel completeModel = getSchedulerConstants().getEnvironmentFactory().getCompleteModel();
boolean doDebug = QVTs2QVTiVisitor.POLLED_PROPERTIES.isActive();
if (doDebug) {
QVTs2QVTiVisitor.POLLED_PROPERTIES.println("analyzing " + this + " (" + getIndexRangeText() + ")");
}
for (@NonNull NavigableEdge predicatedEdge : getPredicatedNavigationEdges()) {
if (!predicatedEdge.isCast()) {
Property property = predicatedEdge.getProperty();
if (doDebug) {
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" analyzing " + predicatedEdge.getSource().getName() + "::" + property.getName() + " : " + predicatedEdge.getSource().getCompleteClass());
}
EdgeConnection edgeConnection = predicatedEdge.getIncomingConnection();
if (edgeConnection != null) {
boolean isChecked = false;
for (@NonNull NavigableEdge usedEdge : edgeConnection.getSources()) {
Region usedRegion = usedEdge.getRegion();
usedRegion.addEnforcedEdge(usedEdge);
if (usedRegion.getFinalExecutionIndex() >= getInvocationIndex()) {
addCheckedEdge(predicatedEdge);
isChecked = true;
}
}
if (isChecked) {
for (@NonNull NavigableEdge usedEdge : edgeConnection.getSources()) {
Region usedRegion = usedEdge.getRegion();
usedRegion.addEnforcedEdge(usedEdge);
}
}
}
Node laterNode = predicatedEdge.getSource();
Node predicatedSourceNode = predicatedEdge.getSource();
Node predicatedTargetNode = predicatedEdge.getTarget();
NodeConnection usedConnection = predicatedTargetNode.getIncomingUsedConnection();
if (usedConnection != null) {
for (@NonNull Node usedSourceNode : usedConnection.getSources()) {
Region usedRegion = usedSourceNode.getRegion();
if (usedRegion.getFinalExecutionIndex() >= getInvocationIndex()) { // FIXME =
CompleteClass predicatedSourceType = predicatedSourceNode.getCompleteClass();
CompleteClass predicatedTargetType = predicatedTargetNode.getCompleteClass();
ClassDatumAnalysis classDatumAnalysis = laterNode.getClassDatumAnalysis();
TypedModel typedModel = classDatumAnalysis.getTypedModel();
Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>> property2realizedEdges = typedModel2property2realizedEdges.get(typedModel);
assert property2realizedEdges != null;
Property oclContainerProperty = getStandardLibraryHelper().getOclContainerProperty();
if (property == oclContainerProperty) {
// Node containerNode = predicatedEdge.getTarget();
// Node containedNode = predicatedEdge.getSource();
// CompleteClass containerType = containerNode.getCompleteClass();
// CompleteClass containedType = containedNode.getCompleteClass();
for (@NonNull Property candidateProperty : property2realizedEdges.keySet()) {
if (candidateProperty.isIsComposite()) {
// CompleteClass candidateContainerType = completeModel.getCompleteClass(candidateProperty.getOwningClass());
// CompleteClass candidateContainedType = completeModel.getCompleteClass(candidateProperty.getType());
// if (candidateContainerType.conformsTo(containerType) && containedType.conformsTo(candidateContainedType)) {
List<@NonNull NavigableEdge> realizedEdges = property2realizedEdges.get(candidateProperty);
assert realizedEdges != null;
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
// FIXME recheck for narrower types ??
Region earlierRegion = realizedEdge.getRegion();
// String isNotHazardous;
// if (this == earlierRegion) {
// isNotHazardous = "same region"; // FIXME must handle recursion
// }
// else if (earlierRegion.getLatestIndex() < getEarliestIndex()) {
// isNotHazardous = "later";// FIXME must handle any possible reads of any possible write
// }
// else {
Node realizedSourceNode = realizedEdge.getSource();
Node realizedTargetNode = realizedEdge.getTarget();
CompleteClass realizedSourceType = realizedSourceNode.getCompleteClass();
CompleteClass realizedTargetType = realizedTargetNode.getCompleteClass();
if (realizedSourceType.conformsTo(predicatedSourceType) && realizedTargetType.conformsTo(predicatedTargetType)) {
assert getFinalExecutionIndex() >= earlierRegion.getInvocationIndex();
// isNotHazardous = null;
}
else {
// isNotHazardous = "incompatible";
}
assert getFinalExecutionIndex() >= earlierRegion.getInvocationIndex();
// isNotHazardous = null;
// }
// if (isNotHazardous == null) {
addCheckedEdge(predicatedEdge);
earlierRegion.addEnforcedEdge(realizedEdge);
// }
// else if (doDebug) {
// QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" ignored " + this + "::" + laterNode.getName() + "(" + getEarliestIndex() + ".." + getLatestIndex() + ")" +
// " " + isNotHazardous + " (" + earlierRegion.getEarliestIndex() + ".." + earlierRegion.getLatestIndex() + ")" + earlierRegion + "::" + realizedEdge.getSource().getName());
// }
// }
}
}
}
}
else {
assert property2realizedEdges != null : "No realized typed model for " + typedModel;
List<@NonNull NavigableEdge> realizedEdges = property2realizedEdges.get(property);
if (realizedEdges == null) {
System.err.println("No realized edges for " + property + " in " + typedModel);
}
else {
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
Region earlierRegion = realizedEdge.getRegion();
String checkIsHazardFreeBecause;
String enforceIsHazardFreeBecause;
Node realizedSourceNode = realizedEdge.getSource();
Node realizedTargetNode = realizedEdge.getTarget();
CompleteClass realizedSourceType = realizedSourceNode.getCompleteClass();
CompleteClass realizedTargetType = realizedTargetNode.getCompleteClass();
if (!realizedSourceType.conformsTo(predicatedSourceType) || !realizedTargetType.conformsTo(predicatedTargetType)) {
checkIsHazardFreeBecause = "incompatible";
enforceIsHazardFreeBecause = "incompatible";
}
else if (this == earlierRegion) {
checkIsHazardFreeBecause = null; // Same region requires inter-recursion check
enforceIsHazardFreeBecause = null; // Same region requires inter-recursion enforce to be available for check
}
else if (earlierRegion.getFinalExecutionIndex() < getInvocationIndex()) {
checkIsHazardFreeBecause = "later";
enforceIsHazardFreeBecause = null; // Enforce required for later check
}
else {
// The QVTi AS has insufficient precision to identify which of multiple references is hazardous
checkIsHazardFreeBecause = null;
enforceIsHazardFreeBecause = null;
}
if (checkIsHazardFreeBecause == null) {
addCheckedEdge(predicatedEdge);
}
else if (doDebug) {
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" ignored check for " + this + "::" + laterNode.getName() + "(" + getIndexRangeText() + ")" +
" " + checkIsHazardFreeBecause + " (" + earlierRegion.getIndexRangeText() + ")" + earlierRegion + "::" + realizedEdge.getSource().getName());
}
if (enforceIsHazardFreeBecause == null) {
earlierRegion.addEnforcedEdge(realizedEdge);
}
else if (doDebug) {
QVTs2QVTiVisitor.POLLED_PROPERTIES.println(" ignored enforce " + this + "::" + laterNode.getName() + "(" + getIndexRangeText() + ")" +
" " + enforceIsHazardFreeBecause + " (" + earlierRegion.getIndexRangeText() + ")" + earlierRegion + "::" + realizedEdge.getSource().getName());
}
}
}
}
}
}
}
}
}
}
/**
* Recurse over the pattern of navigation edges from calledNode in a called mapping and callingNode in a calling mapping
* to populate called2binding with the pair-wise equivalent nodes. The callingNode may be null for a null navigation.
*
private void computeCompatiblePattern(@NonNull Node calledNode, @Nullable Node callingNode,
@NonNull Map<@NonNull Node, @Nullable Node> calledNode2callingNode, @NonNull Map<@NonNull NavigationEdge, @Nullable NavigationEdge> calledEdge2callingEdge) {
Node oldCallingNode = calledNode2callingNode.get(calledNode);
if (oldCallingNode != null) { // been here before
assert (oldCallingNode == callingNode) || (callingNode == null);
return;
}
if ((callingNode == null) && calledNode2callingNode.containsKey(calledNode)) { // here before and consistently null
return;
}
calledNode2callingNode.put(calledNode, callingNode);
for (@NonNull NavigationEdge calledEdge : calledNode.getNavigationEdges()) {
Node nextCalledNode = calledEdge.getTarget();
if (!nextCalledNode.isRealized() && !nextCalledNode.isAttributeNode()) {
Property property = calledEdge.getProperty();
NavigationEdge callingEdge = callingNode != null ? callingNode.getNavigationEdge(property) : null;
if (!property.isIsImplicit()) {
calledEdge2callingEdge.put(calledEdge, callingEdge);
}
if (callingEdge != null) {
Node nextCallingNode = callingEdge.getTarget();
assert nextCallingNode.isNull() == nextCalledNode.isNull();
if (!nextCalledNode.isNull()) {
computeCompatiblePattern(nextCalledNode, nextCallingNode, calledNode2callingNode, calledEdge2callingEdge);
}
}
else {
computeCompatiblePattern(nextCalledNode, null, calledNode2callingNode, calledEdge2callingEdge);
}
}
}
} */
protected @NonNull SymbolNameBuilder computeSymbolName() {
// List<String> names = new ArrayList<>();
// for (@NonNull MappingAction action : getMappingActions()) {
// names.add(action.getMapping().getName());
// }
// Collections.sort(names);
SymbolNameBuilder s = null;
Set<@NonNull Node> bestToOneSubRegion = null;
Node bestNamingNode = null;
for (@NonNull Node newNode : getNewNodes()) {
Set<@NonNull Node> toOneSubRegion = computeToOneSubRegion(new HashSet<>(), newNode);
if ((bestToOneSubRegion == null) || (toOneSubRegion.size() > bestToOneSubRegion.size())) {
bestToOneSubRegion = toOneSubRegion;
bestNamingNode = newNode;
}
else if ((bestNamingNode != null) && (toOneSubRegion.size() == bestToOneSubRegion.size())) {
if (ClassUtil.safeCompareTo(bestNamingNode.getCompleteClass().getName(), newNode.getCompleteClass().getName()) > 0) {
bestToOneSubRegion = toOneSubRegion;
bestNamingNode = newNode;
}
}
}
if (bestNamingNode != null) {
s = new SymbolNameBuilder();
s.appendString("m_");
s.appendName(bestNamingNode.getCompleteClass().getName());
List<@NonNull String> headNames = new ArrayList<>();
for (@NonNull Node headNode : getHeadNodes()) {
String name = headNode.getCompleteClass().getName();
if (name != null) {
headNames.add(name);
}
}
for (@NonNull String headName : headNames) {
s.appendString("_");
s.appendString(headName);
}
}
else {
for (@NonNull Node headNode : getHeadNodes()) {
s = new SymbolNameBuilder();
s.appendString("m_");
s.appendName(headNode.getCompleteClass().getName());
List<String> edgeNames = new ArrayList<>();
for (@NonNull NavigableEdge edge : headNode.getNavigationEdges()) {
String propertyName = edge.getProperty().getName();
edgeNames.add(edge.getTarget().isExplicitNull() ? propertyName + "0" : propertyName);
}
Collections.sort(edgeNames);
for (String edgeName : edgeNames) {
s.appendString("_");
s.appendName(edgeName);
}
break;
}
}
if (s == null) {
s = new SymbolNameBuilder();
s.appendString("m_");
}
return s;
}
private @NonNull Set<@NonNull Node> computeToOneSubRegion(@NonNull Set<@NonNull Node> toOneSubRegion, @NonNull Node atNode) {
if (toOneSubRegion.add(atNode)) {
for (@NonNull NavigableEdge edge : atNode.getNavigationEdges()) {
assert edge.getSource() == atNode;
Property source2target = edge.getProperty();
if (!source2target.isIsMany() && !source2target.isIsImplicit()) {
computeToOneSubRegion(toOneSubRegion, edge.getTarget());
}
}
}
return toOneSubRegion;
}
/**
* Create a NodeConnection to the realized node for each attribute.
*
private void createAttributeConnections() {
ScheduledRegion invokingRegion2 = invokingRegion;
assert invokingRegion2 != null;
RootScheduledRegion rootScheduledRegion = invokingRegion2.getRootScheduledRegion();
for (@NonNull NavigationEdge predicatedEdge : getPredicatedNavigationEdges()) {
Node predicatedTarget = predicatedEdge.getTarget();
if (predicatedEdge.isNavigation()
&& !predicatedEdge.isCast()
&& (predicatedTarget.getIncomingConnection() == null)
&& predicatedTarget.isAttributeNode()) { // FIXME isCast does not need to be isNavigation now that it can be isNavigable
Iterable<@NonNull NavigationEdge> realizedEdges = rootScheduledRegion.getRealizedEdges(predicatedEdge);
if (realizedEdges != null) {
Property predicatedProperty = predicatedEdge.getProperty();
assert !predicatedProperty.isIsImplicit();
ClassDatumAnalysis classDatumAnalysis = predicatedTarget.getClassDatumAnalysis();
List<@NonNull Node> sourceNodes = new ArrayList<>();
for (@NonNull NavigationEdge realizedEdge : realizedEdges) {
sourceNodes.add(realizedEdge.getTarget());
}
NodeConnection nodeConnection = invokingRegion2.getNodeConnection(sourceNodes, classDatumAnalysis);
nodeConnection.addUsedTargetNode(predicatedTarget, false);
if (Scheduler.CONNECTIONS.isActive()) {
Scheduler.CONNECTIONS.println(" Attribute NodeConnection to " + predicatedTarget);
}
}
}
}
} */
/**
* Create an EdgeConnection for the predicatedEdge and/or its target node.
*/
private void createEdgeConnection(@NonNull NavigableEdge predicatedEdge) {
assert predicatedEdge.isNavigation();
assert predicatedEdge.getIncomingConnection() == null;
if (predicatedEdge.isCast()) {
return; // casts are handled as an extension of a true navigation
}
Property predicatedProperty = predicatedEdge.getProperty();
if (predicatedProperty.isIsImplicit()) {
return; // unnavigable opposites are handled by the navigable property
}
ScheduledRegion invokingRegion2 = invokingRegion;
assert invokingRegion2 != null;
RootScheduledRegion rootScheduledRegion = invokingRegion2.getRootScheduledRegion();
NavigableEdge castEdge = RegionUtil.getCastTarget(predicatedEdge);
Node castTarget = RegionUtil.getCastTarget(castEdge.getTarget());
ClassDatumAnalysis classDatumAnalysis = castTarget.getClassDatumAnalysis();
if (classDatumAnalysis.getCompleteClass().getPrimaryClass() instanceof DataType) {
Iterable<@NonNull NavigableEdge> realizedEdges = rootScheduledRegion.getRealizedEdges(predicatedEdge, classDatumAnalysis);
if (realizedEdges != null) {
List<@NonNull Node> sourceNodes = new ArrayList<>();
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
if (RegionUtil.isElementallyConformantSource(realizedEdge, predicatedEdge) && RegionUtil.isConformantTarget(realizedEdge, predicatedEdge)) {
sourceNodes.add(realizedEdge.getTarget());
}
}
NodeConnection nodeConnection = invokingRegion2.getAttributeConnection(sourceNodes, predicatedEdge.getSource().getCompleteClass(), predicatedProperty, classDatumAnalysis);
nodeConnection.addUsedTargetNode(castTarget, false);
if (QVTp2QVTs.CONNECTION_CREATION.isActive()) {
QVTp2QVTs.CONNECTION_CREATION.println(" Attribute NodeConnection \"" + nodeConnection + "\" to " + castTarget);
// Scheduler.CONNECTIONS.println(" classDatumAnalysis " + classDatumAnalysis);
// for (@NonNull Node sourceNode : sourceNodes) {
// Scheduler.CONNECTIONS.println(" from " + sourceNode.getRegion());
// Scheduler.CONNECTIONS.println(" " + sourceNode);
// }
// for (@NonNull NavigationEdge realizedEdge : realizedEdges) {
// Scheduler.CONNECTIONS.println(" edge " + realizedEdge);
// }
}
}
}
else {
Iterable<@NonNull Node> sourceNodes = rootScheduledRegion.getIntroducingOrProducingNodes(classDatumAnalysis);
// if (sourceNodes != null) {
Iterable<@NonNull NavigableEdge> realizedEdges = rootScheduledRegion.getRealizedEdges(predicatedEdge, classDatumAnalysis);
if (realizedEdges != null) {
Set<@NonNull Region> edgeSourceRegions = new HashSet<>();
Set<@NonNull Region> nodeSourceRegions = new HashSet<>();
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
edgeSourceRegions.add(realizedEdge.getRegion());
}
if (sourceNodes != null) {
for (@NonNull Node sourceNode : sourceNodes) {
nodeSourceRegions.add(sourceNode.getRegion());
}
}
//
// Create an EdgeConnection for the edge realizations unless all edges are sources by node sources.
//
if (!nodeSourceRegions.containsAll(edgeSourceRegions)) { // If edges are assigned independently of their targets.
Set<@NonNull Region> conformantEdgeSourceRegions = null;
List<@NonNull NavigableEdge> thoseEdges = null;
for (@NonNull NavigableEdge realizedEdge : realizedEdges) {
if (RegionUtil.isElementallyConformantSource(realizedEdge, predicatedEdge) && RegionUtil.isConformantTarget(realizedEdge, predicatedEdge)) {
if (thoseEdges == null) {
thoseEdges = new ArrayList<>();
conformantEdgeSourceRegions = new HashSet<>();
}
if (!thoseEdges.contains(realizedEdge)) {
thoseEdges.add(realizedEdge);
assert conformantEdgeSourceRegions != null;
conformantEdgeSourceRegions.add(realizedEdge.getRegion());
}
}
}
if ((thoseEdges != null) && !nodeSourceRegions.containsAll(conformantEdgeSourceRegions)) {
EdgeConnection edgeConnection = invokingRegion2.getEdgeConnection(thoseEdges, predicatedProperty);
if (QVTp2QVTs.CONNECTION_CREATION.isActive()) {
QVTp2QVTs.CONNECTION_CREATION.println(" EdgeConnection \"" + edgeConnection + "\" to " + predicatedEdge);
}
if (!Iterables.contains(edgeConnection.getTargetEdges(), castEdge)) {
edgeConnection.addUsedTargetEdge(castEdge, false);
if (QVTp2QVTs.CONNECTION_CREATION.isActive()) {
for (@NonNull NavigableEdge thatEdge : thoseEdges) {
QVTp2QVTs.CONNECTION_CREATION.println(" from " + thatEdge.getRegion() + " : " + thatEdge);
}
}
}
}
}
}
//
// Create a NodeConnection for the node realizations.
//
if ((sourceNodes != null)
&& !castTarget.isLoaded() // WIP and !isOnlyCast
&& !castTarget.isConstant()
&& !castTarget.isHead()
&& !castTarget.isOperation()
&& !castTarget.isTrue()
&& (castTarget.getIncomingConnection() == null)
// && !castTarget.isAttributeNode()
// && !rootScheduledRegion.isOnlyCastOrRecursed(predicatedNode)
// && !hasEdgeConnection(predicatedNode)
) {
NodeConnection predicatedConnection = invokingRegion2.getNodeConnection(sourceNodes, classDatumAnalysis);
predicatedConnection.addUsedTargetNode(castTarget, false);
if (QVTp2QVTs.CONNECTION_CREATION.isActive()) {
QVTp2QVTs.CONNECTION_CREATION.println(" NodeConnection \"" + predicatedConnection + "\" to " + castTarget);
for (@NonNull Node sourceNode : sourceNodes) {
QVTp2QVTs.CONNECTION_CREATION.println(" from " + sourceNode.getRegion() + " : " + sourceNode);
}
}
}
// }
}
}
@Override
public @NonNull RegionProblem createError(@NonNull String messageTemplate, Object... bindings) {
String boundMessage = StringUtil.bind(messageTemplate, bindings);
return new RegionProblem(CompilerProblem.Severity.ERROR, this, boundMessage);
}
/**
* Create and return a NodeConnection to the nodes that can provide the sources for headNode.
* Returns null if the pattern surrounding the headNode conflicts with the pattern
* surrounding all possible sources.
*/
private @Nullable NodeConnection createHeadConnection(@NonNull Node headNode) {
ScheduledRegion invokingRegion2 = invokingRegion;
assert invokingRegion2 != null;
RootScheduledRegion rootScheduledRegion = invokingRegion2.getRootScheduledRegion();
ClassDatumAnalysis classDatumAnalysis = headNode.getClassDatumAnalysis();
List<@NonNull Node> headSources = null;
//
// Locate compatible introducers and non-recursive producers
//
Iterable<@NonNull Node> sourceNodes = rootScheduledRegion.getIntroducingOrProducingNodes(headNode);
if (sourceNodes != null) {
for (@NonNull Node sourceNode : sourceNodes) {
Region sourceRegion = sourceNode.getRegion();
if (sourceRegion != this) {
Map<@NonNull Node, @NonNull Node> called2calling = new HashMap<>();
if (isCompatiblePattern(headNode, sourceNode, called2calling)) {
if (headSources == null) {
headSources = new ArrayList<>();
}
headSources.add(sourceNode);
}
}
}
}
if (headSources == null) {
return null;
}
//
// Connect up the head
//
NodeConnection headConnection = invokingRegion2.getNodeConnection(headSources, classDatumAnalysis);
if (headNode.isDependency()) {
headConnection.addUsedTargetNode(headNode, false);
}
else {
headConnection.addPassedTargetNode(headNode);
}
if (QVTp2QVTs.CONNECTION_CREATION.isActive()) {
QVTp2QVTs.CONNECTION_CREATION.println((headNode.isDependency() ? " Extra NodeConnection " : " Head NodeConnection \"") + headConnection + "\" to " + headNode);
for (@NonNull Node sourceNode : headSources) {
QVTp2QVTs.CONNECTION_CREATION.println(" from " + sourceNode.getRegion() + " : " + sourceNode);
}
}
return headConnection;
}
/**
* Return the Connections to each of the head nodes. Returns null if the pattern surrounding any headNode conflicts with the pattern
* surrounding all its possible sources. (Any head with no sources is a non-invocation.)
*/
private @Nullable Iterable<@NonNull NodeConnection> createHeadConnections() {
List<@NonNull NodeConnection> headConnections = null;
for (@NonNull Node headNode : getHeadNodes()) {
if (headNode.isTrue()) { /* true nodes do not need connections. */ }
else if (headNode.isDependency()) {
createHeadConnection(headNode); /** Dependency nodes have extra not-head connections. */
}
else {
NodeConnection headConnection = createHeadConnection(headNode);
if (headConnection == null) {
multiRegion.getSchedulerConstants().addProblem(createError("createHeadConnections abandoned for " + headNode));
headConnection = createHeadConnection(headNode); // FIXME debugging
return null; // so matching only fails for unmatchable real heads
}
else {
if (headConnections == null) {
headConnections = new ArrayList<>();
}
headConnections.add(headConnection);
}
// FIXME. If there are multiple heads and an internal node is reachable from more than one head, then the possible
// sources for the internal node are the intersection of the alternatives which may eliminate some call paths.
}
}
return headConnections;
}
/**
* Create the connections that establish the inter-region dependencies.
*
* Every node/edge must have a connection to all its possible sources to ensure that the compile-time / run-time
* scheduler delays the execution of this region until the sources are available.
*
* Connections may be omitted when we can prove that the connection is available as a consequence of some restriction.
* - a connection to a CONSTANT source is unnecessary (always available)
* - a connection to a LOADED source is unnecessary (always available)
* - a connection to a source whose navigation path is incompatible with the head-to-target path is unnecessary
* - a connection to a node that is only used in cast form is unnecessary (the cast node provides more precision)
* - a connection to a cast edge is unnecessary (the cast edge extends a navigation edge that has a connection)
*
* Connections to attribute nodes are connected to just the node; a 'duplicate' edge connection is unnecessary
*
* Connections to realized nodes can be omitted if they are at one end of a realized edge
*
* Connections to edges account for type conformance of the nodes ends. The edge ends extend to account for casts.
*
* Each head node has a passed connection from its sources.
* Consistently related nodes navigable from the head have a bindable connection to the correspondingly related sources.
* Inconsistently related nodes navigable from the head have a computable connection to all compatibly typed sources.
* Unrelated nodes such as the internals of computations are not connected; their dependencies should be in dependency heads.
* Edges dependent on realization elsewhere are represented by connection from all head nodes of the dependent region
* to all heads of the realizing region.
*/
@Override
public void createIncomingConnections() {
if (QVTp2QVTs.CONNECTION_CREATION.isActive()) {
QVTp2QVTs.CONNECTION_CREATION.println("connecting " + this);
}
assert !(this instanceof RootCompositionRegion);
Iterable<@NonNull NodeConnection> headConnections = createHeadConnections();
if (headConnections != null) {
for (@NonNull NavigableEdge predicatedEdge : getPredicatedNavigationEdges()) {
if (!predicatedEdge.isCast()) {
createEdgeConnection(predicatedEdge);
}
}
}
}
/**
* Create a NodeConnection to the realized node for each node, unless there is already an EdgeConnection for any edge to/from the node.
*
private void createNodeConnections() {
ScheduledRegion invokingRegion2 = invokingRegion;
assert invokingRegion2 != null;
RootScheduledRegion rootScheduledRegion = invokingRegion2.getRootScheduledRegion();
for (@NonNull Node predicatedNode : getPredicatedNodes()) {
if (!predicatedNode.isLoaded() // WIP and !isOnlyCast
&& !predicatedNode.isConstant()
&& !predicatedNode.isHead()
&& !predicatedNode.isOperation()
&& !predicatedNode.isTrue()
&& (predicatedNode.getIncomingConnection() == null)
&& !predicatedNode.isAttributeNode()
&& !rootScheduledRegion.isOnlyCastOrRecursed(predicatedNode)
&& !hasEdgeConnection(predicatedNode)) {
ClassDatumAnalysis classDatumAnalysis = predicatedNode.getClassDatumAnalysis();
Iterable<@NonNull Node> sourceNodes = rootScheduledRegion.getIntroducingOrProducingNodes(classDatumAnalysis);
if (sourceNodes != null) {
NodeConnection predicatedConnection = invokingRegion2.getNodeConnection(sourceNodes, classDatumAnalysis);
predicatedConnection.addUsedTargetNode(predicatedNode, false);
if (Scheduler.CONNECTIONS.isActive()) {
Scheduler.CONNECTIONS.println(" NodeConnection from " + predicatedNode);
}
}
}
}
} */
/**
* Create a NodeConnection to the realized node for each node in the pattern related to a head node.
*
private void createRelatedConnections(@NonNull NodeConnection headConnection) {
@NonNull Iterable<@NonNull Node> headSources = headConnection.getSources();
/**
* Each bindable node is navigable within the guard/predicate from the head and has an equivalent node in every possible caller.
* A bindable node may therefore by passed by value from each of a known set of calling nodes to each called node.
* /
Map<@NonNull NavigationEdge, @NonNull List<@NonNull NavigationEdge>> related2bindableSources = new HashMap<>();
/**
* Each computable node is navigable within the guard/predicate from the head but does not have an equivalent node in every possible caller.
* A computable node cannot always be passed by value. It is therefore recomputed within a known set of calling regions at each called node.
* /
Map<@NonNull Node, @NonNull List<@NonNull Region>> related2computableSources = new HashMap<>();
ScheduledRegion invokingRegion2 = invokingRegion;
assert invokingRegion2 != null;
RootScheduledRegion rootScheduledRegion = invokingRegion2.getRootScheduledRegion();
//
// Categorize the corresponding sources for each related node as uniformly bindable else re-computable.
//
for (@NonNull Node headSource : headSources) {
Region sourceRegion = headSource.getRegion();
assert this != sourceRegion;
for (@NonNull Node headNode : headConnection.getTargets()) {
if ((headNode.getRegion() == this) && headNode.isHead()) {
Map<@NonNull Node, @Nullable Node> calledNode2callingNode = new HashMap<>();
Map<@NonNull NavigationEdge, @Nullable NavigationEdge> calledEdge2callingEdge = new HashMap<>();
computeCompatiblePattern(headNode, headSource, calledNode2callingNode, calledEdge2callingEdge);
for (Map./*@NonNull* /Entry<@NonNull NavigationEdge, @Nullable NavigationEdge> entry : calledEdge2callingEdge.entrySet()) {
@NonNull NavigationEdge calledEdge = entry.getKey();
@NonNull Node calledNode = calledEdge.getTarget();
assert calledNode.isHead() == (calledNode == headNode); // FIXME validating more efficient test
if (!calledNode.isHead()
&& !calledNode.isLoaded()
&& !calledNode.isConstant()
&& !rootScheduledRegion.isOnlyCastOrRecursed(calledNode)) {
@Nullable NavigationEdge callingEdge = entry.getValue();
if ((callingEdge != null) && !related2computableSources.containsKey(calledNode)) {
List<@NonNull NavigationEdge> bindableSources = related2bindableSources.get(calledEdge);
if (bindableSources == null) {
bindableSources = new ArrayList<>();
related2bindableSources.put(calledEdge, bindableSources);
}
assert !bindableSources.contains(callingEdge);
bindableSources.add(callingEdge);
}
else {
List<@NonNull Region> computableSources = related2computableSources.get(calledNode);
if (computableSources == null) {
computableSources = new ArrayList<>();
related2computableSources.put(calledNode, computableSources);
}
assert !computableSources.contains(sourceRegion);
computableSources.add(sourceRegion);
if (related2bindableSources.containsKey(calledEdge)) {
List<@NonNull NavigationEdge> bindableSources = related2bindableSources.remove(calledEdge);
assert bindableSources != null;
for (@NonNull NavigationEdge bindableSource : bindableSources) {
Region bindableRegion = bindableSource.getRegion();
assert !computableSources.contains(sourceRegion);
computableSources.add(bindableRegion);
}
}
}
}
}
}
}
}
assert Sets.intersection(related2bindableSources.keySet(), related2computableSources.keySet()).isEmpty();
//
// Connect the uniformly bindable calling sources to their called nodes.
//
for (Map./*@NonNull* /Entry<@NonNull NavigationEdge, @NonNull List<@NonNull NavigationEdge>> entry : related2bindableSources.entrySet()) {
@NonNull NavigationEdge calledEdge = entry.getKey();
@NonNull Node calledNode = calledEdge.getTarget();
assert !calledNode.isLoaded() && !calledNode.isConstant();
@NonNull List<@NonNull NavigationEdge> bindableEdges = entry.getValue();
EdgeConnection bindableConnection = invokingRegion2.getEdgeConnection(bindableEdges, calledEdge.getProperty());
bindableConnection.addUsedTargetEdge(calledEdge, false);
if (Scheduler.CONNECTIONS.isActive()) {
Scheduler.CONNECTIONS.println(" Bindable EdgeConnection to " + calledEdge);
}
}
//
// Connect the introducing/producing nodes of the not uniformly bindable calling sources to their called nodes.
//
for (Map./*@NonNull* /Entry<@NonNull Node, @NonNull List<@NonNull Region>> entry : related2computableSources.entrySet()) {
@NonNull Node calledNode = entry.getKey();
assert !calledNode.isLoaded() && !calledNode.isConstant() && !rootScheduledRegion.isOnlyCastOrRecursed(calledNode);
ClassDatumAnalysis classDatumAnalysis = calledNode.getClassDatumAnalysis();
Iterable<@NonNull Node> introducingOrProducingNodes = rootScheduledRegion.getIntroducingOrProducingNodes(classDatumAnalysis);
if (introducingOrProducingNodes != null) {
@NonNull List<@NonNull Region> computableRegions = entry.getValue();
List<@NonNull Node> computableSourceNodes = null;
assert calledNode.getRegion() == this; // FIXME just checking simpler test
for (@NonNull Node computableSourceNode : introducingOrProducingNodes) {
Region sourceRegion = computableSourceNode.getRegion();
if ((computableSourceNode.getRegion() != this) && !computableRegions.contains(sourceRegion)) { // FIXME only if a Recursion Edge
if (computableSourceNodes == null) {
computableSourceNodes = new ArrayList<>();
}
computableSourceNodes.add(computableSourceNode);
}
}
if (computableSourceNodes != null) {
NodeConnection computableConnection = invokingRegion2.getNodeConnection(computableSourceNodes, classDatumAnalysis);
computableConnection.addUsedTargetNode(calledNode, false);
if (Scheduler.CONNECTIONS.isActive()) {
Scheduler.CONNECTIONS.println(" Computable NodeConnection to " + calledNode);
}
}
}
}
} */
@Override
public @NonNull RegionProblem createWarning(@NonNull String messageTemplate, Object... bindings) {
String boundMessage = StringUtil.bind(messageTemplate, bindings);
return new RegionProblem(CompilerProblem.Severity.WARNING, this, boundMessage);
}
protected @Nullable Map<@NonNull Node, @NonNull Node> expandRecursion(@NonNull Node nextNode, @NonNull Node prevNode, @NonNull Map<@NonNull Node, @NonNull Node> bindings) {
Node oldPrevNode = bindings.put(nextNode, prevNode);
if (oldPrevNode != null) {
assert oldPrevNode == prevNode;
return bindings;
}
for (@NonNull NavigableEdge navigationEdge : prevNode.getNavigationEdges()) {
Node nextTarget = nextNode.getNavigationTarget(navigationEdge.getProperty());
if (nextTarget == null) {
return null;
}
Node prevTarget = navigationEdge.getTarget();
if (expandRecursion(nextTarget, prevTarget, bindings) == null) {
return null;
}
}
return bindings;
}
@Override
public @NonNull Iterable<@NonNull Node> getAncestorsOf(@NonNull Node node) {
List<@NonNull Node> ancestors = new ArrayList<>();
HashSet<@NonNull Node> ancestorSet = new HashSet<>();
node.getAllAncestors(ancestorSet);
for (@NonNull Node ancestor : ancestorSet) {
if (ancestor.getRegion() == this) {
ancestors.add(ancestor);
}
}
return ancestors;
}
@Override
public final @NonNull Iterable<@NonNull Node> getAssignedNodes() {
return Iterables.filter(nodes, IsAssignedNodePredicate.INSTANCE);
}
public final @NonNull Iterable<@NonNull NavigableEdge> getAssignmentEdges() {
@SuppressWarnings("unchecked")
@NonNull Iterable<@NonNull NavigableEdge> filter = (Iterable<@NonNull NavigableEdge>)(Object)Iterables.filter(edges, IsAssignmentEdgePredicate.INSTANCE);
return filter;
}
protected @Nullable List<@NonNull NavigableEdge> getBestPath(@Nullable List<@NonNull NavigableEdge> bestPath, @Nullable List<@NonNull NavigableEdge> candidatePath) {
if (bestPath == null) {
return candidatePath;
}
if (candidatePath == null) {
return bestPath;
}
int bestCost = getCost(bestPath);
int candidateCost = getCost(candidatePath);
if (candidateCost < bestCost) {
return candidatePath;
}
return bestPath;
}
private @NonNull NavigableEdge getBestEdge(@Nullable NavigableEdge bestEdge, @NonNull NavigableEdge candidateEdge) {
if (bestEdge == null) {
return candidateEdge;
}
if ((bestEdge.getProperty().isIsImplicit() && !candidateEdge.getProperty().isIsImplicit())) {
return candidateEdge;
}
return bestEdge; // ??? containment
}
@Override
public @NonNull Iterable<@NonNull Region> getCallableChildren() {
return callableChildren;
}
@Override
public @NonNull Iterable<@NonNull Region> getCallableParents() {
return callableParents;
}
// @Override
// public @NonNull ClassDatumAnalysis getClassDatumAnalysis(@NonNull Type type) {
// return getSchedulerConstants().getClassDatumAnalysis(type);
// }
/* @Override
public final @NonNull Iterable<Edge> getCastEdges() {
@SuppressWarnings("null")
@NonNull Iterable<Edge> filter = Iterables.filter(edges, IsCastEdgePredicate.INSTANCE);
return filter;
} */
@Override
public @NonNull List<@NonNull Region> getCalledRegions() {
List<@NonNull Region> childRegions = new ArrayList<>(); // FIXME cache
for (@NonNull NodeConnection childConnection : getOutgoingPassedConnections()) {
for (@NonNull Node childNode : childConnection.getTargetNodes()) {
Region childRegion = childNode.getRegion();
if (!childRegions.contains(childRegion)) {
childRegions.add(childRegion);
}
}
}
return childRegions;
}
@Override
public @NonNull List<@NonNull Region> getCallingRegions() {
List<@NonNull Region> callingRegions = new ArrayList<>(); // FIXME cache
for (@NonNull NodeConnection callingConnection : getIncomingPassedConnections()) {
for (@NonNull Node callingNode : callingConnection.getSources()) {
Region callingRegion = callingNode.getRegion();
if (!callingRegions.contains(callingRegion)) {
callingRegions.add(callingRegion);
}
}
}
return callingRegions;
}
@Override
public @Nullable Set<@NonNull NavigableEdge> getCheckedEdges(@NonNull TypedModel typedModel) {
assert typedModel2checkedEdges != null;
return typedModel2checkedEdges.get(typedModel);
}
@Override
public @NonNull ClassDatumAnalysis getClassDatumAnalysis(@NonNull TypedElement typedElement) {
return getSchedulerConstants().getClassDatumAnalysis(typedElement);
}
@Override
public @NonNull String getColor() {
return "blue";
}
@Override
public final @NonNull Iterable<@NonNull Node> getComposedNodes() {
return Iterables.filter(nodes, IsComposedNodePredicate.INSTANCE);
}
/* public final @NonNull Iterable<@NonNull Node> getComputedNodes() {
return Iterables.filter(nodes, IsComputedPredicate.INSTANCE);
} */
/* public final @NonNull Iterable<? extends Edge> getConsumedOrderingEdges() {
@SuppressWarnings("null")
@NonNull Iterable<Edge> filter = Iterables.filter(edges, IsConsumedOrderingEdgePredicate.INSTANCE);
return filter;
} */
private int getCost(@NonNull List<@NonNull NavigableEdge> path) {
int cost = 0;
for (@NonNull NavigableEdge edge : path) {
if (edge.getProperty().isIsImplicit()) {
cost++;
} // ??? containment
}
return cost;
}
/* public int getEarliestPassedConnectionSourceIndex() {
int earliestPassedConnectionSourceIndex = 0;
for (@NonNull NodeConnection passedConnection : getIncomingPassedConnections()) {
for (@NonNull Region sourceRegion : passedConnection.getSourceRegions()) {
int firstPassedConnectionSourceIndex = sourceRegion.getIndexes().get(0);
if (firstPassedConnectionSourceIndex > earliestPassedConnectionSourceIndex) { // Latest of multiple passed connections
earliestPassedConnectionSourceIndex = firstPassedConnectionSourceIndex;
}
}
}
return earliestPassedConnectionSourceIndex;
} */
@Override
public @NonNull Collection<@NonNull Edge> getEdges() {
return edges;
}
@Override
public @Nullable Set<@NonNull NavigableEdge> getEnforcedEdges(@NonNull TypedModel typedModel) {
assert typedModel2enforcedEdges != null;
return typedModel2enforcedEdges.get(typedModel);
}
@Override
public final @NonNull Iterable<@NonNull Edge> getExpressionEdges() {
@NonNull Iterable<@NonNull Edge> filter = Iterables.filter(edges, IsExpressionEdgePredicate.INSTANCE);
return filter;
}
@Override
public int getFinalExecutionIndex() {
assert indexes.size() > 0;
return indexes.get(indexes.size()-1);
}
@Override
public @NonNull Iterable<@NonNull DatumConnection> getIncomingConnections() { // FIXME cache
List<@NonNull DatumConnection> connections = new ArrayList<>();
for (@NonNull Node headNode : getHeadNodes()) {
NodeConnection connection = headNode.getIncomingPassedConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
for (@NonNull Node node : getNodes()) {
if (node.isDependency() || node.isPattern()) {
if (node.isLoaded() || node.isSpeculated() || node.isPredicated()) { // A DataType may be loaded but subject to an edge predication
NodeConnection connection = node.getIncomingUsedConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
}
}
for (@NonNull NavigableEdge edge : getPredicatedNavigationEdges()) {
EdgeConnection connection = edge.getIncomingConnection();
if ((connection != null) && !connections.contains(connection)) {
connections.add(connection);
}
}
return connections;
}
@Override
public @NonNull Iterable<@NonNull NodeConnection> getIncomingPassedConnections() { // FIXME cache
List<@NonNull NodeConnection> connections = new ArrayList<>();
for (@NonNull Node headNode : getHeadNodes()) {
NodeConnection connection = headNode.getIncomingPassedConnection();
if (connection != null) {
connections.add(connection);
}
}
return connections;
}
@Override
public @NonNull Iterable<@NonNull NodeConnection> getIncomingUsedConnections() { // FIXME cache
List<@NonNull NodeConnection> connections = new ArrayList<>();
for (@NonNull Node node : getPatternNodes()) {
if (node.isLoaded() || node.isSpeculated() || node.isPredicated()) { // A DataType may be loaded but subject to an edge predication
NodeConnection connection = node.getIncomingUsedConnection();
if (connection != null) {
connections.add(connection);
}
}
}
return connections;
}
@Override
public @NonNull String getIndexRangeText() {
return getInvocationIndex() + ".." + getFinalExecutionIndex();
}
public @Nullable String getIndexText() {
StringBuilder s = null;
for (@NonNull Integer index : indexes) {
if (s == null) {
s = new StringBuilder();
}
else {
s.append(",");
}
s.append(index.toString());
}
return s != null ? s.toString() : null;
}
@Override
public @NonNull List<@NonNull Integer> getIndexes() {
return indexes;
}
@Override
public @NonNull List<@NonNull NodeConnection> getIntermediateConnections() {
return intermediateConnections;
}
@Override
public int getInvocationIndex() {
assert indexes.size() > 0;
return indexes.get(0);
}
@Override
public @Nullable ScheduledRegion getInvokingRegion() {
return invokingRegion;
}
@Override
public @NonNull List<@NonNull DatumConnection> getLoopingConnections() {
List<@NonNull DatumConnection> loopingConnections = new ArrayList<>();
for (@NonNull DatumConnection connection : getOutgoingConnections()) {
for (@NonNull Region sourceRegion : connection.getSourceRegions()) {
if (this == sourceRegion) {
for (@NonNull Region targetRegion : connection.getTargetRegions()) {
if ((this == targetRegion) && !loopingConnections.contains(connection)) {
loopingConnections.add(connection);
}
}
}
}
}
return loopingConnections;
}
@Override
public @NonNull MultiRegion getMultiRegion() {
return multiRegion;
}
@Override
public final @NonNull Iterable<@NonNull Node> getNavigableNodes() {
return Iterables.filter(nodes, IsNavigableNodePredicate.INSTANCE);
}
@Override
public final @NonNull Iterable<@NonNull NavigableEdge> getNavigationEdges() {
@NonNull Iterable<@NonNull NavigableEdge> filter = Iterables.filter(edges, NavigableEdge.class);
return filter;
}
// @Override
// public @Nullable Node getNavigationTarget(@NonNull ClassNode sourceNode, @NonNull Property source2targetProperty) {
// NavigationEdge navigationEdge = getNavigationEdge(sourceNode, source2targetProperty);
// return navigationEdge != null ? navigationEdge.getTarget() : null;
// }
@Override
public final @NonNull Iterable<@NonNull Node> getNewNodes() {
return Iterables.filter(nodes, IsNewNodePredicate.INSTANCE);
}
@Override
public @NonNull Collection<@NonNull Node> getNodes() {
return nodes;
}
@Override
public @NonNull Iterable<@NonNull DatumConnection> getNextConnections() {
return getOutgoingConnections();
}
@Override
public final @NonNull Iterable<@NonNull Node> getOldNodes() {
return Iterables.filter(nodes, IsOldNodePredicate.INSTANCE);
}
@Override
public @NonNull List<@NonNull DatumConnection> getOutgoingConnections() { // FIXME cache
List<@NonNull DatumConnection> connections = new ArrayList<>();
for (@NonNull Node node : getNodes()) {
for (@NonNull NodeConnection connection : node.getOutgoingPassedConnections()) {
connections.add(connection);
}
for (@NonNull NodeConnection connection : node.getOutgoingUsedBindingEdges()) {
connections.add(connection);
}
}
for (@NonNull NavigableEdge edge : getNavigationEdges()) {
for (@NonNull EdgeConnection connection : edge.getOutgoingConnections()) {
connections.add(connection);
}
}
return connections;
}
@Override
public @NonNull Iterable<@NonNull NodeConnection> getOutgoingPassedConnections() { // FIXME cache
List<@NonNull NodeConnection> connections = new ArrayList<>();
for (@NonNull Node node : getNodes()) {
for (@NonNull NodeConnection connection : node.getOutgoingPassedConnections()) {
connections.add(connection);
}
}
return connections;
}
@Override
public @NonNull Iterable<@NonNull NodeConnection> getOutgoingUsedConnections() { // FIXME cache
List<@NonNull NodeConnection> connections = new ArrayList<>();
for (@NonNull Node node : getNodes()) {
for (@NonNull NodeConnection connection : node.getOutgoingUsedBindingEdges()) {
connections.add(connection);
}
}
return connections;
}
protected @Nullable List<@NonNull NavigableEdge> getPath(@NonNull Node sourceNode, @NonNull Node targetNode, @NonNull Set<@NonNull Edge> usedEdges) {
assert sourceNode.getRegion() == targetNode.getRegion();
NavigableEdge bestEdge = null;
List<@NonNull NavigableEdge> bestPath = null;
for (@NonNull NavigableEdge edge : sourceNode.getNavigationEdges()) {
if (!usedEdges.contains(edge) && !edge.getProperty().isIsMany() && !edge.isRealized()) {
if (edge.getTarget() == targetNode) {
bestEdge = getBestEdge(bestEdge, edge);
}
else {
Set<@NonNull Edge> moreUsedEdges = new HashSet<>(usedEdges);
moreUsedEdges.add(edge);
List<@NonNull NavigableEdge> tailPath = getPath(edge.getTarget(), targetNode, moreUsedEdges);
if (tailPath != null) {
tailPath = new ArrayList<>(tailPath);
tailPath.add(0, edge);
}
bestPath = getBestPath(bestPath, tailPath);
}
}
}
if (bestEdge == null) {
return bestPath;
}
else if (bestPath == null) {
return Collections.singletonList(bestEdge);
}
else {
return getBestPath(Collections.singletonList(bestEdge), bestPath);
}
}
@Override
public final @NonNull Iterable<@NonNull Node> getPatternNodes() {
return Iterables.filter(nodes, IsPatternNodePredicate.INSTANCE);
}
public final @NonNull Iterable<NavigableEdge> getPredicateEdges() {
@SuppressWarnings("unchecked")
@NonNull Iterable<@NonNull NavigableEdge> filter = (Iterable<@NonNull NavigableEdge>)(Object)Iterables.filter(edges, IsPredicatedEdgePredicate.INSTANCE);
return filter;
}
@Override
public final @NonNull Iterable<@NonNull NavigableEdge> getPredicatedNavigationEdges() {
@SuppressWarnings("unchecked")
@NonNull Iterable<@NonNull NavigableEdge> filter = (Iterable<@NonNull NavigableEdge>)(Object)Iterables.filter(edges, IsPredicatedNavigationEdgePredicate.INSTANCE);
return filter;
}
@Override
public final @NonNull Iterable<@NonNull Edge> getRealizedEdges() {
@NonNull Iterable<@NonNull Edge> filter = Iterables.filter(edges, IsRealizedEdgePredicate.INSTANCE);
return filter;
}
@Override
public final @NonNull Iterable<@NonNull NavigableEdge> getRealizedNavigationEdges() {
@SuppressWarnings("unchecked")
@NonNull Iterable<@NonNull NavigableEdge> filter = (Iterable<@NonNull NavigableEdge>)(Object)Iterables.filter(edges, IsRealizedNavigationEdgePredicate.INSTANCE);
return filter;
}
@Override
public final @NonNull Iterable<@NonNull Edge> getRecursionEdges() {
@NonNull Iterable<@NonNull Edge> filter = Iterables.filter(edges, IsRecursionEdgePredicate.INSTANCE);
return filter;
}
@Override
public @NonNull List<@NonNull NodeConnection> getRootConnections() {
return rootConnections;
}
@Override
public @NonNull SchedulerConstants getSchedulerConstants() {
return multiRegion.getSchedulerConstants();
}
@Override
public @Nullable String getShape() {
return null;
}
protected @NonNull StandardLibraryHelper getStandardLibraryHelper() {
return getSchedulerConstants().getStandardLibraryHelper();
}
@Override
public @Nullable String getStyle() {
return null;
}
@Override
public final @NonNull String getSymbolName() {
String symbolName2 = symbolName;
if (symbolName2 == null) {
symbolName = symbolName2 = getSchedulerConstants().reserveSymbolName(computeSymbolName(), this);
}
return symbolName2;
}
@Override
public final @NonNull Iterable<@NonNull Node> getTrueNodes() {
return Iterables.filter(nodes, IsTrueNodePredicate.INSTANCE);
}
@Override
public @NonNull List<@NonNull NodeConnection> getUsedConnections() { // FIXME cache
List<@NonNull NodeConnection> usedConnections = new ArrayList<>();
for (@NonNull Node node : getPatternNodes()) {
if (node.isLoaded() || node.isSpeculated() || node.isPredicated()) { // A DataType may be loaded but subject to an edge predication
NodeConnection connection = node.getIncomingUsedConnection();
if (connection != null) {
usedConnections.add(connection);
}
}
}
return usedConnections;
}
/**
* Expand/create and return sequentialRegion to cascade thisAction and its transitive
* successors as possible into a sequentially scheduled region. The invocation only makes a
* single attempt to cascade each successor, so it is possible that initial failures could
* succeed following further growth.
*
@Override
public @Nullable HierarchicalRegion growHierarchicalRegion(@Nullable HierarchicalRegion hierarchicalRegion) {
for (@SuppressWarnings("null")@NonNull Region nextRegion : getSuccessors()) {
Map<Node, Node> bindings = HierarchicalRegion.getHierarchicalBinding(nextRegion, this);
if (bindings != null) {
if (hierarchicalRegion == null) {
hierarchicalRegion = new HierarchicalRegion(this);
// this.setInvokingRegion(hierarchicalRegion);
}
resolveFutureNodes(hierarchicalRegion, bindings);
hierarchicalRegion.addAction(this, nextRegion, bindings);
// nextRegion.setInvokingRegion(hierarchicalRegion);
// hierarchicalRegion = nextRegion.growhierarchicalRegion(hierarchicalRegion);
}
}
if (hierarchicalRegion != null) {
hierarchicalRegion.toDOT();
}
return hierarchicalRegion;
} */
/**
* Expand/create and return sequentialRegion to cascade thisAction and its transitive
* successors as possible into a sequentially scheduled region. The invocation only makes a
* single attempt to cascade each successor, so it is possible that initial failures could
* succeed following further growth.
*
@Override
public @Nullable SequentialRegion growSequentialRegion(@Nullable SequentialRegion sequentialRegion) {
for (@SuppressWarnings("null")@NonNull Region nextRegion : getSuccessors()) {
Map<Node, Node> bindings = SequentialRegion.getSequentialBinding(nextRegion, this);
if (bindings != null) {
if (sequentialRegion == null) {
sequentialRegion = new SequentialRegion(this);
// this.setInvokingRegion(sequentialRegion);
}
resolveFutureNodes(sequentialRegion, bindings);
sequentialRegion.addAction(this, nextRegion, bindings);
// nextRegion.setInvokingRegion(sequentialRegion);
sequentialRegion = nextRegion.growSequentialRegion(sequentialRegion);
}
}
return sequentialRegion;
} */
/* private boolean hasEdgeConnection(@NonNull Node predicatedNode) {
for (@NonNull Edge edge : predicatedNode.getIncomingEdges()) {
if ((edge instanceof NavigationEdge) && (((NavigationEdge) edge).getIncomingConnection() != null)) {
return true;
}
}
for (@NonNull Edge edge : predicatedNode.getOutgoingEdges()) {
if ((edge instanceof NavigationEdge) && (((NavigationEdge) edge).getIncomingConnection() != null)) {
return true;
}
}
return false;
} */
/**
* Return true if the predicates of calledNode are not in conflict with the navigable paths from callingNode.
* called2calling identifies already identified conflict free pairs that do not need re-assessment.
*/
private boolean isCompatiblePattern(@NonNull Node calledNode, @NonNull Node callingNode, @NonNull Map<@NonNull Node, @NonNull Node> called2calling) {
Node oldPrevNode = called2calling.put(calledNode, callingNode);
if (oldPrevNode != null) {
return oldPrevNode == callingNode;
}
for (@NonNull NavigableEdge calledEdge : calledNode.getNavigationEdges()) {
Node nextCalledNode = calledEdge.getTarget();
if (!nextCalledNode.isRealized() && !nextCalledNode.isDataType()) { // FIXME why exclude AttributeNodes?
Edge nextCallingEdge = callingNode.getNavigationEdge(calledEdge.getProperty());
if (nextCallingEdge != null) {
Node nextCallingNode = nextCallingEdge.getTarget();
if ((nextCallingNode.isExplicitNull() != nextCalledNode.isExplicitNull())) {
return false;
}
if (!isCompatiblePattern(nextCalledNode, nextCallingNode, called2calling)) {
return false;
}
}
}
}
return true;
}
@Override
public boolean isChildCompositionRegion() {
return false;
}
@Override
public boolean isCyclicScheduledRegion() {
return false;
}
@Override
public boolean isOperationRegion() {
return false;
}
@Override
public boolean isRootCompositionRegion() {
return false;
}
/**
* Refine the call bindings of a mapping so that:
*
* Passed Bindings to the head that violate the head's predicates are removed, and in the case of a single caller
* the passed binding is redirected direct to the caller to facilitate re-use of the calling context.
*/
@Override
public void refineBindings(@NonNull Region bindingRegion) {
refineHeadBindings(bindingRegion);
/* List<Node> predicatedNodes = new ArrayList<>();
Iterables.addAll(predicatedNodes, getPredicatedNodes());
for (Node calledNode : predicatedNodes) {
if (calledNode.isHead() && !calledNode.isAttributeNode()) {
// for (@SuppressWarnings("null")@NonNull List<Node> headGroup : calledRegion.getHeadNodeGroups()) {
// for (@SuppressWarnings("null")@NonNull Node headNode : headGroup) {
List<Node> resolvedCallingSources = new ArrayList<>();
boolean prunedOne = false;
for (@SuppressWarnings("null")@NonNull Node callingSource : calledNode.getPassedBindingSources()) {
if (canExpandRecursion(callingSource, calledNode, new HashMap<>())) {
resolvedCallingSources.add(callingSource);
}
else {
prunedOne = true;
}
}
if (prunedOne) {
List<Edge> deadEdges = new ArrayList<>();
Iterables.addAll(deadEdges, calledNode.getIncomingPassedBindingEdges());
for (@SuppressWarnings("null")@NonNull Edge deadEdge : deadEdges) {
deadEdge.destroy();
}
Node targetNode = calledNode;
if (resolvedCallingSources.size() > 1) {
targetNode = Nodes.JOIN.createNode(this, "-join-", targetNode.getClassDatumAnalysis());
Edges.PASSED_BINDING.createEdge(this, targetNode, null, calledNode);
}
for (@SuppressWarnings("null")@NonNull Node resolvedCallingSource : resolvedCallingSources) {
Edges.PASSED_BINDING.createEdge(this, resolvedCallingSource, null, targetNode);
}
}
// }
// }
}
else if (!calledNode.isHead() && !calledNode.isAttributeNode()) {
List<NavigationEdge> bestPath = null;
for (@SuppressWarnings("null")@NonNull List<Node> headGroup : getHeadNodeGroups()) {
for (@SuppressWarnings("null")@NonNull Node headNode : headGroup) {
bestPath = getBestPath(bestPath, getPath(headNode, calledNode, new HashSet<>()));
}
}
if (bestPath != null) {
for (@SuppressWarnings("null")@NonNull List<Node> headGroup : getHeadNodeGroups()) {
for (@SuppressWarnings("null")@NonNull Node headNode : headGroup) {
List<Node> resolvedCallingSources = new ArrayList<>();
for (@SuppressWarnings("null")@NonNull Node callingSource : headNode.getPassedBindingSources()) {
Region callingRegion = callingSource.getRegion();
boolean isRecursion = false;
if (callingRegion == this) {
for (Edge edge : calledNode.getRecursionEdges()) {
if (edge.getTarget() == headNode) {
isRecursion= true;
}
}
}
if (!isRecursion) {
if (canCreatePath(callingSource, bestPath)) {
resolvedCallingSources.add(callingSource);
}
}
}
for (@SuppressWarnings("null")@NonNull Node callingSource : resolvedCallingSources) {
Map<Edge, Edge> innerEdge2outerEdge = createPath(callingSource, bestPath);
List<Edge> deadEdges = new ArrayList<>();
for (@SuppressWarnings("null")@NonNull Map.Entry<Edge, Edge> entry : innerEdge2outerEdge.entrySet()) {
Edge innerEdge2 = entry.getKey();
Node innerTarget = innerEdge2.getTarget();
for (Edge bindingEdge : innerTarget.getIncomingUsedBindingEdges()) {
deadEdges.add(bindingEdge);
}
Edge outerEdge = entry.getValue();
Edges.USED_BINDING.createEdge(this, outerEdge.getTarget(), innerEdge2.getName(), innerTarget);
}
for (@SuppressWarnings("null")@NonNull Edge deadEdge : deadEdges) {
deadEdge.destroy();
}
}
}
}
}
}
} */
}
/**
* Refine the call bindings of a mapping so that:
*
* Passed Bindings to the head that violate the head's predicates are removed, and in the case of a single caller
* the passed binding is redirected direct to the caller to facilitate re-use of the calling context.
*/
protected void refineHeadBindings(@NonNull Region bindingRegion) {
/* for (@SuppressWarnings("null")@NonNull List<Node> headGroup : getHeadNodeGroups()) {
for (@SuppressWarnings("null")@NonNull Node headNode : headGroup) {
List<Node> resolvedCallingSources = new ArrayList<>();
boolean prunedOne = false;
for (@SuppressWarnings("null")@NonNull Node callingSource : headNode.getPassedBindingSources()) {
if (isConflictFree(callingSource, headNode)) {
resolvedCallingSources.add(callingSource);
}
else {
prunedOne = true;
}
}
if (prunedOne) {
List<Edge> deadEdges = new ArrayList<>();
Iterables.addAll(deadEdges, headNode.getIncomingPassedBindingEdges());
for (@SuppressWarnings("null")@NonNull Edge deadEdge : deadEdges) {
deadEdge.destroy();
}
Node targetNode = headNode;
if (resolvedCallingSources.size() > 1) {
targetNode = Nodes.JOIN.createNode(this, "-join-", targetNode.getClassDatumAnalysis());
Edges.PASSED_BINDING.createEdge(bindingRegion, targetNode, null, headNode);
}
for (@SuppressWarnings("null")@NonNull Node resolvedCallingSource : resolvedCallingSources) {
Edges.PASSED_BINDING.createEdge(bindingRegion, resolvedCallingSource, null, targetNode);
}
}
}
} */
}
@Override
public void removeEdge(@NonNull Edge edge) {
boolean wasRemoved = edges.remove(edge);
assert wasRemoved;
}
@Override
public void removeNode(@NonNull Node node) {
boolean wasRemoved = nodes.remove(node);
assert wasRemoved;
}
@Override
public void resetHead(@NonNull Node headNode) {
throw new UnsupportedOperationException("resetHead not supported for " + this);
}
public void resolveRecursion() {
Map<@NonNull CompleteClass, @NonNull List<@NonNull Node>> completeClass2nodes = RegionUtil.getCompleteClass2Nodes(this);
List<@NonNull Node> headNodes = getHeadNodes();
if (headNodes.size() == 1) { // FIXME multi-heads
Node headNode = headNodes.get(0);
List<@NonNull Node> nodeList = completeClass2nodes.get(headNode.getCompleteClass());
assert nodeList != null;
if (nodeList.size() > 1) {
for (@NonNull Node node : nodeList) {
if (node != headNode) {
Map<@NonNull Node, @NonNull Node> bindings = expandRecursion(headNode, node, new HashMap<>());
if (bindings != null) {
// this.recursiveBindings = bindings;
for (Map.@NonNull Entry<@NonNull Node, @NonNull Node> entry : bindings.entrySet()) {
@NonNull Node prevNode = entry.getKey();
@NonNull Node nextNode = entry.getValue();
RegionUtil.createRecursionEdge(prevNode, nextNode, prevNode.isHead());
}
return; // FIXME can we have more than one recursion ??
}
}
}
}
}
}
@Override
public void setInvokingRegion(@NonNull ScheduledRegion invokingRegion) {
this.invokingRegion = invokingRegion;
}
// @Override
// public void setIsCyclic() {
// this.isCyclic = true;
// }
@Override
public void toCallGraph(@NonNull GraphStringBuilder s) {
s.appendNode(this);
for (final @NonNull Region region : getCallableChildren()) {
GraphEdge graphEdge = new GraphEdge()
{
@Override
public void appendEdgeAttributes(@NonNull GraphStringBuilder s, @NonNull GraphNode source, @NonNull GraphNode target) {
s.appendAttributedEdge(source, this, target);
}
@Override
public @NonNull GraphNode getSource() {
return AbstractRegion.this;
}
@Override
public @NonNull GraphNode getTarget() {
return region;
}
};
s.appendEdge(graphEdge.getSource(), graphEdge, graphEdge.getTarget());
}
for (final @NonNull NodeConnection connection : getRootConnections())
{
GraphEdge graphEdge1 = new GraphEdge() {
@Override
public void appendEdgeAttributes(@NonNull GraphStringBuilder s, @NonNull GraphNode source, @NonNull GraphNode target) {
s.appendAttributedEdge(source, this, target);
}
@Override
public @NonNull GraphNode getSource() {
return AbstractRegion.this;
}
@Override
public @NonNull GraphNode getTarget() {
return (AbstractConnection<?>)connection;
}
};
s.appendEdge(graphEdge1.getSource(), graphEdge1, graphEdge1.getTarget());
for (final @NonNull Node targetNode : connection.getTargetNodes())
{
GraphEdge graphEdge = new GraphEdge() {
@Override
public void appendEdgeAttributes(@NonNull GraphStringBuilder s, @NonNull GraphNode source, @NonNull GraphNode target) {
s.appendAttributedEdge(source, this, target);
}
@Override
public @NonNull GraphNode getSource() {
return (AbstractConnection<?>)connection;
}
@Override
public @NonNull GraphNode getTarget() {
return targetNode.getRegion();
}
};
s.appendEdge(graphEdge.getSource(), graphEdge, graphEdge.getTarget());
}
}
}
@Override
public void toGraph(@NonNull GraphStringBuilder s) {
s.setLabel(getName());
s.pushCluster();
for (@NonNull Node node : getNodes()) {
node.toGraph(s);
// s.appendNode(node);
}
for (@NonNull Edge edge : getEdges()) {
edge.toGraph(s);
// s.appendEdge(edge.getSource(), edge, edge.getTarget());
}
s.popCluster();
}
@Override
public void toRegionGraph(@NonNull GraphStringBuilder s) {
s.appendNode(this);
for (@NonNull Edge edge : getRecursionEdges()) {
s.appendEdge(edge.getSource().getRegion(), edge, edge.getTarget().getRegion());
}
}
@Override
public @NonNull String toString() {
return symbolName != null ? (symbolName/* + " - " + getName()*/) : getName();
}
public void writeDebugGraphs(@Nullable String context) {
SchedulerConstants scheduler = getSchedulerConstants();
scheduler.writeDOTfile(this, context != null ? "-" + context : null);
scheduler.writeGraphMLfile(this, context != null ? "-" + context : null);
}
}