| /******************************************************************************* |
| * Copyright (c) 2016, 2017 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; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Stream; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.CompleteClass; |
| import org.eclipse.ocl.pivot.CompleteModel; |
| import org.eclipse.ocl.pivot.DataType; |
| import org.eclipse.ocl.pivot.Model; |
| import org.eclipse.ocl.pivot.Property; |
| import org.eclipse.ocl.pivot.Type; |
| import org.eclipse.ocl.pivot.utilities.NameUtil; |
| import org.eclipse.ocl.pivot.utilities.PivotConstants; |
| import org.eclipse.ocl.pivot.utilities.PivotUtil; |
| import org.eclipse.qvtd.compiler.CompilerChainException; |
| import org.eclipse.qvtd.compiler.ProblemHandler; |
| import org.eclipse.qvtd.compiler.internal.qvtm2qvts.ContentsAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvtm2qvts.QVTm2QVTs; |
| import org.eclipse.qvtd.compiler.internal.qvtm2qvts.RegionUtil; |
| import org.eclipse.qvtd.compiler.internal.qvtm2qvts.RootMappingAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvtm2qvts.ScheduleManager; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.merger.LateConsumerMerger; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.Partitioner; |
| import org.eclipse.qvtd.pivot.qvtbase.TypedModel; |
| import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil; |
| import org.eclipse.qvtd.pivot.qvtbase.utilities.StandardLibraryHelper; |
| import org.eclipse.qvtd.pivot.qvtcore.analysis.DomainUsage; |
| import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeHelper; |
| import org.eclipse.qvtd.pivot.qvtschedule.ClassDatum; |
| import org.eclipse.qvtd.pivot.qvtschedule.DatumConnection; |
| import org.eclipse.qvtd.pivot.qvtschedule.Edge; |
| import org.eclipse.qvtd.pivot.qvtschedule.EdgeConnection; |
| import org.eclipse.qvtd.pivot.qvtschedule.LoadingRegion; |
| import org.eclipse.qvtd.pivot.qvtschedule.MappingRegion; |
| import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge; |
| import org.eclipse.qvtd.pivot.qvtschedule.Node; |
| import org.eclipse.qvtd.pivot.qvtschedule.NodeConnection; |
| import org.eclipse.qvtd.pivot.qvtschedule.QVTscheduleFactory; |
| import org.eclipse.qvtd.pivot.qvtschedule.Region; |
| import org.eclipse.qvtd.pivot.qvtschedule.ScheduledRegion; |
| import org.eclipse.qvtd.pivot.qvtschedule.impl.LoadingRegionImpl; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleConstants; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.SymbolNameBuilder; |
| |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * QVTs2QVTi supervises the serialization of a QVTs schedule as a QVTi transformation. |
| */ |
| public class QVTs2QVTs extends QVTimperativeHelper |
| { |
| protected final @NonNull ScheduleManager scheduleManager; |
| protected final @NonNull ProblemHandler problemHandler; |
| protected final @NonNull String rootName; |
| private final @NonNull LoadingRegion loadingRegion; |
| private ContentsAnalysis contentsAnalysis; |
| private final @NonNull RootMappingAnalysis rootAnalysis; |
| |
| protected final @NonNull CompleteModel completeModel; |
| private final @NonNull Map<@NonNull Region, org.eclipse.qvtd.compiler.internal.qvts2qvts.RegionAnalysis> region2regionAnalysis = new HashMap<>(); |
| |
| |
| /** |
| * The input models that may introduce model elements for transformation. |
| */ |
| private final @NonNull Map<@NonNull Model, @NonNull DomainUsage> inputModels = new HashMap<>(); |
| private final @NonNull StandardLibraryHelper standardLibraryHelper; |
| // private final @NonNull Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2predicatedEdges = new HashMap<>(); |
| // private final @NonNull Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2realizedEdges = new HashMap<>(); |
| |
| /** |
| * The per-class node connections that unite a set of sources via a shared connection. |
| */ |
| public final @NonNull Map<@NonNull ClassDatum, @NonNull Map<@NonNull Set<@NonNull Node>, @NonNull NodeConnection>> classDatum2nodes2nodeConnections = new HashMap<>(); |
| |
| /** |
| * The edge connections that unite a set of sources via a shared connection. |
| */ |
| public final @NonNull Map<@NonNull Set<@NonNull NavigableEdge>, @NonNull EdgeConnection> edges2edgeConnection = new HashMap<>(); |
| |
| public QVTs2QVTs(@NonNull ProblemHandler problemHandler, @NonNull ScheduleManager qvtm2qvts, @NonNull String rootName) { |
| super(qvtm2qvts.getEnvironmentFactory()); |
| this.scheduleManager = qvtm2qvts; |
| this.standardLibraryHelper = new StandardLibraryHelper(standardLibrary); |
| this.problemHandler = problemHandler; |
| this.rootName = rootName; |
| this.loadingRegion = createLoadingRegion(); |
| |
| this.rootAnalysis = new RootMappingAnalysis(loadingRegion); |
| this.completeModel = environmentFactory.getCompleteModel(); |
| } |
| |
| private void computeInputModels(@NonNull ScheduledRegion rootScheduledRegion) { |
| for (ClassDatumAnalysis classDatumAnalysis : scheduleManager.getClassDatumAnalyses()) { |
| DomainUsage domainUsage = classDatumAnalysis.getDomainUsage(); |
| if (domainUsage.isInput() && !domainUsage.isOutput()) { |
| Type type = classDatumAnalysis.getClassDatum().getCompleteClass().getPrimaryClass(); |
| org.eclipse.ocl.pivot.Package asPackage = PivotUtil.getContainingPackage(type); |
| if ((asPackage != null) && !PivotConstants.ORPHANAGE_URI.equals(asPackage.getURI())) { |
| Model model = PivotUtil.getContainingModel(type); |
| if (model != null) { |
| inputModels.put(model, domainUsage); |
| } |
| } |
| } |
| // if (!domainUsage.isEnforceable()) { |
| // Model model = PivotUtil.getContainingModel(classDatumAnalysis.getClassDatum().getType()); |
| // if ((model != null) && !PivotConstants.ORPHANAGE_URI.equals(model.getExternalURI())) { |
| // inputModels.put(model, domainUsage); |
| // } |
| // } |
| } |
| } |
| |
| private @NonNull NodeConnection createNodeConnection(@NonNull ScheduledRegion scheduledRegion, @NonNull Set<@NonNull Node> sourceSet, @NonNull ClassDatum classDatum, @NonNull SymbolNameBuilder s) { |
| NodeConnection connection = QVTscheduleFactory.eINSTANCE.createNodeConnection(); |
| connection.setOwningScheduledRegion(scheduledRegion); |
| connection.getSourceEnds().addAll(sourceSet); |
| connection.setName(scheduleManager.getScheduleModel().reserveSymbolName(s, connection)); |
| connection.setClassDatum(classDatum); |
| for (@NonNull Node sourceNode : sourceSet) { |
| // assert !sourceNode.isConstant(); |
| sourceNode.addOutgoingConnection(connection); |
| } |
| return connection; |
| } |
| |
| /** |
| * Create a RootContainmentRegion that introduces model elements directly from the input model root, or from |
| * composition relationships that form part of an extended metamodel that is not known until run-time. |
| */ |
| private @NonNull LoadingRegion createRootContainmentRegion(@NonNull ScheduledRegion rootScheduledRegion) { |
| loadingRegion.setOwningScheduledRegion(rootScheduledRegion); |
| if (QVTm2QVTs.DEBUG_GRAPHS.isActive()) { |
| scheduleManager.writeDebugGraphs(loadingRegion, null); |
| } |
| return loadingRegion; |
| } |
| |
| /** |
| * Create the Passed and Used Connections between all introducers and their corresponding consuming nodes. |
| */ |
| private void createConnections(@NonNull ScheduledRegion rootScheduledRegion) { |
| List<@NonNull Region> sortedCallableRegions = new ArrayList<>(); |
| Iterables.addAll(sortedCallableRegions, rootScheduledRegion.getCallableRegions()); |
| Collections.sort(sortedCallableRegions, NameUtil.NAMEABLE_COMPARATOR); |
| for (Region region : sortedCallableRegions) { |
| createIncomingConnections(region); |
| } |
| if (QVTm2QVTs.DEBUG_GRAPHS.isActive()) { |
| scheduleManager.writeDebugGraphs(rootScheduledRegion, "4-bindings", true, true, false); |
| } |
| // for (Region region : sortedCallableRegions) { |
| // region.checkIncomingConnections(); |
| // } |
| } |
| |
| /** |
| * 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 Region region, @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 = region.getOwningScheduledRegion(); |
| assert invokingRegion2 != null; |
| NavigableEdge castEdge = QVTscheduleUtil.getCastTarget(predicatedEdge); |
| Node castTarget = QVTscheduleUtil.getCastTarget(castEdge.getEdgeTarget()); |
| ClassDatumAnalysis classDatumAnalysis = RegionUtil.getClassDatumAnalysis(castTarget); |
| if (classDatumAnalysis.getClassDatum().getCompleteClass().getPrimaryClass() instanceof DataType) { |
| Iterable<@NonNull NavigableEdge> realizedEdges = getNewEdges(predicatedEdge, classDatumAnalysis); |
| if (realizedEdges != null) { |
| List<@NonNull Node> sourceNodes = new ArrayList<>(); |
| for (@NonNull NavigableEdge realizedEdge : realizedEdges) { |
| if (RegionUtil.isElementallyConformantSource(realizedEdge, predicatedEdge) && QVTscheduleUtil.isConformantTarget(realizedEdge, predicatedEdge)) { |
| sourceNodes.add(realizedEdge.getEdgeTarget()); |
| } |
| } |
| NodeConnection nodeConnection = getAttributeConnection(invokingRegion2, sourceNodes, predicatedEdge.getEdgeSource().getCompleteClass(), predicatedProperty, classDatumAnalysis.getClassDatum()); |
| nodeConnection.addUsedTargetNode(castTarget, false); |
| if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) { |
| QVTscheduleConstants.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 = getNewNodes(classDatumAnalysis); |
| // if (sourceNodes != null) { |
| Iterable<@NonNull NavigableEdge> realizedEdges = getNewEdges(predicatedEdge, classDatumAnalysis); |
| if (realizedEdges != null) { |
| Set<@NonNull Region> edgeSourceRegions = new HashSet<>(); |
| Set<@NonNull Region> nodeSourceRegions = new HashSet<>(); |
| for (@NonNull NavigableEdge realizedEdge : realizedEdges) { |
| edgeSourceRegions.add(QVTscheduleUtil.getOwningRegion(realizedEdge)); |
| } |
| if (sourceNodes != null) { |
| for (@NonNull Node sourceNode : sourceNodes) { |
| nodeSourceRegions.add(QVTscheduleUtil.getOwningRegion(sourceNode)); |
| } |
| } |
| // |
| // 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) && QVTscheduleUtil.isConformantTarget(realizedEdge, predicatedEdge)) { |
| if (thoseEdges == null) { |
| thoseEdges = new ArrayList<>(); |
| conformantEdgeSourceRegions = new HashSet<>(); |
| } |
| if (!thoseEdges.contains(realizedEdge)) { |
| thoseEdges.add(realizedEdge); |
| assert conformantEdgeSourceRegions != null; |
| conformantEdgeSourceRegions.add(QVTscheduleUtil.getOwningRegion(realizedEdge)); |
| } |
| } |
| } |
| if ((thoseEdges != null) && !nodeSourceRegions.containsAll(conformantEdgeSourceRegions)) { |
| EdgeConnection edgeConnection = getEdgeConnection(invokingRegion2, thoseEdges, predicatedProperty); |
| if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) { |
| QVTscheduleConstants.CONNECTION_CREATION.println(" EdgeConnection \"" + edgeConnection + "\" to " + predicatedEdge); |
| } |
| if (!Iterables.contains(edgeConnection.getTargetEdges(), castEdge)) { |
| edgeConnection.addUsedTargetEdge(castEdge, false); |
| if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) { |
| for (@NonNull NavigableEdge thatEdge : thoseEdges) { |
| QVTscheduleConstants.CONNECTION_CREATION.println(" from " + thatEdge.getOwningRegion() + " : " + 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 = getNodeConnection(invokingRegion2, sourceNodes, classDatumAnalysis.getClassDatum(), classDatumAnalysis.getDomainUsage()); |
| predicatedConnection.addUsedTargetNode(castTarget, false); |
| if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) { |
| QVTscheduleConstants.CONNECTION_CREATION.println(" NodeConnection \"" + predicatedConnection + "\" to " + castTarget); |
| for (@NonNull Node sourceNode : sourceNodes) { |
| QVTscheduleConstants.CONNECTION_CREATION.println(" from " + sourceNode.getOwningRegion() + " : " + sourceNode); |
| } |
| } |
| } |
| // } |
| } |
| } |
| |
| private @NonNull EdgeConnection createEdgeConnection(@NonNull ScheduledRegion scheduledRegion, @NonNull Set<@NonNull NavigableEdge> sourceSet, @NonNull Property property, @NonNull SymbolNameBuilder s) { |
| assert !property.isIsImplicit(); |
| EdgeConnection connection = QVTscheduleFactory.eINSTANCE.createEdgeConnection(); |
| |
| // protected DatumConnectionImpl(@NonNull ScheduledRegion region, @NonNull Set<@NonNull CE> sourceEnds, @NonNull String name) { |
| connection.setOwningScheduledRegion(scheduledRegion); |
| connection.setName(scheduleManager.getScheduleModel().reserveSymbolName(s, connection)); |
| connection.getSourceEnds().addAll(sourceSet); |
| // } |
| |
| // public EdgeConnectionImpl(@NonNull ScheduledRegion region, @NonNull Set<@NonNull NavigableEdge> sourceEdges, @NonNull String name, @NonNull Property property) { |
| // super(region, sourceEdges, name); |
| connection.setReferredProperty(property); |
| for (@NonNull NavigableEdge sourceEdge : sourceSet) { |
| sourceEdge.addOutgoingConnection(connection); |
| } |
| // } |
| // return new EdgeConnectionImpl(this, sourceSet, s, property); |
| return connection; |
| } |
| |
| /** |
| * 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 Region region, @NonNull Node headNode) { |
| ScheduledRegion invokingRegion2 = RegionUtil.getOwningScheduledRegion(region); |
| ClassDatumAnalysis classDatumAnalysis = RegionUtil.getClassDatumAnalysis(headNode); |
| List<@NonNull Node> headSources = null; |
| // |
| // Locate compatible introducers and non-recursive producers |
| // |
| Iterable<@NonNull Node> sourceNodes = getIntroducingOrNewNodes(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(region, headNode, sourceNode, called2calling)) { |
| if (headSources == null) { |
| headSources = new ArrayList<>(); |
| } |
| headSources.add(sourceNode); |
| } |
| // } |
| } |
| } |
| if (headSources == null) { |
| return null; |
| } |
| // |
| // Connect up the head |
| // |
| NodeConnection headConnection = getNodeConnection(invokingRegion2, headSources, classDatumAnalysis.getClassDatum(), classDatumAnalysis.getDomainUsage()); |
| if (headNode.isDependency()) { |
| headConnection.addUsedTargetNode(headNode, false); |
| } |
| else { |
| headConnection.addPassedTargetNode(headNode); |
| } |
| if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) { |
| QVTscheduleConstants.CONNECTION_CREATION.println((headNode.isDependency() ? " Extra NodeConnection " : " Head NodeConnection \"") + headConnection + "\" to " + headNode); |
| for (@NonNull Node sourceNode : headSources) { |
| QVTscheduleConstants.CONNECTION_CREATION.println(" from " + sourceNode.getOwningRegion() + " : " + 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(@NonNull Region region) { |
| List<@NonNull NodeConnection> headConnections = null; |
| for (@NonNull Node headNode : QVTscheduleUtil.getHeadNodes(region)) { |
| if (headNode.isTrue()) { /* true nodes do not need connections. */ } |
| else if (headNode.isDependency()) { |
| createHeadConnection(region, headNode); /** Dependency nodes have extra not-head connections. */ |
| } |
| else { |
| NodeConnection headConnection = createHeadConnection(region, headNode); |
| if (headConnection == null) { |
| scheduleManager.addRegionError(region, "createHeadConnections abandoned for " + headNode); |
| scheduleManager.addRegionError(region, "createHeadConnections abandoned for " + headNode); |
| headConnection = createHeadConnection(region, 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. |
| */ |
| public void createIncomingConnections(@NonNull Region region) { |
| if (QVTscheduleConstants.CONNECTION_CREATION.isActive()) { |
| QVTscheduleConstants.CONNECTION_CREATION.println("connecting " + region); |
| } |
| assert !(this instanceof LoadingRegion); |
| // assert !"LoadingRegion".equals(region.eClass().getName()); |
| Iterable<@NonNull NodeConnection> headConnections = createHeadConnections(region); |
| if (headConnections != null) { |
| for (@NonNull NavigableEdge predicatedEdge : region.getPredicatedNavigationEdges()) { |
| if (!predicatedEdge.isCast()) { |
| createEdgeConnection(region, predicatedEdge); |
| } |
| } |
| } |
| } |
| |
| protected @NonNull LoadingRegion createLoadingRegion() { |
| LoadingRegion loadingRegion = QVTscheduleFactory.eINSTANCE.createLoadingRegion(); |
| SymbolNameBuilder s = new SymbolNameBuilder(); |
| s.appendString(QVTscheduleUtil.ROOT_MAPPING_NAME); |
| loadingRegion.setSymbolName(scheduleManager.getScheduleModel().reserveSymbolName(s, loadingRegion)); |
| return loadingRegion; |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| } |
| } |
| } */ |
| |
| private void createLocalSchedule(@NonNull ScheduledRegion scheduledRegion) { |
| // |
| // Partition single region recursive connections into base cases recursive case connections with associated region recursions. |
| // |
| // splitConnectionVariables(); |
| // |
| if (QVTm2QVTs.DEBUG_GRAPHS.isActive()) { |
| scheduleManager.writeDebugGraphs(scheduledRegion, "5-cycled", true, true, false); |
| } |
| } |
| |
| private void createLocalSchedule2(@NonNull ScheduledRegion scheduledRegion, @NonNull List<@NonNull Region> orderedRegions) { |
| // region2order.computeRegionIndexes(getCallableRegions()); |
| // Iterable<Region> sortedCallableRegions = regionOrdering;//AbstractRegion.EarliestRegionComparator.sort(getCallableRegions()); |
| // |
| // Index all predicated and realized edges by typed model and property. |
| // |
| Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2predicatedEdges = new HashMap<>(); |
| Map<@NonNull TypedModel, @NonNull Map<@NonNull Property, @NonNull List<@NonNull NavigableEdge>>> typedModel2property2realizedEdges = new HashMap<>(); |
| for (@NonNull Region region : orderedRegions) { |
| QVTscheduleConstants.POLLED_PROPERTIES.println("building indexes for " + region + " " + region.getIndexRangeText()); |
| RegionAnalysis regionAnalysis = RegionAnalysis.get(region); |
| regionAnalysis.buildNavigationEdgesIndex(typedModel2property2predicatedEdges, typedModel2property2realizedEdges); |
| } |
| // |
| // Eliminate dependencies that are satisfied by the linear invocation indexes. |
| // |
| for (@NonNull Region region : orderedRegions) { |
| // int earliestPassedConnectionSourceIndex = region.getEarliestPassedConnectionSourceIndex(); |
| int earliestIndex = region.getIndexes().get(0); |
| List<@NonNull DatumConnection<?>> redundantConnections = null; |
| for (@NonNull DatumConnection<?> usedConnection : region.getIncomingConnections()) { |
| if (!usedConnection.isPassed(region)) { |
| boolean isRedundant = true; |
| for (@NonNull Region sourceRegion : usedConnection.getSourceRegions()) { |
| List<@NonNull Integer> indexes = sourceRegion.getIndexes(); |
| int lastIndex = indexes.get(indexes.size()-1); |
| if (lastIndex >= earliestIndex) { |
| isRedundant = false; |
| break; |
| } |
| } |
| if (isRedundant) { |
| if (redundantConnections == null) { |
| redundantConnections = new ArrayList<>(); |
| } |
| redundantConnections.add(usedConnection); |
| } |
| } |
| } |
| if (redundantConnections != null) { |
| for (@NonNull DatumConnection<?> redundantConnection : redundantConnections) { |
| redundantConnection.removeTargetRegion(region); |
| } |
| } |
| } |
| if (QVTm2QVTs.DEBUG_GRAPHS.isActive()) { |
| scheduleManager.writeDebugGraphs(scheduledRegion, "7-pruned", true, true, false); |
| } |
| |
| boolean noLateConsumerMerge = scheduleManager.isNoLateConsumerMerge(); |
| if (!noLateConsumerMerge) { |
| lateMerge(scheduledRegion, orderedRegions, typedModel2property2predicatedEdges, typedModel2property2realizedEdges); |
| } |
| |
| for (@NonNull Region region : orderedRegions) { |
| getRegionAnalysis(region).computeCheckedOrEnforcedEdges(typedModel2property2predicatedEdges, typedModel2property2realizedEdges); |
| } |
| /* suspended - just an optimization - needs more hierarchical consideration |
| // |
| // Redirect ordered consumers to depend on each other's heads thereby respecting the ordering and |
| // making earlier results available to later mappings. |
| // |
| if (region2orderingEdge2usedEdges != null) { |
| for (@SuppressWarnings("null")@NonNull Region commonRegion : region2orderingEdge2usedEdges.keySet()) { |
| @SuppressWarnings("null")@NonNull Map<Edge, Set<Edge>> orderingEdge2usedEdges = region2orderingEdge2usedEdges.get(commonRegion); |
| convertConsumedOrdering(commonRegion, orderingEdge2usedEdges); |
| } |
| } |
| writeDOTfile("-5-reconsume"); |
| writeGraphMLfile("-5-reconsume"); */ |
| /* suspended - just an optimization - needs more hierarchical consideration |
| // |
| // Merge again now that dependencies may be available. |
| // |
| Region2Depth region2depths = new Region2Depth(); |
| List<Region> sortedRegions = region2depths.getSortedRegions(getRegions()); |
| for (Region calledRegion : sortedRegions) { |
| List<List<Node>> headNodeGroups = calledRegion.getHeadNodeGroups(); |
| if ((headNodeGroups.size() == 1) && !(calledRegion instanceof LoadingRegion)) { |
| Region callingRegion = null; |
| boolean isMergeable = true; |
| for (Node headNode : headNodeGroups.get(0)) { |
| for (Node callingNode : headNode.getPassedBindingSources()) { |
| if (callingRegion == null) { |
| callingRegion = callingNode.getRegion(); |
| } |
| else if (callingRegion != callingNode.getRegion()) { |
| isMergeable = false; |
| break; |
| } |
| } |
| } |
| if ((callingRegion != null) && isMergeable && callingRegion.isLateMergeable(calledRegion, region2depths)) { |
| Map<Node, Node> node2mergedNode = callingRegion.canMerge(calledRegion, region2depths, true); |
| if (node2mergedNode != null) { |
| MergedRegion mergedRegion; |
| if (callingRegion instanceof MergedRegion) { |
| mergedRegion = (MergedRegion)callingRegion; |
| } |
| else { |
| getRegions().remove(calledRegion); |
| mergedRegion = new MergedRegion((MergeableRegion)callingRegion); |
| Region invokingRegion = callingRegion.getInvokingRegion(); |
| assert invokingRegion != null; |
| List<Region> regions = invokingRegion.getRegions(); |
| int index = regions.indexOf(callingRegion); |
| assert index >= 0; |
| regions.set(index, mergedRegion); |
| // mergedRegion.writeDOTfile("-6-merged"); |
| // mergedRegion.writeGraphMLfile("-6-merged"); |
| } |
| mergedRegion.mergeRegion(calledRegion, node2mergedNode); |
| getRegions().remove(calledRegion); |
| mergedRegion.writeDOTfile("-6-merged"); |
| mergedRegion.writeGraphMLfile("-6-merged"); |
| mergedRegion.resolveRecursion(); |
| mergedRegion.writeDOTfile("-7-merged"); |
| mergedRegion.writeGraphMLfile("-7-merged"); |
| region2depths.addRegion(mergedRegion); |
| } |
| } |
| } |
| } */ |
| // |
| // Propagate early results down to later mappings that need them. |
| // |
| /* for (Region calledRegion : sortedRegions) { |
| calledRegion.refineBindings(this); |
| } */ |
| /* HashMap<Node, List<Node>> outerNode2outerNodes = new HashMap<>(); |
| Map<Region, Map<NavigationEdge, NavigationEdge>> region2innerEdge2outerEdge = new HashMap<>(); |
| propagateCommonNavigations(rootContainmentRegion, outerNode2outerNodes, region2innerEdge2outerEdge); |
| for (@SuppressWarnings("null")@NonNull Map.Entry<Region, Map<NavigationEdge, NavigationEdge>> entry1 : region2innerEdge2outerEdge.entrySet()) { |
| Region innerRegion = entry1.getKey(); |
| for (@SuppressWarnings("null")@NonNull NavigationEdge innerEdge : entry1.getValue().keySet()) { |
| Node innerNode = innerEdge.getTarget(); |
| List<NavigationEdge> bestPath = null; |
| for (@SuppressWarnings("null")@NonNull List<Node> headGroup : innerRegion.getHeadNodeGroups()) { |
| for (@SuppressWarnings("null")@NonNull Node headNode : headGroup) { |
| bestPath = getBestPath(bestPath, getPath(headNode, innerNode, new HashSet<>())); |
| } |
| } |
| assert bestPath != null; |
| for (@SuppressWarnings("null")@NonNull Node node : innerRegion.getNodes()) { |
| for (@SuppressWarnings("null")@NonNull Edge edge : node.getIncomingPassedBindingEdges()) { // ??? joins |
| assert edge.getTarget() == node; |
| Region outerRegion = edge.getSource().getRegion(); |
| Map<Edge, Edge> innerEdge2outerEdge = createPath(edge.getSource(), bestPath); |
| for (@SuppressWarnings("null")@NonNull Map.Entry<Edge, Edge> entry : innerEdge2outerEdge.entrySet()) { |
| Edge outerEdge = entry.getValue(); |
| Edge innerEdge2 = entry.getKey(); |
| Edges.USED_BINDING.createEdge(outerRegion, outerEdge.getTarget(), innerEdge2.getName(), innerEdge2.getTarget()); |
| } |
| // innerNode2outerNode.put(node, edge.getSource()); |
| // propagateSharedNodes(edge.getSource(), node, innerNode2outerNode); |
| |
| |
| |
| |
| // propagatePassedEdges(edge.getSource(), node, innerNode2outerNode, innerNode2edge); |
| } |
| } |
| } */ |
| /* Map<Node, Edge> innerNode2edge = new HashMap<>(); |
| Map<Node, Node> innerNode2outerNode = new HashMap<>(); |
| // for (NavigationEdge innerEdge : entry1.getValue().keySet()) { |
| // innerNode2edge.put(innerEdge.getSource(), innerEdge); |
| // } |
| for (Node node : innerRegion.getNodes()) { |
| for (Edge edge : node.getIncomingPassedBindingEdges()) { // ??? joins |
| assert edge.getTarget() == node; |
| |
| Node outerNode = createPath(edge.getRegion(), bestEdge); |
| |
| innerNode2outerNode.put(node, edge.getSource()); |
| propagateSharedNodes(edge.getSource(), node, innerNode2outerNode); |
| |
| |
| |
| |
| propagatePassedEdges(edge.getSource(), node, innerNode2outerNode, innerNode2edge); |
| } |
| } |
| for (Map.Entry<NavigationEdge, NavigationEdge> entry2 : entry1.getValue().entrySet()) { |
| NavigationEdge innerEdge = entry2.getKey(); |
| NavigationEdge outerEdge = entry2.getValue(); |
| propagateEdge(outerEdge.getSource(), innerEdge.getSource()); |
| propagateEdge(outerEdge.getTarget(), innerEdge.getTarget()); |
| } */ |
| // } |
| // firstPassRegion.writeDOTfile(); |
| // firstPassRegion.writeGraphMLfile(); |
| // |
| // if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) { |
| // writeDebugGraphs("9-final", true, true, true); |
| // } |
| } |
| |
| public @NonNull ScheduledRegion createRootRegion(@NonNull Iterable<@NonNull ? extends Region> allRegions) { |
| ScheduledRegion rootRegion = null; |
| for (@NonNull Region region : Lists.newArrayList(allRegions)) { |
| if (region.getOwningScheduledRegion() == null) { |
| if (rootRegion == null) { |
| rootRegion = QVTscheduleFactory.eINSTANCE.createScheduledRegion(); |
| rootRegion.setOwningScheduleModel(scheduleManager.getScheduleModel()); |
| rootRegion.setName(rootName); |
| } |
| region.setOwningScheduledRegion(rootRegion); |
| } |
| } |
| assert rootRegion != null; |
| return rootRegion; |
| } |
| |
| public void createSchedule1(@NonNull ScheduledRegion rootScheduledRegion) { |
| // |
| // Identify the input models. |
| // |
| computeInputModels(rootScheduledRegion); |
| if (QVTm2QVTs.DUMP_INPUT_MODEL_TO_DOMAIN_USAGE.isActive()) { |
| QVTm2QVTs.DUMP_INPUT_MODEL_TO_DOMAIN_USAGE.println(dumpInputModels().reduce("", QVTscheduleUtil.stringJoin("\n\t"))); |
| } |
| // |
| // Identify the content of each region. |
| // |
| for (@NonNull Region region : RegionUtil.getOwnedRegions(rootScheduledRegion)) { |
| contentsAnalysis.addRegion(region); |
| } |
| if (QVTm2QVTs.DUMP_CLASS_TO_REALIZED_NODES.isActive()) { |
| QVTm2QVTs.DUMP_CLASS_TO_REALIZED_NODES.println(contentsAnalysis.dumpClass2newNode().reduce("", QVTscheduleUtil.stringJoin("\n\t"))); |
| } |
| if (QVTm2QVTs.DUMP_CLASS_TO_CONSUMING_NODES.isActive()) { |
| QVTm2QVTs.DUMP_CLASS_TO_CONSUMING_NODES.println(contentsAnalysis.dumpClass2oldNode().reduce("", QVTscheduleUtil.stringJoin("\n\t"))); |
| } |
| // |
| // Create the root containment region to introduce all root and otherwise contained consumed classes. |
| // |
| createRootContainmentRegion(rootScheduledRegion); |
| // |
| // Create a connection between each consumer and the corresponding introducer/producer. |
| // |
| createConnections(rootScheduledRegion); |
| // createSchedule2(); |
| } |
| |
| protected void createSchedule2(@NonNull ScheduledRegion rootScheduledRegion) { |
| // |
| // Replace multi-region recursions by single nested region recursions. |
| // |
| List<@NonNull ScheduledRegion> allScheduledRegions = new ArrayList<>(); |
| allScheduledRegions.add(rootScheduledRegion); |
| // CyclesAnalyzer cyclesAnalyzer = new CyclesAnalyzer(rootScheduledRegion, rootScheduledRegion.getCallableRegions()); |
| // List<@NonNull RegionCycle> regionCycles = cyclesAnalyzer.getOrderedCycles(); |
| // if (regionCycles != null) { |
| /* for (@NonNull RegionCycle regionCycle : regionCycles) { |
| Iterable<@NonNull Region> regions = regionCycle.getRegions(); |
| if (Iterables.size(regions) == 1) { |
| regions.iterator().next().setIsCyclic(); |
| } |
| else { |
| CyclicScheduledRegion cyclicRegion = createCyclicRegion(regions); |
| allScheduledRegions.add(cyclicRegion); |
| } |
| } */ |
| // } |
| // if (QVTp2QVTs.DEBUG_GRAPHS.isActive()) { |
| // rootScheduledRegion.writeDebugGraphs("4-cycles", true, true, false); |
| // } |
| // |
| // Create the schedule for each directed acyclic scheduled region. |
| // |
| /* for (@NonNull Region region : rootScheduledRegion.getCallableRegions()) { |
| Splitter splitter = new Splitter(region); |
| Split split = splitter.split(); |
| if (split != null) { |
| // split.install(); |
| } |
| } */ |
| // |
| // Create the schedule for each directed acyclic scheduled region. |
| // |
| for (@NonNull ScheduledRegion scheduledRegion : allScheduledRegions) { |
| createLocalSchedule(scheduledRegion); |
| } |
| ScheduleIndexer scheduleIndexer = new ScheduleIndexer(rootScheduledRegion); |
| scheduleIndexer.schedule(rootScheduledRegion); |
| for (@NonNull ScheduledRegion scheduledRegion : allScheduledRegions) { |
| createLocalSchedule2(scheduledRegion, scheduleIndexer.getOrdering()); |
| } |
| } |
| |
| private Stream<String> dumpInputModels() { |
| Stream<String> entries = inputModels.keySet().stream().map( |
| k -> String.valueOf(k) + " : " + String.valueOf(inputModels.get(k))); |
| return entries.sorted(); |
| } |
| |
| public @NonNull NodeConnection getAttributeConnection(@NonNull ScheduledRegion scheduledRegion, @NonNull Iterable<@NonNull Node> sourceNodes, @NonNull CompleteClass owningClass, @NonNull Property property, @NonNull ClassDatum classDatum) { |
| Map<@NonNull Set<@NonNull Node>, @NonNull NodeConnection> nodes2connection = classDatum2nodes2nodeConnections.get(classDatum); |
| if (nodes2connection == null) { |
| nodes2connection = new HashMap<>(); |
| classDatum2nodes2nodeConnections.put(classDatum, nodes2connection); |
| } |
| Set<@NonNull Node> sourceSet = Sets.newHashSet(sourceNodes); |
| NodeConnection connection = nodes2connection.get(sourceSet); |
| if (connection == null) { |
| SymbolNameBuilder s = new SymbolNameBuilder(); |
| s.appendString("ja_"); |
| s.appendName(owningClass.getName()); |
| s.appendString("_"); |
| s.appendName(property.getName()); |
| connection = createNodeConnection(scheduledRegion, sourceSet, classDatum, s); |
| nodes2connection.put(sourceSet, connection); |
| } |
| return connection; |
| } |
| |
| private @NonNull EdgeConnection getEdgeConnection(@NonNull ScheduledRegion scheduledRegion, @NonNull Iterable<@NonNull NavigableEdge> sourceEdges, @NonNull Property property) { |
| Set<@NonNull NavigableEdge> sourceSet = Sets.newHashSet(sourceEdges); |
| EdgeConnection connection = edges2edgeConnection.get(sourceSet); |
| if (connection == null) { |
| SymbolNameBuilder s = new SymbolNameBuilder(); |
| s.appendString("je_"); |
| s.appendName(property.getOwningClass().getName()); |
| s.appendString("_"); |
| s.appendName(property.getName()); |
| connection = createEdgeConnection(scheduledRegion, sourceSet, property, s); |
| edges2edgeConnection.put(sourceSet, connection); |
| } |
| return connection; |
| } |
| |
| public @Nullable Iterable<@NonNull Node> getIntroducingOrNewNodes(@NonNull Node headNode) { |
| ClassDatumAnalysis classDatumAnalysis = RegionUtil.getClassDatumAnalysis(headNode); |
| if (!classDatumAnalysis.getDomainUsage().isInput()) { |
| return contentsAnalysis.getNewNodes(classDatumAnalysis); // FIXME also dependsOn ?? |
| } |
| List<@NonNull Node> nodes = new ArrayList<>(); |
| nodes.add(rootAnalysis.getIntroducerNode(headNode)); |
| for (@NonNull TypedModel dependsOn : QVTbaseUtil.getDependsOns(RegionUtil.getTypedModel(classDatumAnalysis))) { |
| ClassDatumAnalysis classDatumAnalysis2 = scheduleManager.getClassDatumAnalysis(headNode.getCompleteClass().getPrimaryClass(), dependsOn); |
| Iterable<@NonNull Node> newNodes = contentsAnalysis.getNewNodes(classDatumAnalysis2); |
| if (newNodes != null) { |
| for (@NonNull Node newNode : newNodes) { |
| if (!nodes.contains(newNode)) { |
| nodes.add(newNode); |
| } |
| } |
| } |
| } |
| return nodes; |
| } |
| |
| public @Nullable Iterable<@NonNull NavigableEdge> getNewEdges(@NonNull NavigableEdge edge, @NonNull ClassDatumAnalysis requiredClassDatumAnalysis) { |
| return contentsAnalysis.getNewEdges(edge, requiredClassDatumAnalysis); |
| } |
| |
| public @Nullable Iterable<@NonNull Node> getNewNodes(@NonNull ClassDatumAnalysis classDatumAnalysis) { |
| return contentsAnalysis.getNewNodes(classDatumAnalysis); |
| } |
| |
| private @NonNull NodeConnection getNodeConnection(@NonNull ScheduledRegion scheduledRegion, @NonNull Iterable<@NonNull Node> sourceNodes, @NonNull ClassDatum classDatum, @NonNull DomainUsage domainUsage) { |
| Map<@NonNull Set<@NonNull Node>, @NonNull NodeConnection> nodes2connection = classDatum2nodes2nodeConnections.get(classDatum); |
| if (nodes2connection == null) { |
| nodes2connection = new HashMap<>(); |
| classDatum2nodes2nodeConnections.put(classDatum, nodes2connection); |
| } |
| Set<@NonNull Node> sourceSet = Sets.newHashSet(sourceNodes); |
| NodeConnection connection = nodes2connection.get(sourceSet); |
| if (connection == null) { |
| SymbolNameBuilder s = new SymbolNameBuilder(); |
| s.appendString("j"); |
| s.appendString(domainUsage.isInput() ? "i" : domainUsage.isOutput() ? "o" : "m"); |
| s.appendString("_"); |
| s.appendName(classDatum.getCompleteClass().getName()); |
| connection = createNodeConnection(scheduledRegion, sourceSet, classDatum, s); |
| nodes2connection.put(sourceSet, connection); |
| } |
| return connection; |
| } |
| |
| public @NonNull RegionAnalysis getRegionAnalysis(@NonNull Region region) { |
| RegionAnalysis regionAnalysis = region2regionAnalysis.get(region); |
| if (regionAnalysis == null) { |
| regionAnalysis = RegionAnalysis.get(/*this,*/ region); |
| region2regionAnalysis.put(region, regionAnalysis); |
| } |
| return regionAnalysis; |
| } |
| |
| public @NonNull StandardLibraryHelper getStandardLibraryHelper() { |
| return standardLibraryHelper ; |
| } |
| |
| /** |
| * 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 Region region, @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.getEdgeTarget(); |
| if (!nextCalledNode.isRealized() && !nextCalledNode.isDataType()) { // FIXME why exclude AttributeNodes? |
| Edge nextCallingEdge = callingNode.getNavigationEdge(QVTscheduleUtil.getProperty(calledEdge)); |
| if (nextCallingEdge != null) { |
| Node nextCallingNode = nextCallingEdge.getEdgeTarget(); |
| if ((nextCallingNode.isExplicitNull() != nextCalledNode.isExplicitNull())) { |
| return false; |
| } |
| if (!isCompatiblePattern(region, nextCalledNode, nextCallingNode, called2calling)) { |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| private void lateMerge(@NonNull ScheduledRegion scheduledRegion, @NonNull List<@NonNull Region> orderedRegions, |
| @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) { |
| Map<@NonNull Region, @NonNull List<@NonNull Region>> newRegion2oldRegions = LateConsumerMerger.merge(scheduledRegion); |
| for (Map.Entry<@NonNull Region, @NonNull List<@NonNull Region>> entry : newRegion2oldRegions.entrySet()) { |
| Region newRegion = entry.getKey(); |
| List<@NonNull Region> oldRegions = entry.getValue(); |
| assert oldRegions.size() >= 2; |
| int orderedRegionIndex = orderedRegions.indexOf(oldRegions.get(0)); |
| for (@NonNull Region oldRegion : oldRegions) { |
| orderedRegions.remove(oldRegion); |
| oldRegion.setOwningScheduledRegion(null); |
| } |
| orderedRegions.add(orderedRegionIndex, newRegion); |
| QVTscheduleConstants.POLLED_PROPERTIES.println("building indexes for " + newRegion + " " + newRegion.getIndexRangeText()); |
| RegionAnalysis regionAnalysis = RegionAnalysis.get(newRegion); |
| regionAnalysis.buildNavigationEdgesIndex(typedModel2property2predicatedEdges, typedModel2property2realizedEdges); |
| } |
| } |
| |
| protected void splitRegions() { |
| } |
| |
| public @NonNull ScheduledRegion transform(@NonNull ScheduleManager scheduleManager, @NonNull Iterable<@NonNull ? extends Region> activeRegions) throws CompilerChainException { |
| this.contentsAnalysis = new ContentsAnalysis(scheduleManager); |
| ((LoadingRegionImpl)loadingRegion).setFixmeScheduleModel(scheduleManager.getScheduleModel()); |
| // for (@NonNull Region region : activeRegions) { |
| // System.out.println("activeRegions " + region); |
| // } |
| Iterable<@NonNull MappingRegion> partitionedRegions = Partitioner.partition(problemHandler, activeRegions); |
| if (!Iterables.isEmpty(partitionedRegions)) { |
| // for (@NonNull Region region : partitionedRegions) { |
| // System.out.println("partitionedRegions " + region); |
| // } |
| activeRegions = partitionedRegions; |
| } |
| ScheduledRegion rootRegion = createRootRegion(activeRegions); |
| createSchedule1(rootRegion); |
| createSchedule2(rootRegion); |
| if (QVTm2QVTs.DEBUG_GRAPHS.isActive()) { |
| scheduleManager.writeDebugGraphs(rootRegion, "9-final", true, true, true); |
| } |
| return rootRegion; |
| } |
| } |