예제 #1
0
        /// <summary>
        /// Inserts a new child node before the reference node passed.
        /// </summary>
        /// <param name="Node">The new child node to add. This may not be already childed to another node</param>
        /// <param name="After">The reference node. This must be a child of the current node</param>
        /// <returns>The added node</returns>
        public virtual BBCodeNode InsertBefore(BBCodeNode Node, BBCodeNode Before)
        {
            if (Singular)
            {
                throw new InvalidOperationException("Cannot add children to a singular node");
            }

            if (Node == null)
            {
                throw new ArgumentNullException("Node may not be null");
            }

            if (Before == null)
            {
                throw new ArgumentNullException("After may not be null");
            }

            if (Node.Parent != null)
            {
                throw new ArgumentException("The Node provided is already a child of another node");
            }

            if (Before.Parent == null || Before.Parent != this)
            {
                throw new ArgumentException("The Before node provided is not a child of this node");
            }

            children.Insert(children.IndexOf(Before), Node);

            return(Node);
        }
예제 #2
0
        /// <summary>
        /// Adds a new child node at the end of this node's descendants
        /// </summary>
        /// <param name="TagName">The node's tag name. Mandatory.</param>
        /// <param name="Attribute">The node's optional attribute. This may be an empty string or null.</param>
        /// <returns>The newly created child node</returns>
        public virtual BBCodeNode AppendChild(string TagName, string Attribute)
        {
            var node = new BBCodeNode(TagName, Attribute)
            {
                Parent = this,
            };

            return(AppendChild(node));
        }
예제 #3
0
        /// <summary>
        /// Creates a recursive copy of the current nodes and its children
        /// </summary>
        /// <returns>A deep clone of the current node</returns>
        public virtual object Clone()
        {
            BBCodeNode node = new BBCodeNode(TagName, Attribute);

            foreach (var Child in Children)
            {
                node.AppendChild((BBCodeNode)Child.Clone());
            }
            return(node);
        }
예제 #4
0
        /// <summary>
        /// Removes a specific child node
        /// </summary>
        /// <param name="Node">The child node to remove. This must be a child of the current node.</param>
        /// <returns>The removed node</returns>
        public virtual BBCodeNode RemoveChild(BBCodeNode Node)
        {
            if (Node == null)
            {
                throw new ArgumentNullException("Node may not be null");
            }

            if (Node.Parent != null)
            {
                throw new ArgumentException("The BBCodeNode provided is not a child of this node");
            }

            children.Remove(Node);

            return(Node);
        }
예제 #5
0
        /// <summary>
        /// Adds a new child node at the beginning of this node's descendants
        /// </summary>
        /// <param name="Node">The existing BBCodeNode to add. This may not already be childed to another node.</param>
        /// <returns>The node passed</returns>
        public virtual BBCodeNode PrependChild(BBCodeNode Node)
        {
            if (Singular)
            {
                throw new InvalidOperationException("Cannot add children to a singular node");
            }

            if (Node == null)
            {
                throw new ArgumentNullException("Node may not be null");
            }

            if (Node.Parent != null)
            {
                throw new ArgumentException("The BBCodeNode provided is already a child of another node");
            }

            children.Insert(0, Node);

            return(Node);
        }
예제 #6
0
        /// <summary>
        /// Replaces a specific child node with another
        /// </summary>
        /// <param name="Old">The node to remove. This must be a child of this node</param>
        /// <param name="New">The replacement node. This may not already be childed to another node</param>
        /// <returns>The removed node</returns>
        public virtual BBCodeNode ReplaceChild(BBCodeNode Old, BBCodeNode New)
        {
            if (Old == null || New == null)
            {
                throw new ArgumentNullException("Arguments may not be null");
            }

            if (Old.Parent != this)
            {
                throw new ArgumentException("The Old node provided is not a child of this node");
            }

            if (New.Parent != null)
            {
                throw new ArgumentException("The New node provided is a child of another node");
            }

            int index = children.IndexOf(Old);

            children.Remove(Old);
            children.Insert(index, New);

            return(Old);
        }
예제 #7
0
        /// <summary>
        /// Loads a string of BBCode as a BBCodeDocument object
        /// </summary>
        /// <param name="BBCode">A string of BBCode text</param>
        /// <param name="ThrowOnError">Whether to throw an exception on parse error. If false, the error is ignored</param>
        /// <param name="SingularTags">A list of tags which should be considered singular by the parser. Singular tags are self closing and may not have children.</param>
        /// <returns>The DOM representation of the text</returns>
        public static BBCodeDocument Load(string BBCode, bool ThrowOnError, IEnumerable<string> SingularTags)
        {
            BBCodeDocument document = new BBCodeDocument();
            Stack<BBCodeNode> nodestack = new Stack<BBCodeNode>();
            nodestack.Push(document);

            // iterate through all characters in text
            for (int i = 0; i < BBCode.Length; i++)
            {
                // the character is not a tag
                if (BBCode[i] != '[')
                    AddPlainText(document, nodestack, BBCode[i].ToString());
                // beginning of a tag
                else
                {
                    // if we have an open text node, close it.
                    if ((nodestack.Peek() as BBCodeTextNode) != null)
                        nodestack.Pop();

                    StringBuilder TagName = new StringBuilder();
                    i++;

                    // edge case where there is an [ as the last character
                    if (i == BBCode.Length)
                    {
                        if (ThrowOnError)
                            throw new BBCodeParseException("Reached end of document while reading tag", i);
                        AddPlainText(document, nodestack, "[" + TagName.ToString());
                        continue;
                    }

                    bool IsClosing = BBCode[i] == '/';
                    if (IsClosing)
                        i++;

                    // read in the entire tagname
                    while (i < BBCode.Length && char.IsLetter(BBCode[i]))
                        TagName.Append(BBCode[i++]);

                    if (i == BBCode.Length)
                        break;

                    // reached the end of tagname, handle accordingly
                    if (!IsClosing && (BBCode[i] == '=' || BBCode[i] == ']'))
                    {
                        // this fixes exception thrown when user inputs []
                        if (string.IsNullOrEmpty(TagName.ToString()))
                            continue;

                        var el = new BBCodeNode(TagName.ToString(), "", SingularTags.Contains(TagName.ToString()));
                        nodestack.Peek().AppendChild(el);
                        if (!el.Singular)
                            nodestack.Push(el);
                        if (BBCode[i] == ']')
                            continue;

                        // skip over the equals sign so we don't accidentally read it into the attribute
                        if (BBCode[i] == '=')
                            i++;

                        StringBuilder Attribute = new StringBuilder();
                        while (i < BBCode.Length && BBCode[i] != ']')
                            Attribute.Append(BBCode[i++]);
                        el.Attribute = Attribute.ToString();
                    }
                    else if (IsClosing && BBCode[i] == ']')
                    {
                        if (nodestack.Count == 0 || nodestack.Peek().TagName != TagName.ToString())
                        {
                            if (ThrowOnError)
                                throw new BBCodeParseException("Unmatched closing tag", i);
                            AddPlainText(document, nodestack, "[/" + TagName.ToString() + "]");
                            continue;
                        }

                        nodestack.Pop();
                    }
                    else
                    {
                        // edge case where encountering something like: [[i] would cause the [i] tag not to be read properly
                        if (BBCode[i] == '[')
                            i--;

                        // illegal character in tag name
                        if (ThrowOnError)
                            throw new BBCodeParseException("Illegal character in tag name", i);
                        // if ThrowOnError is false, we'll just add it as plain text.
                        AddPlainText(document, nodestack, "[" + TagName.ToString()); // prepend the [ char which started the tag
                    }
                }
            }
            // close up a final text node if it exists:
            if ((nodestack.Peek() as BBCodeTextNode) != null)
                nodestack.Pop();

            // close the body tag if it's the next one
            if ((nodestack.Peek() as BBCodeDocument) != null)
                nodestack.Pop();

            if (nodestack.Count > 0 && ThrowOnError)
                throw new BBCodeParseException("Reached end of document with " + (nodestack.Count-1).ToString() + " unclosed tags", BBCode.Length);

            return document;
        }
예제 #8
0
        /// <summary>
        /// Replaces a specific child node with another
        /// </summary>
        /// <param name="Old">The node to remove. This must be a child of this node</param>
        /// <param name="New">The replacement node. This may not already be childed to another node</param>
        /// <returns>The removed node</returns>
        public virtual BBCodeNode ReplaceChild(BBCodeNode Old, BBCodeNode New)
        {
            if (Old == null || New == null)
                throw new ArgumentNullException("Arguments may not be null");

            if (Old.Parent != this)
                throw new ArgumentException("The Old node provided is not a child of this node");

            if (New.Parent != null)
                throw new ArgumentException("The New node provided is a child of another node");

            int index = children.IndexOf(Old);
            children.Remove(Old);
            children.Insert(index, New);

            return Old;
        }
예제 #9
0
        /// <summary>
        /// Removes a specific child node
        /// </summary>
        /// <param name="Node">The child node to remove. This must be a child of the current node.</param>
        /// <returns>The removed node</returns>
        public virtual BBCodeNode RemoveChild(BBCodeNode Node)
        {
            if (Node == null)
                throw new ArgumentNullException("Node may not be null");

            if (Node.Parent != null)
                throw new ArgumentException("The BBCodeNode provided is not a child of this node");

            children.Remove(Node);

            return Node;
        }
예제 #10
0
        /// <summary>
        /// Adds a new child node at the beginning of this node's descendants
        /// </summary>
        /// <param name="Node">The existing BBCodeNode to add. This may not already be childed to another node.</param>
        /// <returns>The node passed</returns>
        public virtual BBCodeNode PrependChild(BBCodeNode Node)
        {
            if (Singular)
                throw new InvalidOperationException("Cannot add children to a singular node");

            if (Node == null)
                throw new ArgumentNullException("Node may not be null");

            if (Node.Parent != null)
                throw new ArgumentException("The BBCodeNode provided is already a child of another node");

            children.Insert(0, Node);

            return Node;
        }
예제 #11
0
        /// <summary>
        /// Inserts a new child node before the reference node passed.
        /// </summary>
        /// <param name="Node">The new child node to add. This may not be already childed to another node</param>
        /// <param name="After">The reference node. This must be a child of the current node</param>
        /// <returns>The added node</returns>
        public virtual BBCodeNode InsertBefore(BBCodeNode Node, BBCodeNode Before)
        {
            if (Singular)
                throw new InvalidOperationException("Cannot add children to a singular node");

            if(Node == null)
                throw new ArgumentNullException("Node may not be null");

            if(Before == null)
                throw new ArgumentNullException("After may not be null");

            if (Node.Parent != null)
                throw new ArgumentException("The Node provided is already a child of another node");

            if (Before.Parent == null || Before.Parent != this)
                throw new ArgumentException("The Before node provided is not a child of this node");

            children.Insert(children.IndexOf(Before), Node);

            return Node;
        }
예제 #12
0
 /// <summary>
 /// Creates a recursive copy of the current nodes and its children
 /// </summary>
 /// <returns>A deep clone of the current node</returns>
 public virtual object Clone()
 {
     BBCodeNode node = new BBCodeNode(TagName, Attribute);
     foreach (var Child in Children)
         node.AppendChild((BBCodeNode)Child.Clone());
     return node;
 }
예제 #13
0
        /// <summary>
        /// Adds a new child node at the end of this node's descendants
        /// </summary>
        /// <param name="TagName">The node's tag name. Mandatory.</param>
        /// <param name="Attribute">The node's optional attribute. This may be an empty string or null.</param>
        /// <returns>The newly created child node</returns>
        public virtual BBCodeNode AppendChild(string TagName, string Attribute)
        {
            var node = new BBCodeNode(TagName, Attribute)
            {
                Parent = this,
            };

            return AppendChild(node);
        }
예제 #14
0
        /// <summary>
        /// Loads a string of BBCode as a BBCodeDocument object
        /// </summary>
        /// <param name="BBCode">A string of BBCode text</param>
        /// <param name="ThrowOnError">Whether to throw an exception on parse error. If false, the error is ignored</param>
        /// <param name="SingularTags">A list of tags which should be considered singular by the parser. Singular tags are self closing and may not have children.</param>
        /// <returns>The DOM representation of the text</returns>
        public static BBCodeDocument Load(string BBCode, bool ThrowOnError, IEnumerable <string> SingularTags)
        {
            BBCodeDocument     document  = new BBCodeDocument();
            Stack <BBCodeNode> nodestack = new Stack <BBCodeNode>();

            nodestack.Push(document);

            // iterate through all characters in text
            for (int i = 0; i < BBCode.Length; i++)
            {
                // the character is not a tag
                if (BBCode[i] != '[')
                {
                    AddPlainText(document, nodestack, BBCode[i].ToString());
                }
                // beginning of a tag
                else
                {
                    // if we have an open text node, close it.
                    if ((nodestack.Peek() as BBCodeTextNode) != null)
                    {
                        nodestack.Pop();
                    }

                    StringBuilder TagName = new StringBuilder();
                    i++;

                    // edge case where there is an [ as the last character
                    if (i == BBCode.Length)
                    {
                        if (ThrowOnError)
                        {
                            throw new BBCodeParseException("Reached end of document while reading tag", i);
                        }
                        AddPlainText(document, nodestack, "[" + TagName.ToString());
                        continue;
                    }


                    bool IsClosing = BBCode[i] == '/';
                    if (IsClosing)
                    {
                        i++;
                    }

                    // read in the entire tagname
                    while (i < BBCode.Length && char.IsLetter(BBCode[i]))
                    {
                        TagName.Append(BBCode[i++]);
                    }

                    if (i == BBCode.Length)
                    {
                        break;
                    }

                    // reached the end of tagname, handle accordingly
                    if (!IsClosing && (BBCode[i] == '=' || BBCode[i] == ']'))
                    {
                        var el = new BBCodeNode(TagName.ToString(), "", SingularTags.Contains(TagName.ToString()));
                        nodestack.Peek().AppendChild(el);
                        if (!el.Singular)
                        {
                            nodestack.Push(el);
                        }
                        if (BBCode[i] == ']')
                        {
                            continue;
                        }

                        // skip over the equals sign so we don't accidentally read it into the attribute
                        if (BBCode[i] == '=')
                        {
                            i++;
                        }

                        StringBuilder Attribute = new StringBuilder();
                        while (i < BBCode.Length && BBCode[i] != ']')
                        {
                            Attribute.Append(BBCode[i++]);
                        }
                        el.Attribute = Attribute.ToString();
                    }
                    else if (IsClosing && BBCode[i] == ']')
                    {
                        if (nodestack.Count == 0 || nodestack.Peek().TagName != TagName.ToString())
                        {
                            if (ThrowOnError)
                            {
                                throw new BBCodeParseException("Unmatched closing tag", i);
                            }
                            AddPlainText(document, nodestack, "[/" + TagName.ToString() + "]");
                            continue;
                        }

                        nodestack.Pop();
                    }
                    else
                    {
                        // edge case where encountering something like: [[i] would cause the [i] tag not to be read properly
                        if (BBCode[i] == '[')
                        {
                            i--;
                        }

                        // illegal character in tag name
                        if (ThrowOnError)
                        {
                            throw new BBCodeParseException("Illegal character in tag name", i);
                        }
                        // if ThrowOnError is false, we'll just add it as plain text.
                        AddPlainText(document, nodestack, "[" + TagName.ToString()); // prepend the [ char which started the tag
                    }
                }
            }
            // close up a final text node if it exists:
            if ((nodestack.Peek() as BBCodeTextNode) != null)
            {
                nodestack.Pop();
            }

            // close the body tag if it's the next one
            if ((nodestack.Peek() as BBCodeDocument) != null)
            {
                nodestack.Pop();
            }

            if (nodestack.Count > 0 && ThrowOnError)
            {
                throw new BBCodeParseException("Reached end of document with " + (nodestack.Count - 1).ToString() + " unclosed tags", BBCode.Length);
            }

            return(document);
        }