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("\"");"); 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 match; List 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(new string[]{match}), new List(new string[]{replace}), critical) { } public TextReplacer(string[] match, string[] replace, bool critical) : this(new List(match), new List(replace), critical) { } public TextReplacer(List match, List replace) : this(match, replace, false) { } public TextReplacer(List match, List 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 tagMap = new Dictionary(); private Stack> tagStack = new Stack>(); private List criticalReplaceIndexes = new List(); private List replaces = new List(); private string textToParse; public string TextToParse { get { return textToParse; } set { textToParse = value; } } private List CriticalReplaces { get { List ret = new List(); 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 tagData = new KeyValuePair(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 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 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); } } }