All pastes #791337 Raw Edit

ImprovedTextParser - again

public text v1 · immutable
·published 2007-11-20 20:53 UTC
rendered paste body
using System;
using System.Data;
using System.Configuration;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Text.RegularExpressions;

namespace BloggerLibrary
{

    public interface ITextParserTag
    {
        bool NoTouchContent { get; }
        string ProduceOutput(string content, string parameter);
        bool CanProduceOutput(string content, string parameter); 
    }

    public interface ITextParserReplacer
    {
        string PerformReplace(string str);
        bool IsCritical { get; }
    }
    public class TextParserTag : ITextParserTag
    {
        private string output;

        public string Output {
            get {
                return output.Replace(":questionmarkgoeshere:", "?").Replace(":moniesformepls:", "$");
            }
            set {
                output = value.Replace("?", ":questionmarkgoeshere:").Replace("$", ":moniesformepls:");
            }
        }

        protected string RealOutput {
            get {
                return output;
            }
            set {
                output = value;
            }
        }

        public TextParserTag(string output)
        {
         
            this.Output = output;
        }

        private string ReplaceReservedChars(string str){
            return str.Replace("?", ":questionmarkgoeshere:").Replace("$", ":moniesformepls:");
        }

        private string ReplaceReservedCharsBack(string str){
            return str.Replace(":questionmarkgoeshere:", "?").Replace(":moniesformepls:", "$");
        }

        public string ProduceOutput(string content, string parameter){
            string value = ReplaceReservedCharsBack(Output.Replace("?", parameter).Replace("$", content));
            return value;
        }

        public bool CanProduceOutput(string content, string parameter)
        {
            return true;
        }

        public bool NoTouchContent
        {
            get
            {
                return false;
            }
        }

    }

    public class ImageTextParserTag : ITextParserTag
    {
        public ImageTextParserTag()
        {
        }



        #region ITextParserTag Members

        public bool NoTouchContent
        {
            get { return true; }
        }

        public string ProduceOutput(string content, string parameter)
        {
            StringBuilder ret = new StringBuilder("<img src=\"");
            ret.Append(content);
            ret.Append("\" alt=\"");
            if (parameter != string.Empty)
            {
                ret.Append(parameter);
            }
            ret.Append("\" />");

            return ret.ToString();

        }

        public bool CanProduceOutput(string content, string parameter)
        {
            if (content.Contains("\n") || content.Contains(" ") || content.Contains("\"") || content.Contains("'")) //Don't allow newlines or spaces, or quotes of any kind
            {
                return false;
            }
            return true;
        }


        #endregion
    }

    public class TextReplacer : ITextParserReplacer
    {
        List<string> match;
        List<string> replace;
        bool critical;

        public TextReplacer(string match, string replace)
            : this(match, replace, false)
        {
        }

        public TextReplacer(string[] match, string[] replace)
            : this(match, replace, false)
        {
        }

        public TextReplacer(string match, string replace, bool critical)
            : this(new List<string>(new string[]{match}), new List<string>(new string[]{replace}), critical)  
        {
        }
        

        public TextReplacer(string[] match, string[] replace, bool critical) : this(new List<string>(match), new List<string>(replace), critical) {
        }

        public TextReplacer(List<string> match, List<string> replace)
            : this(match, replace, false)
        {
        }

        public TextReplacer(List<string> match, List<string> replace, bool critical){
            this.match = match;
            this.replace = replace;
            if(replace.Count != match.Count){
                if(replace.Count > match.Count){
                    while(replace.Count > match.Count){
                        replace.RemoveAt(replace.Count-1);
                    }
                } else {
                    while(replace.Count < match.Count){
                        replace.RemoveAt(match.Count-1);
                    }
                }
            }
            this.critical = critical;
        }

        public bool IsCritical
        {
            get
            {
                return critical;
            }
            set
            {
                critical = value;
            }
        }

        public string PerformReplace(string text){
            for(int i = 0, n = replace.Count; i < n; i++){
                text = text.Replace(match[i], replace[i]);
            }
            return text;
        }
    }

    public class ImprovedTextParser
    {

        class Tag
        {
            public string Parameter;
            public StringBuilder Content;

            internal Tag()
            {
                Parameter = String.Empty;
                Content = new StringBuilder();
            }
        }

        private bool boolStripHTMLTags = true;

        public bool StripHTMLTags
        {
            get { return boolStripHTMLTags; }
            set { boolStripHTMLTags = value; }
        }

        private const char customOpenTag = '[';
        private const char customCloseTag = ']';
        private const char customEndToken = '/';

        private Dictionary<string, ITextParserTag> tagMap = new Dictionary<string, ITextParserTag>();

        private Stack<KeyValuePair<string, Tag>> tagStack = new Stack<KeyValuePair<string, Tag>>();

        private List<int> criticalReplaceIndexes = new List<int>();

        private List<ITextParserReplacer> replaces = new List<ITextParserReplacer>();

        private string textToParse;

        public string TextToParse
        {
            get { return textToParse; }
            set { textToParse = value; }
        }

        private List<ITextParserReplacer> CriticalReplaces
        {
            get
            {
                List<ITextParserReplacer> ret = new List<ITextParserReplacer>();
                foreach (int i in criticalReplaceIndexes)
                {
                    ret.Add(replaces[i]);
                }
                return ret;
            }
        }

        public ImprovedTextParser(string textToParse)
        {
            TextToParse = textToParse;
        }

        public ImprovedTextParser()
            : this(String.Empty)
        {
        }

        public void AddTag(string customTag, ITextParserTag tagInfo)
        {
            tagMap.Add(customTag, tagInfo);
        }

        public void AddReplace(ITextParserReplacer replacer)
        {
            if (replacer.IsCritical)
            {
                criticalReplaceIndexes.Add(replaces.Count);
            }
            replaces.Add(replacer);
        }

        public string Parse()
        {
            string text = TextToParse;

            bool searchForNextTag = true;
            bool inTag = false, inCloseTag = false, inQuote = false, inParam = false;

            string currentTag = String.Empty;
            ITextParserTag currentTagInfo = null;

            StringBuilder sb = new StringBuilder();
            StringBuilder appendTo = sb;

            StringBuilder param = new StringBuilder();


            int i = 0, n = text.Length;
            while (i < n)
            {
                char curChar = text[i];
                if (searchForNextTag)
                {
                    int g = i;
                    i = text.IndexOf(customOpenTag, g);
                    if (i < 0)
                    {
                        i = n;
                    }

                    if (i != g)
                    {
                        string textToAppend = text.Substring(g, i - g);
                        textToAppend = PerformReplaces(textToAppend, currentTagInfo);                     
                        appendTo.Append(textToAppend);

                    }
                    if (i == n)
                    {
                        break; //we're done
                    }
                    searchForNextTag = false;
                    curChar = text[i]; //Make sure this one is pointing to the right character
                }

                if (inQuote) //Check first if we're in a quote so we will not take [mytag="]"] and parse it wrong
                {
                    if (curChar == '\n')
                    { //Be a noob and ignore new lines
                        StringBuilder append = new StringBuilder();

                        append.Append(customOpenTag);
                        append.Append(currentTag);
                        append.Append("=\"");
                        append.Append(param.ToString());
                        append.Append(curChar);

                        appendTo.Append(PerformReplaces(append.ToString(), currentTagInfo));

                        inTag = false;
                        inQuote = false;
                        inParam = false;
                        currentTag = String.Empty;
                        searchForNextTag = true;

                    }
                    else if (curChar != '"')
                    {
                        param.Append(curChar);
                    }
                    else //char is "
                    {
                        inQuote = false;
                    }
                }
                else if (curChar == customOpenTag) //Are we starting an opening tag?
                {
                    inTag = true;
                }
                else if (inTag)
                {
                    if (curChar == customEndToken)
                    {
                        inCloseTag = true;
                    }
                    else if (curChar == customCloseTag)
                    {
                        if (inCloseTag)
                        {

                            if (tagMap.ContainsKey(currentTag) && tagStack.Count > 0 && tagStack.Peek().Key == currentTag)
                            { //We have a closing tag which exists in the tagMap and is the next tag that needs to be closed

                                Tag tag = tagStack.Pop().Value;
                                string tagContent = tag.Content.ToString(); //Get all the content that is within this tag
                                string tagParam = tag.Parameter;

                                if (tagStack.Count > 0) //Need to see if we need to append this to anything new
                                { //If there's any more tags we need to append it to that tag and not the outer most stringbuilder
                                    appendTo = tagStack.Peek().Value.Content;
                                }
                                else
                                {
                                    appendTo = sb;
                                }
                                ITextParserTag textParserTag = tagMap[currentTag];

                                if (textParserTag.CanProduceOutput(tagContent, tagParam))
                                { //We're good to go
                                    appendTo.Append(tagMap[currentTag].ProduceOutput(tagContent, tagParam));
                                }
                                else
                                { //No go, just send the inputted text to be output
                                    StringBuilder append = new StringBuilder();
                                    append.Append(customOpenTag);
                                    append.Append(currentTag);
                                    if (tagParam != String.Empty)
                                    {
                                        append.Append("=\"");
                                        append.Append(tagParam);
                                        append.Append('\"');                                     
                                    }
                                    append.Append(customCloseTag);
                                    append.Append(tagContent);
                                    append.Append(customOpenTag);
                                    append.Append(customEndToken);
                                    append.Append(currentTag);
                                    append.Append(customCloseTag);

                                    appendTo.Append(PerformReplaces(append.ToString(), currentTagInfo));
                                }

                            }
                            else
                            { //Tag non existent or would produce bad code, just output the code for it
                                string append = customOpenTag.ToString() + customEndToken.ToString() + currentTag + customCloseTag.ToString();
                                appendTo.Append(PerformReplaces(append, currentTagInfo));

                            }
                            if (tagStack.Count == 0)
                            {
                                currentTagInfo = null;
                            }
                            else
                            {
                                currentTagInfo = tagMap[tagStack.Peek().Key];
                            }
                            inTag = false; //Reset to default values
                            inCloseTag = false;
                            currentTag = String.Empty;
                        }
                        else
                        { //Must be opening tag

                            //Check if the tag is something we need to act on or not
                            if (tagMap.ContainsKey(currentTag))
                            { //Lets rock

                                KeyValuePair<string, Tag> tagData = new KeyValuePair<string, Tag>(currentTag, new Tag());

                                tagStack.Push(tagData); //simply add the tag to the tagStack

                                appendTo = tagData.Value.Content; //Make sure all future stuff will go to this tag
                                tagData.Value.Parameter = param.ToString();
                                param = new StringBuilder();
                                if (tagStack.Count == 0)
                                {
                                    currentTagInfo = null;
                                }
                                else
                                {
                                    currentTagInfo = tagMap[tagStack.Peek().Key];
                                }
                            }
                            else
                            { //No match - lets just output the stuff and pretend we never saw it
                                appendTo.Append(customOpenTag);
                                appendTo.Append(currentTag);
                                appendTo.Append(customCloseTag);
                            }
                            inTag = false;
                            inParam = false;
                            currentTag = String.Empty;
                        }
                        searchForNextTag = true;
                    }
                    else if (inParam)
                    {
                        if (curChar == ' '|| (curChar == '"' && param.Length != 0))
                        { //Don't accept spaces without a quote around it, end all of it
                            StringBuilder append = new StringBuilder(customOpenTag);
                            append.Append(currentTag);
                            append.Append('=');
                            append.Append(param.ToString());
                            append.Append(curChar);
                            appendTo.Append(PerformReplaces(append.ToString(), currentTagInfo));
                            
                            //End everything and search for next tag
                            currentTag = String.Empty;
                            inTag = false;
                            inParam = false;
                            inQuote = false;
                            searchForNextTag = true;
                        }
                        else if (curChar == '"') //Start a new quote
                        {
                            inQuote = true;
                        } else
                        {//Ok keep on going
                            param.Append(curChar);
                        }
                    }
                    else if (curChar == '=')
                    {
                        inParam = true;
                    }
                    else
                    { //For now just assume it'll be a part of the tag - later on we need to parse parameters and etc
                        currentTag += curChar;
                    }
                }


                i++;
            }
            if (inTag)
            { //Stuff left undone
                appendTo.Append(customOpenTag);
                if (inCloseTag)
                    appendTo.Append(customEndToken);
                appendTo.Append(currentTag);
                if (param.Length > 0)
                {
                    appendTo.Append('\"');
                    appendTo.Append(param);
                    appendTo.Append('\"');
                }
            }
            while(tagStack.Count > 0){
                KeyValuePair<string, Tag> tagData = tagStack.Pop();
                ITextParserTag parserTag = tagMap[tagData.Key];
                appendTo = (tagStack.Count > 0) ? tagStack.Peek().Value.Content : sb;
                appendTo.Append(parserTag.ProduceOutput(tagData.Value.Content.ToString(), tagData.Value.Content.ToString()));

            }
            
            return sb.ToString();
        }

        private string PerformReplaces(string p, ITextParserTag tagInfo)
        {
            p = (StripHTMLTags)? stripHTMLTags(p):p;
            List<ITextParserReplacer> contentReplacers = CriticalReplaces;
            //It is needed to turn around a bit on things here of what would be logical, otherwise we'd get a NullReferenceException here as tagInfo can be null (notouch = false)
            if (tagInfo == null || tagInfo.NoTouchContent)
            {
                contentReplacers = replaces;
            }
            foreach (ITextParserReplacer replacer in contentReplacers)
            {
                p = replacer.PerformReplace(p);
            }
            return p;
        }

        private string stripHTMLTags(string txt)
        {
            return System.Web.HttpUtility.HtmlEncode(txt);
        }
	
    }
}