Example #1
0
        public static IList<ParsedChunk> ParseChunk(string input)
        {
            #region init

            // init
            var toReturn = new List<ParsedChunk>();
            var openTags = new Stack<BbTag>();
            var tags = new Queue<BbTag>();
            var processedQueue = new Queue<BbTag>();

            var finder = new BbFinder(input);

            // find all tags
            while (!finder.HasReachedEnd)
            {
                var next = finder.Next();
                if (next != null)
                    tags.Enqueue(next);
            }

            // return original input if we've no valid bbcode tags
            if (tags.All(x => x.Type == BbCodeType.None))
                return new[] {AsChunk(input)};

            #endregion

            #region handle unbalanced tags

            var unbalancedTags =
                (from t in tags
                    where t.Type != BbCodeType.None
                    group t by t.Type
                    into g
                    select new {Type = g.Key, Tags = g})
                    .Where(x => x.Tags.Count()%2 == 1);

            foreach (var tagGroup in unbalancedTags.ToList())
                tagGroup.Tags.First().Type = BbCodeType.None;

            #endregion

            while (tags.Count > 0)
            {
                // get the next tag to process
                var tag = tags.Dequeue();
                var addToQueue = true;

                #region add as child of last tag

                // check if we're in the context of another open tag
                if (openTags.Count > 0)
                {
                    var lastOpen = openTags.Peek();

                    // check if we're the closing for the last open tag
                    if (lastOpen.Type == tag.Type
                        && tag.IsClosing)
                    {
                        lastOpen.ClosingTag = tag;
                        openTags.Pop();

                        #region handle noparse

                        if (lastOpen.Type == BbCodeType.NoParse)
                        {
                            lastOpen.Children = lastOpen.Children ?? new List<BbTag>();
                            lastOpen.Children.Add(new BbTag
                                {
                                    Type = BbCodeType.None,
                                    End = tag.Start,
                                    Start = lastOpen.End
                                });
                        }

                        #endregion
                    }
                    else
                    {
                        if (lastOpen.Type != BbCodeType.NoParse)
                        {
                            // if not, we have to be a child of it
                            lastOpen.Children = lastOpen.Children ?? new List<BbTag>();

                            lastOpen.Children.Add(tag);
                        }

                        addToQueue = false;
                    }
                }

                #endregion

                // we don't need to continue processing closing tags
                if (tag.IsClosing) continue;

                // tell the system we're in the context of this tag now
                if (tag.Type != BbCodeType.None) // text content can't have children
                    openTags.Push(tag);

                // if we're added as a child to another tag, don't process independently of parent
                if (addToQueue) processedQueue.Enqueue(tag);
            }

            // if in the process of removing improper tags we end up with no bbcode,
            // return original
            if (processedQueue.All(x => x.Type == BbCodeType.None))
                return new[] {AsChunk(input)};

            toReturn.AddRange(processedQueue.Select(x => FromTag(x, input)));

            return toReturn;
        }
Example #2
0
        public static IList <ParsedChunk> ParseChunk(string input)
        {
            #region init

            // init
            var toReturn       = new List <ParsedChunk>();
            var openTags       = new Stack <BbTag>();
            var tags           = new Queue <BbTag>();
            var processedQueue = new Queue <BbTag>();

            var finder = new BbFinder(input);

            // find all tags
            while (!finder.HasReachedEnd)
            {
                var next = finder.Next();
                if (next != null)
                {
                    tags.Enqueue(next);
                }
            }


            // return original input if we've no valid bbcode tags
            if (tags.All(x => x.Type == BbCodeType.None))
            {
                return new[] { AsChunk(input) }
            }
            ;

            #endregion

            while (tags.Count > 0)
            {
                // get the next tag to process
                var tag = tags.Dequeue();

                var addToQueue = true;

                #region add as child of last tag

                // check if we're in the context of another open tag
                if (openTags.Count > 0)
                {
                    var lastOpen     = openTags.Peek();
                    var lastMatching = openTags.FirstOrDefault(x => tag.IsClosing && x.Type == tag.Type);

                    // check if we're closing any previous tags
                    if (lastMatching != null)
                    {
                        lastMatching.ClosingTag = tag;

                        // keep going through our opened tag stack until we find the one
                        // we're closing
                        do
                        {
                            lastOpen = openTags.Pop();

                            // if we end up with a tag that isn't the one we're closing,
                            // it must not have been closed correctly, e.g
                            // [i] [b] [/i]
                            // we'll treat that '[b]' as text
                            if (lastOpen != lastMatching)
                            {
                                lastOpen.Type = BbCodeType.None;
                            }
                        } while (lastOpen != lastMatching);

                        #region handle noparse

                        if (lastMatching.Type == BbCodeType.NoParse)
                        {
                            lastMatching.Children = lastMatching.Children ?? new List <BbTag>();
                            lastMatching.Children.Add(new BbTag
                            {
                                Type  = BbCodeType.None,
                                End   = tag.Start,
                                Start = lastMatching.End
                            });
                        }

                        #endregion
                    }
                    else
                    {
                        if (openTags.All(x => x.Type != BbCodeType.NoParse))
                        {
                            // if not, we have to be a child of it
                            lastOpen.Children = lastOpen.Children ?? new List <BbTag>();

                            lastOpen.Children.Add(tag);
                        }

                        // any matching closing tags would be caught in the if part of this
                        // branch, this is an invalid tag, treat as text
                        if (tag.IsClosing)
                        {
                            tag.Type = BbCodeType.None;
                        }

                        addToQueue = false;
                    }
                }

                #endregion

                // we don't need to continue processing closing tags
                if (tag.IsClosing)
                {
                    continue;
                }

                // tell the system we're in the context of this tag now
                // though ignore children of 'text' and 'hr'
                if (tag.Type != BbCodeType.None && tag.Type != BbCodeType.HorizontalRule)
                {
                    openTags.Push(tag);
                }

                // if we're added as a child to another tag, don't process independently of parent
                if (addToQueue)
                {
                    processedQueue.Enqueue(tag);
                }
            }

            // these tags haven't been closed, so treat them as invalid
            foreach (var openTag in openTags)
            {
                openTag.Type = BbCodeType.None;
            }

            // if we have no bbcode present, just return the text as-is
            if (processedQueue.All(x => x.Type == BbCodeType.None && x.Children == null))
            {
                return new[] { AsChunk(input) }
            }
            ;

            toReturn.AddRange(processedQueue.Select(x => FromTag(x, input)));

            return(toReturn);
        }
Example #3
0
        public static IList<ParsedChunk> ParseChunk(string input)
        {
            #region init

            // init
            var toReturn = new List<ParsedChunk>();
            var openTags = new Stack<BbTag>();
            var tags = new Queue<BbTag>();
            var processedQueue = new Queue<BbTag>();

            var finder = new BbFinder(input);

            // find all tags
            while (!finder.HasReachedEnd)
            {
                var next = finder.Next();
                if (next != null)
                    tags.Enqueue(next);
            }


            // return original input if we've no valid bbcode tags
            if (tags.All(x => x.Type == BbCodeType.None))
                return new[] {AsChunk(input)};

            #endregion

            while (tags.Count > 0)
            {
                // get the next tag to process
                var tag = tags.Dequeue();
                var addToQueue = true;

                #region add as child of last tag

                // check if we're in the context of another open tag
                if (openTags.Count > 0)
                {
                    var lastOpen = openTags.Peek();
                    var lastMatching = openTags.FirstOrDefault(x => tag.IsClosing && x.Type == tag.Type);

                    // check if we're closing any previous tags
                    if (lastMatching != null)
                    {
                        lastMatching.ClosingTag = tag;

                        // keep going through our opened tag stack until we find the one
                        // we're closing
                        do
                        {
                            lastOpen = openTags.Pop();

                            // if we end up with a tag that isn't the one we're closing,
                            // it must not have been closed correctly, e.g
                            // [i] [b] [/i]
                            // we'll treat that '[b]' as text
                            if (lastOpen != lastMatching) lastOpen.Type = BbCodeType.None;
                        } while (lastOpen != lastMatching);

                        #region handle noparse

                        if (lastMatching.Type == BbCodeType.NoParse)
                        {
                            lastMatching.Children = lastMatching.Children ?? new List<BbTag>();
                            lastMatching.Children.Add(new BbTag
                            {
                                Type = BbCodeType.None,
                                End = tag.Start,
                                Start = lastMatching.End
                            });
                        }

                        #endregion
                    }
                    else
                    {
                        if (openTags.All(x => x.Type != BbCodeType.NoParse))
                        {
                            // if not, we have to be a child of it
                            lastOpen.Children = lastOpen.Children ?? new List<BbTag>();

                            lastOpen.Children.Add(tag);
                        }

                        // any matching closing tags would be caught in the if part of this
                        // branch, this is an invalid tag, treat as text
                        if (tag.IsClosing) tag.Type = BbCodeType.None;

                        addToQueue = false;
                    }
                }

                #endregion

                // we don't need to continue processing closing tags
                if (tag.IsClosing) continue;

                // tell the system we're in the context of this tag now
                // though ignore children of 'text' and 'hr'
                if (tag.Type != BbCodeType.None && tag.Type != BbCodeType.HorizontalRule)
                    openTags.Push(tag);

                // if we're added as a child to another tag, don't process independently of parent
                if (addToQueue) processedQueue.Enqueue(tag);
            }

            // these tags haven't been closed, so treat them as invalid
            foreach (var openTag in openTags)
                openTag.Type = BbCodeType.None;

            // if we have no bbcode present, just return the text as-is
            if (processedQueue.All(x => x.Type == BbCodeType.None && x.Children == null))
                return new[] {AsChunk(input)};

            toReturn.AddRange(processedQueue.Select(x => FromTag(x, input)));

            return toReturn;
        }