Click here to Skip to main content
15,893,401 members
Articles / Programming Languages / XML

Using InsomniaServer to Build a Web-interface for your Application

Rate me:
Please Sign up or sign in to vote.
4.80/5 (11 votes)
15 Jul 2011CPOL3 min read 83.5K   1.2K   41  
InsomniaServer enables you to add a fully-featured, customizable webserver to your projects. See how it works.
//All code is copyright � 2007, InsomniaSoftware | Manuel Then, All rights reserved.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;

using InsomniaSoftware.Server.Tools;


namespace InsomniaSoftware.Server.Sources
{
    /// <summary>
    /// A source which enables you to construct html structures. The final html source is build by this class.
    /// </summary>
    public class HtmlSource : Source
    {
        /// <summary>
        /// Gets the page's root (html-element)
        /// </summary>
        public HtmlNode root
        {
            get
            {
                return _root;
            }
        }
        HtmlNode _root;

        /// <summary>
        /// Gets the page's head node
        /// </summary>
        public HtmlNode head
        {
            get
            {
                return _head;
            }
        }
        HtmlNode _head;

        /// <summary>
        /// Gets the page's body node
        /// </summary>
        public HtmlNode body
        {
            get
            {
                return _body;
            }
        }
        HtmlNode _body;


        /// <summary>
        /// Gets/sets the node which has been added most recently.  curNode is very useful to build complex page structures without saving every node handle.  Call MoveCurNodeLevelUp() to go to its parent.
        /// </summary>
        public HtmlNode curNode;


        MemoryStream dataStream;
        internal bool dataChanged = true;

        SourceInfo sourceInfo = null;


        /// <summary>
        /// Initializes a new HtmlSource
        /// </summary>
        /// <param name="pageTitle">Title of the page to be created</param>
        public HtmlSource(string pageTitle)
        {
            this._root = new HtmlNode("html", this);
            _head = _root.AddSubNode(new HtmlNode("head"));
            _head.AddSubNode(new HtmlNode("title", pageTitle));

            _body = _root.AddSubNode(new HtmlNode("body"));

            curNode = _body;
        }

        /// <summary>
        /// Initializes a new HtmlSource
        /// </summary>
        /// <param name="pageTitle">Title of the page to be created</param>
        /// <param name="sourceInfo">SourceInfo for this source</param>
        public HtmlSource(string pageTitle, SourceInfo sourceInfo) : this(pageTitle)
        {
            this.sourceInfo = sourceInfo;
        }


        /// <summary>
        /// Rebuilds the data stream if necessarry
        /// </summary>
        void BuildDataStream()
        {
            if (dataStream == null || dataChanged)
            {
                dataStream = new MemoryStream(GetSubNodesLength(_root));
                BuildDataStreamRecursion(dataStream, _root);

                dataChanged = false;
            }
        }

        void BuildDataStreamRecursion(MemoryStream dataStream, HtmlNode node)
        {
            string openTagString = "";
            if (node.name.Length > 0)
            {
                openTagString = "<" + node.name;
                LinkedListNode<KeyValuePair<string, string>> curAttribute = node.attributes.First;
                while (curAttribute != null)
                {
                    openTagString += " " + curAttribute.Value.Key + "=\"" + curAttribute.Value.Value + "\"";
                    curAttribute = curAttribute.Next;
                }
                openTagString += ">";
            }
            openTagString += HttpTools.EncodeHtmlString(node.value);

            byte[] buffer = Encoding.ASCII.GetBytes(openTagString);
            dataStream.Write(buffer, 0, buffer.Length);
            buffer = null;

            if (node.name.Length > 0)
            {
                if (node.hasEndTag)
                {
                    LinkedListNode<HtmlNode> curNode = node.childNodes.First;
                    while (curNode != null)
                    {
                        BuildDataStreamRecursion(dataStream, curNode.Value);
                        curNode = curNode.Next;
                    }

                    buffer = Encoding.ASCII.GetBytes("</" + node.name + ">");
                    dataStream.Write(buffer, 0, buffer.Length);
                }
            }
        }

        int GetSubNodesLength(HtmlNode node)
        {
            int length = node.approximateLength;
            LinkedListNode<HtmlNode> curNode = node.childNodes.First;
            while (curNode != null)
            {
                length += GetSubNodesLength(curNode.Value);
                curNode = curNode.Next;
            }
            return length;
        }

        /// <summary>
        /// Sets the curNode handle to its parent node
        /// </summary>
        public void MoveCurNodeLevelUp()
        {
            if (curNode.parentNode != null)
                curNode = curNode.parentNode;
        }


        /// <summary>
        /// Gets the length of the source's content
        /// </summary>
        /// <returns>Content length</returns>
        public override int GetLength()
        {
            BuildDataStream();
            return (int)dataStream.Length;
        }

        /// <summary>
        /// Moves current the position withing the binary data
        /// </summary>
        /// <param name="offset">Offset by which the location will move</param>
        /// <param name="origin">Position to start moving at</param>
        /// <returns>The absolute position within the data</returns>
        public override int Seek(int offset, SeekOrigin origin)
        {
            return (int)dataStream.Seek(offset, origin);
        }

        /// <summary>
        /// Reads a specified number of bytes into the given buffer
        /// </summary>
        /// <param name="buffer">Buffer to be filled</param>
        /// <param name="offset">First position in the buffer to be filled</param>
        /// <param name="count">Number of bytes to be read</param>
        /// <returns>The actual number of bytes read</returns>
        public override int Read(ref byte[] buffer, int offset, int count)
        {
            return dataStream.Read(buffer, offset, count);
        }

        /// <summary>
        /// Gets the content type of this source's content
        /// </summary>
        /// <returns>ContentType instance</returns>
        public override ContentType GetContentType()
        {
            return ContentType.FromType(ContentType.Type.Html);
        }

        /// <summary>
        /// Gets the SourceInfo
        /// </summary>
        /// <returns>SourceInfo of this StreamSource</returns>
        public override SourceInfo GetInformation()
        {
            if (sourceInfo != null)
                return sourceInfo;
            else
                return new SourceInfo();
        }
    }

    /// <summary>
    /// Class that represents a single element node within an html page
    /// </summary>
    public class HtmlNode
    {
        internal int approximateLength
        {
            get
            {
                return _approximateLength;
            }
        }
        int _approximateLength = 5;


        /// <summary>
        /// Gets/sets the name of this node
        /// </summary>
        public string name
        {
            get
            {
                return _nodeName;
            }

            set
            {
                lock (this)
                {
                    _approximateLength -= _nodeName.Length * (hasEndTag ? 2 : 1);
                    _nodeName = value;
                    _approximateLength += _nodeName.Length * (hasEndTag ? 2 : 1);

                    if (belongingSource != null)
                        belongingSource.dataChanged = true;
                }
            }
        }
        string _nodeName = "";

        internal LinkedList<KeyValuePair<string, string>> attributes = new LinkedList<KeyValuePair<string, string>>();

        /// <summary>
        /// Gets/sets the value of this node. Remark: Nodes, containing sub nodes must not have a value.
        /// </summary>
        public string value
        {
            get
            {
                return _value;
            }

            set
            {
                lock (this)
                {
                    if (childNodes.Count > 0)
                        throw new Exception("Nodes with sub nodes can not have a value");
                    if (!hasEndTag)
                        throw new Exception("Nodes without an end tag must not have a value");

                    _approximateLength -= _value.Length;
                    _value = value;
                    _approximateLength += _value.Length;

                    if (belongingSource != null)
                        belongingSource.dataChanged = true;
                }
            }
        }
        string _value = "";

        internal LinkedList<HtmlNode> childNodes = new LinkedList<HtmlNode>();

        internal bool hasEndTag;

        HtmlSource belongingSource;


        /// <summary>
        /// Gets this node's parent node
        /// </summary>
        public HtmlNode parentNode
        {
            get
            {
                return _parentNode;
            }
        }
        HtmlNode _parentNode;


        internal HtmlNode(string name, HtmlSource belongingSource)
            : this(name, true)
        {
            this.belongingSource = belongingSource;
        }

        /// <summary>
        /// Initializes a new HtmlNode
        /// </summary>
        /// <param name="name">Node's name</param>
        public HtmlNode(string name)
            : this(name, true)
        { }

        /// <summary>
        /// Initializes a new HtmlNode
        /// </summary>
        /// <param name="name">Node's name</param>
        /// <param name="hasEndTag">Specifies whether this node has an end tag. For example "br" or "img" do not have one. Remark: Nodes without an end tag must not have a value or sub nodes.</param>
        public HtmlNode(string name, bool hasEndTag)
        {
            if (name == null)
                throw new Exception("Node's name must not be null");

            this.name = name;
            this.hasEndTag = hasEndTag;
        }

        /// <summary>
        /// Initializes a new HtmlNode
        /// </summary>
        /// <param name="name">Node's name</param>
        /// <param name="value">Node's value. Remark: Nodes, containing sub nodes must not have a value.</param>
        public HtmlNode(string name, string value)
            : this(name)
        {
            this.value = value;
        }


        /// <summary>
        /// Adds a new sub node
        /// </summary>
        /// <param name="newSubNode">Node to be added</param>
        /// <returns>The added node</returns>
        public HtmlNode AddSubNode(HtmlNode newSubNode)
        {
            AddSubNodeValidate();

            if (newSubNode == null)
                throw new Exception("New sub node must not be null");

            childNodes.AddLast(newSubNode);

            NewNodeUpdates(newSubNode);
            return newSubNode;
        }

        /// <summary>
        /// Adds a new sub node
        /// </summary>
        /// <param name="nodeName">Element name of the new node. Does the same as: AddSubNode(new HtmlNode(nodeName))</param>
        /// <returns>The added node</returns>
        public HtmlNode AddSubNode(string nodeName)
        {
            return AddSubNode(new HtmlNode(nodeName));
        }

        /// <summary>
        /// Adds a new sub node
        /// </summary>
        /// <param name="nodeName">Element name of the new node. Does the same as: AddSubNode(new HtmlNode(nodeName))</param>
        /// <param name="hasEndTag">Specifies whether the new node has an end tag</param>
        /// <returns>The added node</returns>
        public HtmlNode AddSubNode(string nodeName, bool hasEndTag)
        {
            return AddSubNode(new HtmlNode(nodeName, hasEndTag));
        }

        /// <summary>
        /// Adds a new sub node
        /// </summary>
        /// <param name="nodeName">Element name of the new node. Does the same as: AddSubNode(new HtmlNode(nodeName))</param>
        /// <param name="value">New node's value</param>
        /// <returns>The added node</returns>
        public HtmlNode AddSubNode(string nodeName, string value)
        {
            return AddSubNode(new HtmlNode(nodeName, value));
        }


        /// <summary>
        /// Adds a new sub node after an existing one
        /// </summary>
        /// <param name="newSubNode">Node to be added</param>
        /// <param name="nodeAfterWhichItWillBeInserted">Node, after which the new one is inserted</param>
        /// <returns>The added node</returns>
        public HtmlNode AddSubNodeAfter(HtmlNode newSubNode, HtmlNode nodeAfterWhichItWillBeInserted)
        {
            AddSubNodeValidate();

            if (newSubNode == null || nodeAfterWhichItWillBeInserted == null)
                throw new Exception("Parameters must not be null");

            LinkedListNode<HtmlNode> insertNode = childNodes.First;
            while (insertNode != null)
            {
                if (insertNode.Value == nodeAfterWhichItWillBeInserted)
                    break;
                insertNode = insertNode.Next;
            }
            if (insertNode == null)
                throw new Exception("Node after which one the new was to be inserted could not be found");
            childNodes.AddAfter(insertNode, newSubNode);

            NewNodeUpdates(newSubNode);
            return newSubNode;
        }

        /// <summary>
        /// Adds a new sub node before an existing one
        /// </summary>
        /// <param name="newSubNode">Node to be added</param>
        /// <param name="nodeBeforeWhichItWillBeInserted">Node, before which the new one is inserted</param>
        /// <returns>The added node</returns>
        public HtmlNode AddSubNodeBefore(HtmlNode newSubNode, HtmlNode nodeBeforeWhichItWillBeInserted)
        {
            AddSubNodeValidate();

            if (newSubNode == null || nodeBeforeWhichItWillBeInserted == null)
                throw new Exception("Parameters must not be null");

            LinkedListNode<HtmlNode> insertNode = childNodes.First;
            while (insertNode != null)
            {
                if (insertNode.Value == nodeBeforeWhichItWillBeInserted)
                    break;
                insertNode = insertNode.Next;
            }
            if (insertNode == null)
                throw new Exception("Node before which one the new was to be inserted could not be found");
            childNodes.AddBefore(insertNode, newSubNode);

            NewNodeUpdates(newSubNode);
            return newSubNode;
        }

        void AddSubNodeValidate()
        {
            if (_value.Length > 0)
                throw new Exception("Nodes that have a value must not contain sub nodes");
            if (!hasEndTag)
                throw new Exception("Nodes without an end tag must not contain child nodes");
        }

        void NewNodeUpdates(HtmlNode newNode)
        {
            newNode._parentNode = this;
            newNode.belongingSource = belongingSource;
            if (belongingSource != null)
            {
                belongingSource.dataChanged = true;
                if (newNode.hasEndTag)
                    belongingSource.curNode = newNode;
            }
        }


        /// <summary>
        /// Removes a sub node
        /// </summary>
        /// <param name="nodeToRemove">Node which is to be removed</param>
        public void RemoveSubNode(HtmlNode nodeToRemove)
        {
            childNodes.Remove(nodeToRemove);
            belongingSource.dataChanged = true;
        }

        /// <summary>
        /// Removes all sub nodes
        /// </summary>
        public void RemoveSubNodes()
        {
            childNodes.Clear();
            belongingSource.dataChanged = true;
        }


        /// <summary>
        /// Queries all sub nodes with a specific name
        /// </summary>
        /// <param name="name">Name of the sub nodes to get. If the parameter is null all sub nodes are returned</param>
        /// <returns>Array, containing the requested nodes</returns>
        public HtmlNode[] GetSubNodes(string name)
        {
            ArrayList foundNodes = new ArrayList();
            LinkedListNode<HtmlNode> curNode = childNodes.First;
            while (curNode != null)
            {
                if (curNode.Value._nodeName == name || name == null)
                    foundNodes.Add(curNode.Value);
                curNode = curNode.Next;
            }
            return (HtmlNode[])foundNodes.ToArray(typeof(HtmlNode));
        }

        /// <summary>
        /// Requests all sub nodes
        /// </summary>
        /// <returns>Array, containing all sub nodes</returns>
        public HtmlNode[] GetSubNodes()
        {
            return GetSubNodes(null);
        }


        /// <summary>
        /// Sets an attribute
        /// </summary>
        /// <param name="name">Attribute's name</param>
        /// <param name="value">Value to set. If this is null the attribute is removed.</param>
        public void SetAttribute(string name, string value)
        {
            if (name == null)
                throw new Exception("Attribute's name must not be null");

            lock (this)
            {
                LinkedListNode<KeyValuePair<string, string>> curNode = attributes.First;
                while (curNode != null)
                {
                    if (curNode.Value.Key == name)
                    {
                        if (value == null)
                        {
                            _approximateLength -= CalculateAttributeLength(curNode.Value);
                            attributes.Remove(curNode);

                            if (belongingSource != null)
                                belongingSource.dataChanged = true;
                        }
                        else
                        {
                            _approximateLength -= CalculateAttributeLength(curNode.Value);
                            curNode.Value = new KeyValuePair<string, string>(name.ToLower(), value);
                            _approximateLength += CalculateAttributeLength(curNode.Value);

                            if (belongingSource != null)
                                belongingSource.dataChanged = true;
                        }
                        return;
                    }
                    curNode = curNode.Next;
                }
                curNode = attributes.AddLast(new KeyValuePair<string, string>(name.ToLower(), value));
                _approximateLength += CalculateAttributeLength(curNode.Value);

                if (belongingSource != null)
                    belongingSource.dataChanged = true;
            }
        }

        int CalculateAttributeLength(KeyValuePair<string, string> attribute)
        {
            return attribute.Key.Length + attribute.Value.Length + 4;
        }

        /// <summary>
        /// Gets the value of an attribute
        /// </summary>
        /// <param name="name">The name of the attribute to be queried</param>
        /// <returns>The attribute's value. Returns null if the attribute was not found.</returns>
        public string GetAttribute(string name)
        {
            LinkedListNode<KeyValuePair<string, string>> curNode = attributes.First;
            while (curNode != null)
            {
                if (curNode.Value.Key == name)
                    return curNode.Value.Value;
                curNode = curNode.Next;
            }
            return null;
        }
    }


    /// <summary>
    /// A collection of static functions to create common html elements
    /// </summary>
    public static class HtmlObjects
    {
        /// <summary>
        /// Returns a text element
        /// </summary>
        /// <param name="text">Text to be added</param>
        /// <returns>Text node</returns>
        public static HtmlNode SimpleText(string text)
        {
            return new HtmlNode("", text);
        }

        /// <summary>
        /// Returns a line break element ("br")
        /// </summary>
        /// <returns>BR node</returns>
        public static HtmlNode LineBreak()
        {
            return new HtmlNode("br", false);
        }

        /// <summary>
        /// Returns a link element ("a")
        /// </summary>
        /// <param name="destination">Link destination</param>
        /// <returns>Link node</returns>
        public static HtmlNode Link(string destination)
        {
            HtmlNode link = new HtmlNode("a");
            link.SetAttribute("href", destination);
            return link;
        }

        /// <summary>
        /// Returns a image element ("img")
        /// </summary>
        /// <param name="src">Image source</param>
        /// <returns>Image node</returns>
        public static HtmlNode Image(string src)
        {
            HtmlNode img = new HtmlNode("img", false);
            img.SetAttribute("src", src);
            return img;
        }
        
        /// <summary>
        /// Returns a image element ("img")
        /// </summary>
        /// <param name="src">Image source</param>
        /// <param name="border">Border size</param>
        /// <returns>Image node</returns>
        public static HtmlNode Image(string src, int border)
        {
            HtmlNode img = new HtmlNode("img", false);
            img.SetAttribute("src", src);
            img.SetAttribute("border", border.ToString());
            return img;
        }

        /// <summary>
        /// Returns a element representing bold text ("b")
        /// </summary>
        /// <param name="text">Text to be added</param>
        /// <returns>Text node</returns>
        public static HtmlNode BoldText(string text)
        {
            return new HtmlNode("b", text);
        }

        /// <summary>
        /// Returns a element representing italic text ("i")
        /// </summary>
        /// <param name="text">Text to be added</param>
        /// <returns>Text node</returns>
        public static HtmlNode ItalicText(string text)
        {
            return new HtmlNode("i", text);
        }

        /// <summary>
        /// Returns a element representing underlined text ("u")
        /// </summary>
        /// <param name="text">Text to be added</param>
        /// <returns>Text node</returns>
        public static HtmlNode UnderlinedText(string text)
        {
            return new HtmlNode("u", text);
        }


        /// <summary>
        /// Returns a CSS (cascading style sheet) element ("style")
        /// </summary>
        /// <param name="sheetContent">Style sheet's content</param>
        /// <returns>Style element</returns>
        public static HtmlNode CSS(string sheetContent)
        {
            HtmlNode newNode = new HtmlNode("style", "<!--" + sheetContent + "-->");
            newNode.SetAttribute("type", "text/css");
            return newNode;
        }

        /// <summary>
        /// Returns a JavaScript element ("script")
        /// </summary>
        /// <param name="script">Script's content</param>
        /// <returns>Script element</returns>
        public static HtmlNode JavaScript(string script)
        {
            HtmlNode newNode = new HtmlNode("script", "<!--" + script + "-->");
            newNode.SetAttribute("type", "text/javascript");
            return newNode;
        }


        /// <summary>
        /// Returns a form element
        /// </summary>
        /// <param name="method">Submit method. "get" or "post"</param>
        /// <param name="destinationURL">URL to be called on submit</param>
        /// <returns>Form element</returns>
        public static HtmlNode Form(string method, string destinationURL)
        {
            HtmlNode newNode = new HtmlNode("form");
            newNode.SetAttribute("method", method);
            newNode.SetAttribute("action", destinationURL);
            return newNode;
        }

        /// <summary>
        /// Returns an text input element
        /// </summary>
        /// <param name="name">The element's name</param>
        /// <param name="value">Initial value</param>
        /// <returns>Text input element</returns>
        public static HtmlNode TextInput(string name, string value)
        {
            HtmlNode newNode = new HtmlNode("input", false);
            newNode.SetAttribute("name", name);
            newNode.SetAttribute("value", value);
            newNode.SetAttribute("type", "text");
            return newNode;
        }

        /// <summary>
        /// Returns an text input element
        /// </summary>
        /// <param name="name">The element's name</param>
        /// <returns>Text input element</returns>
        public static HtmlNode TextInput(string name)
        {
            return TextInput(name, "");
        }

        /// <summary>
        /// Returns a hidden input element
        /// </summary>
        /// <param name="name">The element's name</param>
        /// <param name="value">Element's value</param>
        /// <returns>Hidden input element</returns>
        public static HtmlNode HiddenInput(string name, string value)
        {
            HtmlNode newNode = new HtmlNode("input", false);
            newNode.SetAttribute("name", name);
            newNode.SetAttribute("value", value);
            newNode.SetAttribute("type", "hidden");
            return newNode;
        }

        /// <summary>
        /// Returns a password input element
        /// </summary>
        /// <param name="name">The element's name</param>
        /// <returns>Text input element</returns>
        public static HtmlNode PasswordInput(string name)
        {
            HtmlNode newNode = new HtmlNode("input", false);
            newNode.SetAttribute("name", name);
            newNode.SetAttribute("type", "password");
            return newNode;
        }

        /// <summary>
        /// Returns a submit button element
        /// </summary>
        /// <returns>Submit button element</returns>
        public static HtmlNode SubmitButtom()
        {
            HtmlNode newNode = new HtmlNode("input", false);
            newNode.SetAttribute("type", "submit");
            return newNode;
        }

        /// <summary>
        /// Returns a submit button element
        /// <param name="value">Text displayed on the button</param>
        /// </summary>
        /// <returns>Submit button element</returns>
        public static HtmlNode SubmitButtom(string value)
        {
            HtmlNode newNode = SubmitButtom();
            newNode.SetAttribute("value", value);
            return newNode;
        }

        /// <summary>
        /// Returns a reset button element
        /// </summary>
        /// <returns>Reset button element</returns>
        public static HtmlNode ResetButtom()
        {
            HtmlNode newNode = new HtmlNode("input", false);
            newNode.SetAttribute("type", "reset");
            return newNode;
        }

        /// <summary>
        /// Returns a reset button element
        /// </summary>
        /// <param name="value">Text displayed on the button</param>
        /// <returns>Reset button element</returns>
        public static HtmlNode ResetButtom(string value)
        {
            HtmlNode newNode = ResetButtom();
            newNode.SetAttribute("value", value);
            return newNode;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Germany Germany
I was born in 1987. Unfortunately too late to experience the real rise of the PC. But fortunately late enough to enjoy things like MS's .net during my school time Wink | ;)

From the time when some relative taught me a little BASIC under MS DOS, I loved to tell computers what to do - even though my real start in programming was around the age of 16.

At the moment, I am studying Software Engineering at University of Augsburg, always hoping to find time to design and program.
Besides, I like meeting friends, spent time with my girlfriend and enjoy life Smile | :)

Comments and Discussions