Introduction
This article shows how to make a generic tree collection base and implement specific trees based on the tree collection. The abstract tree type implements IEnumerable<TreeNode<TNodeValue>>
, ICollection<TreeNode<TNodeValue>>
, IList<TreeNode<TNodeValue>>
, IEnumerable
, ICollection
, IList
, ISerializable
. This gives you a number of scenarios of usage.
Using the Code
Ok, here we go.
The example below creates a tree called BasicTree
and lets it inherit from the base type Tree<TNodeValue>
. We also create a node type for the tree called BasicNodeValue
that inherits from the base type TreeNodeValue
.
This is the only thing we need to get started using the tree collection functionality.
public sealed class BasicTree : Tree<BasicNodeValue>
{
public BasicTree(BasicNodeValue nodeValue)
: base(nodeValue)
{
}
}
public sealed class BasicNodeValue : TreeNodeValue
{
public BasicNodeValue()
: base()
{
}
public BasicNodeValue(String name, String path) :
base(name, path)
{
}
}
Let's use the BasicTree
further in the next code sample below:
Here we instantiate the BasicTree
and manually populate the tree with nodes. Then we foreach
all nodes in the tree collection and print out the path.
BasicTree tree = new BasicTree(new BasicNodeValue(@"Root", @"\"));
TreeNode<BasicNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new BasicNodeValue(@"2", @"\2"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"1", @"\2\1"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"2", @"\2\2"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"3", @"\2\3"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"4", @"\2\4"));
foreach (TreeNode<BasicNodeValue> basicItem in tree)
{
String path = String.Format(@"{0}", basicItem.RelativePath);
Console.WriteLine(path);
}
Ok, this wasn't quite so exciting perhaps? Let's take this a bit further.
In the example below (also included in the downloadable demo code) we instantiate two trees of type FileSystemTree
and populate them manually (we can also build the index automatically by executing the tree.BuildIndex();
method). We have now created two trees that we can compare on whatever predicate we feel like. The example below compares to relative path but you can choose your own compare method, e.g. path combined with hash.
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
TreeNode<FileSystemNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
FileSystemTree tree2 = new FileSystemTree(new DirectoryInfo(@"c:\temp2"));
TreeNode<FileSystemNodeValue> parentNode2_2 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\2")));
TreeNode<FileSystemNodeValue> parentNode2_3 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3")));
tree2.AddLast(parentNode2_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\1")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\2")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\3")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\4")));
CompareTree<FileSystemNodeValue> resultTree =
tree.CompareTo(tree2, FileSystemTreeManager.Predicates.HasSameName());
foreach (TreeNode<CompareNodeValue<FileSystemNodeValue>> compareNode in resultTree)
{
String result = String.Format(@"{0} {1}",
compareNode.RelativePath, compareNode.NodeValue.CompareResult);
Console.WriteLine(result);
}
We can also execute a LINQ query on the tree:
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
TreeNode<FileSystemNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
var nodes = from TreeNode<FileSystemNodeValue> node in tree
where node.Name == @"2" || node.Name == @"3"
select node;
foreach (TreeNode<FileSystemNodeValue> node in nodes)
{
Console.WriteLine(String.Format(@"Name: {0}", node.Name));
}
With the Tree<TNodeValue>
, we can also execute an action on the nodes if a predicate is true
. In the example below, we print out in the console how many nodes have the name '2
':
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
TreeNode<filesystemnodevalue /> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
List<filesystemnodevalue /> nodeValues = new List<filesystemnodevalue />();
tree.ActionIf(
TreeManager<filesystemnodevalue />.Predicates.HasName(@"2", StringComparison.OrdinalIgnoreCase),
TreeManager<filesystemnodevalue />.Actions.GetList(nodeValues), false);
Console.WriteLine(String.Format(@"Count: {0}", nodeValues.Count));
The last example below shows how to merge two trees:
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
TreeNode<FileSystemNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
FileSystemTree tree2 = new FileSystemTree(new DirectoryInfo(@"c:\temp2"));
TreeNode<FileSystemNodeValue> parentNode2_2 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\2")));
TreeNode<FileSystemNodeValue> parentNode2_3 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\1")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\2")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\3")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\4")));
Tree<FileSystemNodeValue> mergedTree = tree.Merge(tree2);
foreach (TreeNode<FileSystemNodeValue> mergedNode in mergedTree)
{
String result = String.Format(@"{0}", mergedNode.RelativePath);
Console.WriteLine(result);
}
Points of Interest
All code in the article is provided in the demo. If you find anything interesting or have some great ideas of how to improve the code, please send me a note!
May the source be with you! ;)
History
- 2009-02-07 Initial release