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);
}
}
}