/// <summary> /// Copies a tree structure. (useful in copying a tree structure to a tree view). /// </summary> /// <typeparam name="A">The node type of the tree to copy.</typeparam> /// <typeparam name="B">The destination node type.</typeparam> /// /// <param name="getSubNodes"> Get the sub-nodes of any given node.</param> /// <param name="newNode">To create a simple non-recursive copy of a node.</param> /// <param name="addSubNodes"></param> /// <param name="checkType"> Type of circular reference checking to perform.</param> /// <returns></returns> public static B RebuildTree <A, B>(A srcNode, Func <A, IEnumerable <A> > getSubNodes, Func <A, B> newNode, Action <B, IEnumerable <B> > addSubNodes, CircularRefernceBehaviour checkType = CircularRefernceBehaviour.DontCheck) where A : class where B : class { return(RebuildTree(srcNode, getSubNodes, newNode, addSubNodes, P => true, checkType)); }
/// <summary> /// Copies a tree structure. (useful in copying a tree structure to a tree view). /// </summary> /// <typeparam name="A">The node type of the tree to copy.</typeparam> /// <typeparam name="B">The destination node type.</typeparam> /// /// <param name="getSubNodes"> Get the sub-nodes of any given node.</param> /// <param name="newNode">To create a simple non-recursive copy of a node.</param> /// <param name="addSubNodes"></param> /// <param name="where"></param> /// <param name="checkType"> Type of circular reference checking to perform.</param> /// <returns></returns> public static B RebuildTree <A, B>(A srcNode, Func <A, IEnumerable <A> > getSubNodes, Func <A, B> newNode, Action <B, IEnumerable <B> > addSubNodes, Predicate <A> where, CircularRefernceBehaviour checkType = CircularRefernceBehaviour.DontCheck) where A : class where B : class { LinkedList <Tuple <A, B> > list = new LinkedList <Tuple <A, B> >(); HashSet <A> visited = new HashSet <A>(); B newRootNode = null; list.AddLast(new Tuple <A, B>(srcNode, null)); while (list.Count > 0) { var tuple = list.Pop(); A item = tuple.Item1; B parent = tuple.Item2; if (visitOk(item, visited, checkType)) { if (where (item)) { B newItem = newNode(item); if (newRootNode == null) //root node { newRootNode = newItem; } safeAddSubNode(parent, newItem, addSubNodes); list.AddLastAll(getSubNodesSafe(item, getSubNodes).Reverse().Select(N => new Tuple <A, B>(N, newItem))); } } } //done return(newRootNode); }
private static bool visitOk <T>(T item, HashSet <T> visited, CircularRefernceBehaviour checkType) { if (checkType != CircularRefernceBehaviour.DontCheck) { if (visited.Contains(item)) { // error if (checkType == CircularRefernceBehaviour.ThrowException) { throw NodeTraversalException.VisitedTwice(item); } return(false); //indicate it's not ok to visit (ie skip) } //no error visited.Add(item); return(true); } //no check return(true); }
/// <summary> /// Enumerates any Tree/graph in a non-recursive manor. /// Does not check for circularReferences. /// </summary> /// <param name="node">Root node.</param> /// <param name="getSubNodes"> Get the sub-nodes of any given node.</param> /// <param name="order">The visit order.</param> /// <param name="checkType">If circular references should be checked, and how to handle them. /// Note (1): Checks repeated node, which is not nesesiarly a circular reference (but all circulare references have a repeated node). /// Note (2): performance hit reduced if node generates a good hashcode. /// </param> public static IEnumerable <T> EnumerateNodes <T>(T node, Func <T, IEnumerable <T> > getSubNodes, NodeVisitOrder order = NodeVisitOrder.DepthFirstPreOrder, CircularRefernceBehaviour checkType = CircularRefernceBehaviour.DontCheck) where T : class { if (node != null) //assuming null indicats an empty tree { //this acts as a stack or queue to resolve the recursion LinkedList <T> list = new LinkedList <T>(); HashSet <T> visited = new HashSet <T>(); switch (order) { case NodeVisitOrder.DepthFirstPreOrder: list.AddLast(node); while (list.Count > 0) { T item = list.Pop(); if (visitOk(item, visited, checkType)) { yield return(item); list.AddLastAll(getSubNodesSafe(item, getSubNodes).Reverse()); } } break; case NodeVisitOrder.DepthFirstPostOrder: //This has a side effects, the first iteeration is slow (also memory consuming) //as the entire structure is copied into a stack Stack <T> output = new Stack <T>(); list.AddLast(node); while (list.Count > 0) { T item = list.Pop(); if (visitOk(item, visited, checkType)) { output.Push(item); list.AddLastAll(getSubNodesSafe(item, getSubNodes)); } } foreach (T item in output) { yield return(item); } break; case NodeVisitOrder.BredthFirst: list.AddLast(node); while (list.Count > 0) { T item = list.Dequeue(); if (visitOk(item, visited, checkType)) { yield return(item); list.AddLastAll(getSubNodesSafe(item, getSubNodes)); } } break; } } }