Click here to Skip to main content
Licence GPL3
First Posted 23 May 2011
Views 4,824
Downloads 147
Bookmarked 9 times

LightTreeNode

By | 23 May 2011 | Article
A .NET tree structure.

Introduction

After several years of just staying at the background and enjoying the fruits of other's labor, it's time to give back. I hope my first contribution will be of use.

Background

Recently, I worked on a project that requires creating a tree structure. It surprises me that until now, the .NET core library does not have a Tree data structure, and Googling on the internet, I was able to find some codes, but they were all either large or complex. So I decided to implement my own lightweight tree that has the ability to add, remove, and traverse nodes. As the tree is being traversed, you can handle each node to perform some action, similar to a SAX parser, if you are familiar with that.

Using the code

Figure 1

Figure 1 shows the class diagram of the LightTreeNode class. Below is a sample code on how to use it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace com.delacruz.tree
{
    class Program
    {
        static void Main(string[] args)
        {
            LightTreeNode root = new LightTreeNode(null);
            XmlDocument doc;

            //TODO remove comment for this line to see output
            //when traversing TopDown direction
            //root.Method = LightTreeNode.traverseMethod.TopDown;

            ///////////////////////////////////////////////////////
            // This section creates the tree structure
            
            LightTreeNode parent;
            root.Name = "ROOT";

            LightTreeNode child = new LightTreeNode(root);
            child.Name = "A1";
            setPayLoad(child, "Text for A1 Node", "id=1", "name=Adam");

            child = new LightTreeNode(root);
            child.Name = "A2";
            setPayLoad(child, "A2 Node", "id=2", "name=Alice");

            parent = child;
            child = new LightTreeNode(parent);
            child.Name = "B1";
            setPayLoad(child, "B1 Node", "id=3", "name=Kathyleen");

            child = new LightTreeNode(parent);
            child.Name = "B2";
            setPayLoad(child, "B2 Node", "id=4", "name=Eliza");

            child = new LightTreeNode(parent);
            child.Name = "B3";
            setPayLoad(child, "B3 Node", "id=6", "name=Mark");

            parent = child;
            child = new LightTreeNode(parent);
            child.Name = "C1";

            child = new LightTreeNode(parent);
            child.Name = "C2";
            setPayLoad(child, "C2 Node", "id=8", "name=Kurabara");

            child = new LightTreeNode(parent);
            child.Name = "C3";
            setPayLoad(child, "C3 Node", "id=9", "name=Yuseke");

            child = new LightTreeNode(root);
            child.Name = "A3";
            setPayLoad(child, "Node for A3", "id=10", "name=Lovinia");

            parent = child;
            child = new LightTreeNode(parent);
            child.Name = "B4";
            setPayLoad(child, "Madel Node B4", "id=11", "name=Madel");

            parent = child;
            child = new LightTreeNode(parent);
            child.Name = "C4";

            child = new LightTreeNode(parent);
            child.Name = "C5";

            // Get the node using name
            parent = root.getNodeByName("A3");
            child = new LightTreeNode(parent);
            child.Name = "B5";

            child = new LightTreeNode(root);
            child.Name = "A4";
            setPayLoad(child, "Node for A4", "id=15", "name=Eugene");

            /////////////////////////////////////////////////////////////////////////
            //This portion just output the name of the node
            LightTreeNode.nodeTraverse handler = onNodeTraverse;
            handler(root);
            // Walk the tree
            root.traverse(handler, root);

            //////////////////////////////////////////////////////////////////////////
            // This portion shows how you can generate an XML by walking the root node
            doc = new XmlDocument();
            root.Data = doc;
            // Point to XML generator handling routine
            handler = onNodeTraverseForXML;
            // Process the root object
            handler(root);
            // Walk the tree to output it in xml format
            root.traverse(handler, root);

            Console.WriteLine(((XmlElement)root.Data).OuterXml);
            // Try to write it to file
            string filePath = string.Format("{0}/LightTreeNodeSample.xml", 
              Environment.GetFolderPath(Environment.SpecialFolder.Personal));
            doc.Save(filePath);
            Console.ReadKey();
        }

        /// <summary>
        /// Output to screen 
        /// </summary>
        /// <param name="node"></param>
        public static void onNodeTraverse(LightTreeNode node)
        {
            for (int i = 0; i < node.getDepth(); i++)
                Console.Write("     ");
            Console.WriteLine(node.Name);
        }

        /// <summary>
        /// Handler to generate xml from the node
        /// </summary>
        /// <param name="node">The tree node to handle</param>
        public static void onNodeTraverseForXML(LightTreeNode node)
        {
            XmlDocument doc;
            XmlElement elem;
            XmlElement parentElem;
            // If the node.data type is xmldocument, this is a root node
            if (node.Data != null && node.Data.GetType() == typeof(System.Xml.XmlDocument))
            {
                doc = (XmlDocument)node.Data;
                elem = doc.CreateElement(node.Name);
                doc.AppendChild(elem);
                node.Data = elem;
            }
            else
            {
                // This is a child node, so we expect a parent here
                parentElem = (XmlElement)node.Parent.Data;
                elem = parentElem.OwnerDocument.CreateElement(node.Name);
                parentElem.AppendChild(elem);
                // Check for payload and process it if there is anything on it
                if (node.Payload != null)
                {
                    doc = parentElem.OwnerDocument;
                    MyPayLoad payload = (MyPayLoad)node.Payload;
                    XmlText text = doc.CreateTextNode(payload.Text);
                    elem.AppendChild(text);
                    foreach (string key in payload.Attributes.Keys)
                    {
                        XmlAttribute attr = doc.CreateAttribute(key);
                        attr.Value = payload.Attributes[key];
                        elem.Attributes.Append(attr);
                    }
                }
                node.Data = elem;
            }
        }

        /// <summary>
        /// Adds some dummy payload to the node
        /// </summary>
        /// <param name="node"></param>
        /// <param name="text"></param>
        /// <param name="attrs"></param>
        private static void setPayLoad(LightTreeNode node, 
                string text, params string[] attrs)
        {
            //TODO Remove comment to add some payload on the output
            /*
            MyPayLoad payload = new MyPayLoad();
            payload.Text = text;
            foreach (string attr in attrs)
            {
                string[] kvp = attr.Split('=');
                payload.Attributes.Add(kvp[0], kvp[1]);
            }
            node.Payload = payload;
            */
        }
        #region Private class
        class MyPayLoad
        {
            public Dictionary<string, string> Attributes = 
                   new Dictionary<string, string>();
            public string Text;
        }
        #endregion
    }
}

...

Points of Interest

The default behavior of the traversal is LeftRight, meaning the tree will walk the node down to the last child before traversing the sibling node (next node on the same level).

Set the method to TopDown and the tree walking will be nodes on the same level first, then children of each node.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

Adamson dela Cruz

Architect
dela Cruz Consulting Ltd.
New Zealand New Zealand

Member

Is the Director and Solution Architect for dela Cruz Consulting. With more than 11 years of software development experience, he has developed systems in .NET and Java platform in an N-Tier environment. Working from the UI all the way to the data layer, has acquired skills in developing softwares for the enterprise system using COM+, Remoting, TCP and UDP Sockets, WCF, ORM, Messaging, EJB, RMI, MVC and Front Controller, Ajax, Javascript and a whole bunch more.
 
You can contact him at
architect@delacruzconsulting.com / adamson.delacruz@gmail.com
MCSD,MCAD,MCTS,MCP,SCJA

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 3 PinPopularmvpAspDotNetDev6:35 23 May '11  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 23 May 2011
Article Copyright 2011 by Adamson dela Cruz
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid