I've made a small class whose purpose is to generate dynamic HTML. However, it seems that my implementation turned into a state machine algorithm. And so here am I seeking help on a possible design pattern that I may apply, so my code can be more readable, objected oriented and more maintainable.
public interface IViewContext
{
object GetContent();
}
public delegate object BindingModelToHtmlEh(object sender, BindingModelToHtmlArgs args);
public class BindingModelToHtmlArgs : EventArgs
{
public BindingModelToHtmlArgs(Type hint, Type domain)
{
this.Hint = hint;
this.Domain = domain;
}
public Type Hint { get; private set; }
public Type Domain { get; set; }
}
public class HtmlViewContext : IViewContext
{
private readonly List<object> htmlList;
private HtmlViewContext parent;
public event BindingModelToHtmlEh ProvideValue;
protected virtual object OnProvideValue(BindingModelToHtmlArgs args)
{
BindingModelToHtmlEh handler = ProvideValue;
if (handler != null) return handler(this, args);
return null;
}
public HtmlViewContext()
{
htmlList = new List<object>();
}
public HtmlViewContext(HtmlViewContext ctx): this()
{
parent = ctx;
}
public void AddContent(string html)
{
if (parent != null)
{
parent.AddContent(html);
return;
}
htmlList.Add(html);
}
public void AddContent(Type type, Func<object, string> html)
{
if (parent != null)
{
parent.AddContent(type, html);
return;
}
htmlList.Add(new Tuple<Type, Func<object, string>>(type, html));
}
public void AddContent(Type type, Func<IEnumerable<object>, string> html)
{
if (parent != null)
{
parent.AddContent(type, html);
return;
}
htmlList.Add(new Tuple<Type, Func<IEnumerable<object>, string>>(type, html));
}
public object GetContent()
{
HtmlViewResult result = new HtmlViewResult(this);
result.GetResult();
while (result.Continues)
{
object value = OnProvideValue(new BindingModelToHtmlArgs(result.Hint, result.Domain));
result.GetResult(value);
}
return result.GetResult();
}
}
private class HtmlViewResult
{
private readonly HtmlViewContext htmlView;
private readonly IEnumerator<object> it;
private object currentState;
private readonly StringBuilder builder;
public HtmlViewResult(HtmlViewContext htmlView)
{
this.htmlView = htmlView;
Continues = true;
this.builder = new StringBuilder();
it = GetHtmlContent();
}
private object GetResult()
{
while (it.MoveNext())
{
if (it.Current != null)
{
builder.Append(it.Current);
}
else
{
break;
}
}
return builder.ToString();
}
internal object GetResult(object context)
{
currentState = context;
it.MoveNext();
builder.Append(it.Current);
builder.Append(GetResult());
return builder.ToString();
}
public bool Continues
{
get;
set;
}
public Type Hint
{
get; private set;
}
public Type Domain
{
get; private set;
}
public IEnumerator<string> GetHtmlContent()
{
foreach (object obj in htmlView.htmlList)
{
string html = obj as string;
if (html != null)
{
yield return html;
}
else
{
Hint = obj.GetType();
if (currentState != null)
{
throw new InvalidOperationException("invalid state");
}
var f1 = obj as Tuple<Type, Func<object, string>>;
if (f1 != null)
{
Domain = f1.Item1;
}
var f2 = obj as Tuple<Type, Func<IEnumerable<object>, string>>;
if (f2 != null)
{
Domain = f2.Item1;
}
yield return null;
if (f1 != null)
{
yield return f1.Item2(currentState);
}
if (f2 != null)
{
yield return f2.Item2(currentState as IEnumerable<object>);
}
currentState = null;
Hint = null;
Domain = null;
}
}
Continues = false;
}
}