Created
January 30, 2013 11:49
-
-
Save choffmeister/4672791 to your computer and use it in GitHub Desktop.
Generates source code for a Visitor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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