private bool ChangeEdgeTailNodeWithoutCleanup(T edgeId, T newTailNodeId) { // Do not attend this unless all dependencies are satisfied. if (!AllDependenciesSatisfied) { return(false); } // Retrieve the activity edge. if (!EdgeLookup.TryGetValue(edgeId, out Edge <T, TEdgeContent> _)) { return(false); } // Retrieve the new tail event node. if (!NodeLookup.TryGetValue(newTailNodeId, out Node <T, TNodeContent> newTailNode)) { return(false); } // Remove the connection from the current tail node. Node <T, TNodeContent> oldTailNode = EdgeTailNodeLookup[edgeId]; oldTailNode.OutgoingEdges.Remove(edgeId); EdgeTailNodeLookup.Remove(edgeId); // Attach to the new tail node. newTailNode.OutgoingEdges.Add(edgeId); EdgeTailNodeLookup.Add(edgeId, newTailNode); return(true); }
private void ResolveUnsatisfiedSuccessorActivities(T activityId) { // Check to make sure the node really exists. if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> dependencyNode)) { return; } // Check to see if any existing activities were expecting this activity // as a dependency. If so, then then hook their nodes to this activity with an edge. if (UnsatisfiedSuccessorsLookup.TryGetValue(activityId, out HashSet <Node <T, TActivity> > unsatisfiedSuccessorNodes)) { // If the dependency node is an End or Isolated node, then convert it. if (dependencyNode.NodeType == NodeType.End) { dependencyNode.SetNodeType(NodeType.Normal); } else if (dependencyNode.NodeType == NodeType.Isolated) { dependencyNode.SetNodeType(NodeType.Start); } foreach (Node <T, TActivity> successorNode in unsatisfiedSuccessorNodes) { T edgeId = EdgeIdGenerator(); var edge = new Edge <T, TEvent>(EventGenerator(edgeId)); dependencyNode.OutgoingEdges.Add(edgeId); EdgeTailNodeLookup.Add(edgeId, dependencyNode); successorNode.IncomingEdges.Add(edgeId); EdgeHeadNodeLookup.Add(edgeId, successorNode); EdgeLookup.Add(edgeId, edge); } UnsatisfiedSuccessorsLookup.Remove(activityId); } }
protected GraphBuilderBase( Graph <T, TEdgeContent, TNodeContent> arrowGraph, Func <T> edgeIdGenerator, Func <T> nodeIdGenerator, Func <T, TEvent> eventGenerator) : this(edgeIdGenerator, nodeIdGenerator, eventGenerator) { if (arrowGraph is null) { throw new ArgumentNullException(nameof(arrowGraph)); } foreach (Edge <T, TEdgeContent> edge in arrowGraph.Edges) { EdgeLookup.Add(edge.Id, edge); } foreach (Node <T, TNodeContent> node in arrowGraph.Nodes) { // Assimilate incoming edges. if (node.NodeType != NodeType.Start && node.NodeType != NodeType.Isolated) { foreach (T edgeId in node.IncomingEdges) { EdgeHeadNodeLookup.Add(edgeId, node); } } // Assimilate Outgoing edges. if (node.NodeType != NodeType.End && node.NodeType != NodeType.Isolated) { foreach (T edgeId in node.OutgoingEdges) { EdgeTailNodeLookup.Add(edgeId, node); } } NodeLookup.Add(node.Id, node); } // Check all edges are used. if (!EdgeLookup.Keys.OrderBy(x => x).SequenceEqual(EdgeHeadNodeLookup.Keys.OrderBy(x => x))) { throw new ArgumentException(Properties.Resources.ListOfEdgeIdsAndEdgesReferencedByHeadNodesDoNotMatch); } if (!EdgeLookup.Keys.OrderBy(x => x).SequenceEqual(EdgeTailNodeLookup.Keys.OrderBy(x => x))) { throw new ArgumentException(Properties.Resources.ListOfEdgeIdsAndEdgesReferencedByTailNodesDoNotMatch); } // Check all nodes are used. IEnumerable <T> edgeNodeLookupIds = EdgeHeadNodeLookup.Values.Select(x => x.Id).Union(EdgeTailNodeLookup.Values.Select(x => x.Id)); if (!NodeLookup.Values.Where(x => x.NodeType != NodeType.Isolated).Select(x => x.Id).OrderBy(x => x).SequenceEqual(edgeNodeLookupIds.OrderBy(x => x))) { throw new ArgumentException(Properties.Resources.ListOfNodeIdsAndEdgesReferencedByTailNodesDoNotMatch); } }
private void ResolveUnsatisfiedSuccessorActivities(T activityId) { // Check to make sure the edge really exists. if (!EdgeLookup.ContainsKey(activityId)) { return; } // Check to see if any existing activities were expecting this activity // as a dependency. If so, then then hook up their tail nodes to this // activity's head node with a dummy edge. if (UnsatisfiedSuccessorsLookup.TryGetValue(activityId, out HashSet <Node <T, TEvent> > unsatisfiedSuccessorTailNodes)) { // We know that there are unsatisfied dependencies, so create a head node. T headEventId = NodeIdGenerator(); var headNode = new Node <T, TEvent>(EventGenerator(headEventId)); headNode.IncomingEdges.Add(activityId); EdgeHeadNodeLookup.Add(activityId, headNode); NodeLookup.Add(headNode.Id, headNode); foreach (Node <T, TEvent> tailNode in unsatisfiedSuccessorTailNodes) { T dummyEdgeId = EdgeIdGenerator(); var dummyEdge = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId)); tailNode.IncomingEdges.Add(dummyEdgeId); EdgeHeadNodeLookup.Add(dummyEdgeId, tailNode); headNode.OutgoingEdges.Add(dummyEdgeId); EdgeTailNodeLookup.Add(dummyEdgeId, headNode); EdgeLookup.Add(dummyEdge.Id, dummyEdge); } UnsatisfiedSuccessorsLookup.Remove(activityId); } else { // No existing activities were expecting this activity as a dependency, // so attach it directly to the end node via a dummy. T headEventId = NodeIdGenerator(); Node <T, TEvent> dependencyHeadNode = new Node <T, TEvent>(EventGenerator(headEventId)); dependencyHeadNode.IncomingEdges.Add(activityId); EdgeHeadNodeLookup.Add(activityId, dependencyHeadNode); NodeLookup.Add(dependencyHeadNode.Id, dependencyHeadNode); T dummyEdgeId = EdgeIdGenerator(); var dummyEdge = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId)); dependencyHeadNode.OutgoingEdges.Add(dummyEdgeId); EdgeTailNodeLookup.Add(dummyEdgeId, dependencyHeadNode); EdgeLookup.Add(dummyEdgeId, dummyEdge); EndNode.IncomingEdges.Add(dummyEdgeId); EdgeHeadNodeLookup.Add(dummyEdgeId, EndNode); } }
public override bool AddActivity(TActivity activity, HashSet <T> dependencies) { if (activity == null) { throw new ArgumentNullException(nameof(activity)); } if (dependencies is null) { throw new ArgumentNullException(nameof(dependencies)); } if (EdgeLookup.ContainsKey(activity.Id)) { return(false); } if (dependencies.Contains(activity.Id)) { return(false); } // Create a new edge for the activity. var edge = new Edge <T, TActivity>(activity); EdgeLookup.Add(edge.Id, edge); // We expect dependencies at some point. if (dependencies.Any()) { // Since we use dummy edges to connect all tail nodes, we can create // a new tail node for this edge. T tailEventId = NodeIdGenerator(); var tailNode = new Node <T, TEvent>(EventGenerator(tailEventId)); tailNode.OutgoingEdges.Add(edge.Id); EdgeTailNodeLookup.Add(edge.Id, tailNode); NodeLookup.Add(tailNode.Id, tailNode); // Check which of the expected dependencies currently exist. IList <T> existingDependencies = EdgeLookup.Keys.Intersect(dependencies).ToList(); IList <T> nonExistingDependencies = dependencies.Except(existingDependencies).ToList(); // If any expected dependencies currently exist, then hook up their head // node to this edge's tail node with dummy edges. foreach (T dependencyId in existingDependencies) { Node <T, TEvent> dependencyHeadNode = EdgeHeadNodeLookup[dependencyId]; T dummyEdgeId = EdgeIdGenerator(); var dummyEdge = new Edge <T, TActivity>(DummyActivityGenerator(dummyEdgeId)); tailNode.IncomingEdges.Add(dummyEdgeId); EdgeHeadNodeLookup.Add(dummyEdgeId, tailNode); // If the head node of the dependency is the End node, then convert it. if (dependencyHeadNode.NodeType == NodeType.End) { dependencyHeadNode.SetNodeType(NodeType.Normal); } dependencyHeadNode.OutgoingEdges.Add(dummyEdgeId); EdgeTailNodeLookup.Add(dummyEdgeId, dependencyHeadNode); EdgeLookup.Add(dummyEdgeId, dummyEdge); } // If any expected dependencies currently do not exist, then record their // IDs and add this edge's tail node as an unsatisfied successor. foreach (T dependencyId in nonExistingDependencies) { if (!UnsatisfiedSuccessorsLookup.TryGetValue(dependencyId, out HashSet <Node <T, TEvent> > tailNodes)) { tailNodes = new HashSet <Node <T, TEvent> >(); UnsatisfiedSuccessorsLookup.Add(dependencyId, tailNodes); } tailNodes.Add(tailNode); } } else { // No dependencies, so attach it directly to the start node. StartNode.OutgoingEdges.Add(edge.Id); EdgeTailNodeLookup.Add(edge.Id, StartNode); } ResolveUnsatisfiedSuccessorActivities(edge.Id); return(true); }
public override bool AddActivityDependencies(T activityId, HashSet <T> dependencies) { if (dependencies is null) { throw new ArgumentNullException(nameof(dependencies)); } if (!NodeLookup.TryGetValue(activityId, out Node <T, TActivity> node)) { return(false); } if (!dependencies.Any()) { return(true); } if (dependencies.Contains(activityId)) { return(false); } // If the node is an Start or Isolated node, then convert it. if (node.NodeType == NodeType.Start) { node.SetNodeType(NodeType.Normal); } else if (node.NodeType == NodeType.Isolated) { node.SetNodeType(NodeType.End); } // Check which of the expected dependencies currently exist. IList <T> existingDependencies = NodeLookup.Keys.Intersect(dependencies).ToList(); IList <T> nonExistingDependencies = dependencies.Except(existingDependencies).ToList(); // If any expected dependencies currently exist, generate an edge to connect them. foreach (T dependencyId in existingDependencies) { Node <T, TActivity> dependencyNode = NodeLookup[dependencyId]; T edgeId = EdgeIdGenerator(); var edge = new Edge <T, TEvent>(EventGenerator(edgeId)); node.IncomingEdges.Add(edgeId); EdgeHeadNodeLookup.Add(edgeId, node); // If the dependency node is an End or Isolated node, then convert it. if (dependencyNode.NodeType == NodeType.End) { dependencyNode.SetNodeType(NodeType.Normal); } else if (dependencyNode.NodeType == NodeType.Isolated) { dependencyNode.SetNodeType(NodeType.Start); } dependencyNode.OutgoingEdges.Add(edgeId); EdgeTailNodeLookup.Add(edgeId, dependencyNode); EdgeLookup.Add(edgeId, edge); } // If any expected dependencies currently do not exist, then record their // IDs and add this node as an unsatisfied successor. foreach (T dependencyId in nonExistingDependencies) { if (!UnsatisfiedSuccessorsLookup.TryGetValue(dependencyId, out HashSet <Node <T, TActivity> > successorNodes)) { successorNodes = new HashSet <Node <T, TActivity> >(); UnsatisfiedSuccessorsLookup.Add(dependencyId, successorNodes); } successorNodes.Add(node); } return(true); }