Skip to content

Instantly share code, notes, and snippets.

@choffmeister
Created January 30, 2013 11:49
Show Gist options
  • Save choffmeister/4672791 to your computer and use it in GitHub Desktop.
Save choffmeister/4672791 to your computer and use it in GitHub Desktop.
Generates source code for a Visitor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Choffmeister.Utils
{
public static class VisitorGenerator
{
public static string GenerateVisitor(this Type baseType, string nameSpace, string visitorName, Func<string, string> visitNameConversion = null)
{
StringBuilder sb = new StringBuilder();
Assembly assembly = Assembly.GetAssembly(baseType);
List<Type> subTypes = assembly.GetTypes().Where(n => baseType.IsAssignableFrom(n)).ToList();
List<Type> subTypesWithChildren = new List<Type>();
sb.AppendLine(0, "using System;");
sb.AppendLine();
sb.AppendLine(0, "namespace " + nameSpace);
sb.AppendLine(0, "{{");
sb.AppendLine(1, "public abstract class {0}", visitorName);
sb.AppendLine(1, "{{");
sb.AppendLine(2, "public void Visit({0} element)", baseType.Name);
sb.AppendLine(2, "{{");
foreach (Type subType in subTypes.Where(n => n.IsAbstract == false))
{
List<Tuple<PropertyInfo, Type>> childrenProperties = subType.GetEnumerableChildrenProperties(subTypes);
sb.AppendLine(3, "if (element.GetType() == typeof({0}))", subType.Name);
sb.AppendLine(3, "{{");
sb.AppendLine(4, "{0} castedElement = ({0})element;", subType.Name);
if (childrenProperties.Count > 0)
{
subTypesWithChildren.Add(subType);
sb.AppendLine(4, "this.Visit{0}Pre(castedElement);", visitNameConversion != null ? visitNameConversion(subType.Name) : subType.Name, subType.Name);
sb.AppendLine();
foreach (Tuple<PropertyInfo, Type> property in childrenProperties)
{
sb.AppendLine(4, "foreach ({0} child in castedElement.{1})", property.Item2.Name, property.Item1.Name);
sb.AppendLine(4, "{{");
sb.AppendLine(5, "this.Visit(child);");
sb.AppendLine(4, "}}");
sb.AppendLine();
}
sb.AppendLine(4, "this.Visit{0}Post(castedElement);", visitNameConversion != null ? visitNameConversion(subType.Name) : subType.Name, subType.Name);
sb.AppendLine();
}
else
{
sb.AppendLine(4, "this.Visit{0}(castedElement);", visitNameConversion != null ? visitNameConversion(subType.Name) : subType.Name, subType.Name);
sb.AppendLine();
}
sb.AppendLine(4, "return;");
sb.AppendLine(3, "}}");
sb.AppendLine();
}
sb.AppendLine(3, "throw new NotSupportedException(string.Format(\"Element of type '{{0}}' is not supported\", element.GetType().FullName));");
sb.AppendLine(2, "}}");
sb.AppendLine();
foreach (Type subType in subTypes.Where(n => n.IsAbstract == false))
{
if (subTypesWithChildren.Contains(subType))
{
sb.AppendLine(2, "protected abstract void Visit{0}Pre({1} element);", visitNameConversion != null ? visitNameConversion(subType.Name) : subType.Name, subType.Name);
sb.AppendLine();
sb.AppendLine(2, "protected abstract void Visit{0}Post({1} element);", visitNameConversion != null ? visitNameConversion(subType.Name) : subType.Name, subType.Name);
sb.AppendLine();
}
else
{
sb.AppendLine(2, "protected abstract void Visit{0}({1} element);", visitNameConversion != null ? visitNameConversion(subType.Name) : subType.Name, subType.Name);
sb.AppendLine();
}
}
sb.AppendLine(1, "}}");
sb.AppendLine(0, "}}");
return sb.ToString();
}
private static List<Tuple<PropertyInfo, Type>> GetEnumerableChildrenProperties(this Type type, List<Type> allowedChildrenTypes)
{
List<Tuple<PropertyInfo, Type>> result = new List<Tuple<PropertyInfo, Type>>();
foreach (PropertyInfo property in type.GetProperties())
{
if (typeof(Array).IsAssignableFrom(property.PropertyType))
{
Type elementType = property.PropertyType.GetElementType();
if (allowedChildrenTypes.Contains(elementType))
{
result.Add(Tuple.Create(property, elementType));
}
}
}
return result;
}
private static void AppendLine(this StringBuilder sb, int indent, string format, params object[] arguments)
{
string indentString = new string(Enumerable.Repeat(' ', indent * 4).ToArray());
sb.AppendLine(indentString + string.Format(format, arguments));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment