sealed class Dumper
{
public static string Dump(Node root)
{
// get all nodes (used later for "to parent" iteration
var allNodeTuples = EnumerateNodes(root, null);
// main query, later we'll agregate results
var query = allNodeTuples.Select(tuple =>
{
var node = tuple.Item1;
var parent = tuple.Item2;
var s = new List<string> {node.Text, Environment.NewLine};
if (parent != null)
{
// thats for 'closest' childs
s.Insert(0, parent.Children.Last() != node ? "├" : "└");
s.Insert(1,"-");
do
{
// build indention
node = parent;
var oldParent = parent;
parent = allNodeTuples.Where(t => t.Item1 == oldParent).FirstOrDefault().Item2;
if (parent != null)
s.Insert(0, (parent.Children.Last() != node
? "│ "
: " "));
} while (parent != null);
}
// joins results into single line
return string.Join(String.Empty,s);
});
// run query with string builder to aggregate
return query.Aggregate(new StringBuilder(), (s, v) => s.Append(v)).ToString();
}
// Enumerate all tree nodes, returning tuple with first item as node, second as node's parent
private static IEnumerable<Tuple<Node,Node>> EnumerateNodes(Node root, Node parent)
{
/* recursive:
yield return new Tuple<Node,Node>(root, parent);
foreach (var child in root.Children)
foreach (var sub in EnumerateNodes(child, root)) yield return sub;*/
var workStack = new Stack<Tuple<Node,Node>>();
workStack.Push(new Tuple<Node, Node>(root, parent));
while (workStack.Count>0)
{
var current = workStack.Pop();
yield return current;
foreach (var child in current.Item1.Children.Reverse())
workStack.Push(new Tuple<Node, Node>(child, current.Item1));
}
}
}