/// <summary> /// Add a graph node and all of its ancestors to the DAG. /// Returns TRUE iff the node and all of its ancestors were successfully added. /// Note that even in the case of a false return value, there could have been some additions to the DAG. /// We do guarantee that after this operation, the DAG will be consistent, in the sense that a GraphNode being in the DAG guarantees that its ancestors are also. /// </summary> /// <param name="item"></param> /// <returns></returns> public bool Add(IGraphNode <NodeDataType> item) { if (this.Contains(item)) { return(true); // If the node is already in this DAG, then there's nothing to do. } OnDagChange?.Invoke(); // In case someone has subscribed to the changed DAG event. Because of recursion, this event will be thrown a lot, but that shouldn't be a problem if (item is RootNode <NodeDataType> ) { return(_containedNodes.Add(item)); // Adding a root node takes no work at all } if (item is GraphNode <NodeDataType> ) { bool returnVal = true; IEnumerable <IGraphNode <NodeDataType> > parentNodes = (item as GraphNode <NodeDataType>).ParentNodes; foreach (IGraphNode <NodeDataType> parent in parentNodes) // We want this to throw an error if parentNodes is null, since that shouldn't be able to ever happen { if (!this.Add(parent)) { returnVal = false; } } if (returnVal) { return(_containedNodes.Add(item)); } return(false); // If we failed to add some ancestor, then we should NOT add this item. } throw new NotImplementedException(); // We should never reach this point! }
/// <summary> /// Remove a graph node and all of its descendants from the DAG. /// This returns TRUE iff the node and all of its descendants were removed from the DAG. /// In the case of a false return, its possible that only some of the nodes were removed. /// We do guarantee that after this operation, the DAG will be consistent, in the sense that a Node being in the DAG guarantees that its ancestors are also. /// </summary> /// <param name="item"></param> /// <returns></returns> public bool Remove(IGraphNode <NodeDataType> item) { if (!this.Contains(item)) { return(true); // If the item isn't in the DAG, then we're done. } OnDagChange?.Invoke(); // In case someone has subscribed to the changed DAG event IEnumerable <IGraphNode <NodeDataType> > childNodes = item.ChildNodes; bool returnVal = true; if (childNodes != null) { foreach (IGraphNode <NodeDataType> child in childNodes) { if (!this.Remove(child)) { returnVal = false; } } } if (returnVal) { return(_containedNodes.Remove(item)); } return(false); // We failed to remove some descendant, so we should NOT remove this item. }
public void Clear() { OnDagChange?.Invoke(); // In case someone has subscribed to the changed DAG event _containedNodes.Clear(); }
/// <summary> /// Takes the intersection of two DAGs. /// This will act like a typical intersection of sets, because both DAGs being intersected are initially consistent. /// </summary> /// <param name="otherDAG"></param> public void IntersectWith(DAG <NodeDataType> otherDAG) { OnDagChange?.Invoke(); // In case someone has subscribed to the changed DAG event. Note that this will fire even if this DAG is unchanged by the intersection. this._containedNodes.IntersectWith(otherDAG._containedNodes); // This assumes that both DAGs are "consistent" in the sense that a node is in a consistent DAG only if all of that node's parents are in the DAG }