Mercurial > pub > bltoolkit
view Source/Data/Linq/Builder/ExpressionTestGenerator.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line source
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BLToolkit.Linq; namespace BLToolkit.Data.Linq.Builder { class ExpressionTestGenerator { readonly StringBuilder _exprBuilder = new StringBuilder(); string _indent = "\t\t\t\t"; void PushIndent() { _indent += '\t'; } void PopIndent () { _indent = _indent.Substring(1); } readonly HashSet<Expression> _visitedExprs = new HashSet<Expression>(); bool BuildExpression(Expression expr) { switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { var e = (BinaryExpression)expr; _exprBuilder.Append("("); e.Left.Visit(new Func<Expression,bool>(BuildExpression)); switch (expr.NodeType) { case ExpressionType.Add : case ExpressionType.AddChecked : _exprBuilder.Append(" + "); break; case ExpressionType.And : _exprBuilder.Append(" & "); break; case ExpressionType.AndAlso : _exprBuilder.Append(" && "); break; #if FW4 || SILVERLIGHT case ExpressionType.Assign : _exprBuilder.Append(" = "); break; #endif case ExpressionType.Coalesce : _exprBuilder.Append(" ?? "); break; case ExpressionType.Divide : _exprBuilder.Append(" / "); break; case ExpressionType.Equal : _exprBuilder.Append(" == "); break; case ExpressionType.ExclusiveOr : _exprBuilder.Append(" ^ "); break; case ExpressionType.GreaterThan : _exprBuilder.Append(" > "); break; case ExpressionType.GreaterThanOrEqual : _exprBuilder.Append(" >= "); break; case ExpressionType.LeftShift : _exprBuilder.Append(" << "); break; case ExpressionType.LessThan : _exprBuilder.Append(" < "); break; case ExpressionType.LessThanOrEqual : _exprBuilder.Append(" <= "); break; case ExpressionType.Modulo : _exprBuilder.Append(" % "); break; case ExpressionType.Multiply : case ExpressionType.MultiplyChecked : _exprBuilder.Append(" * "); break; case ExpressionType.NotEqual : _exprBuilder.Append(" != "); break; case ExpressionType.Or : _exprBuilder.Append(" | "); break; case ExpressionType.OrElse : _exprBuilder.Append(" || "); break; case ExpressionType.Power : _exprBuilder.Append(" ** "); break; case ExpressionType.RightShift : _exprBuilder.Append(" >> "); break; case ExpressionType.Subtract : case ExpressionType.SubtractChecked : _exprBuilder.Append(" - "); break; } e.Right.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(")"); return false; } case ExpressionType.ArrayLength: { var e = (UnaryExpression)expr; e.Operand.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(".Length"); return false; } case ExpressionType.Convert: case ExpressionType.ConvertChecked: { var e = (UnaryExpression)expr; _exprBuilder.AppendFormat("({0})", GetTypeName(e.Type)); e.Operand.Visit(new Func<Expression,bool>(BuildExpression)); return false; } case ExpressionType.Negate: case ExpressionType.NegateChecked: { _exprBuilder.Append("-"); return true; } case ExpressionType.Not: { _exprBuilder.Append("!"); return true; } case ExpressionType.Quote: return true; case ExpressionType.TypeAs: { var e = (UnaryExpression)expr; _exprBuilder.Append("("); e.Operand.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.AppendFormat(" as {0})", GetTypeName(e.Type)); return false; } case ExpressionType.UnaryPlus: { _exprBuilder.Append("+"); return true; } case ExpressionType.ArrayIndex: { var e = (BinaryExpression)expr; e.Left.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append("["); e.Right.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append("]"); return false; } case ExpressionType.MemberAccess : { var e = (MemberExpression)expr; e.Expression.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.AppendFormat(".{0}", e.Member.Name); return false; } case ExpressionType.Parameter : { var e = (ParameterExpression)expr; _exprBuilder.Append(e.Name); return false; } case ExpressionType.Call : { var ex = (MethodCallExpression)expr; var mi = ex.Method; var attrs = mi.GetCustomAttributes(typeof(ExtensionAttribute), false); if (attrs.Length != 0) { ex.Arguments[0].Visit(new Func<Expression,bool>(BuildExpression)); PushIndent(); _exprBuilder.AppendLine().Append(_indent); } else if (ex.Object != null) ex.Object.Visit(new Func<Expression,bool>(BuildExpression)); else _exprBuilder.Append(GetTypeName(mi.DeclaringType)); _exprBuilder.Append(".").Append(mi.Name); if (mi.IsGenericMethod && mi.GetGenericArguments().Select(GetTypeName).All(t => t != null)) { _exprBuilder .Append("<") .Append(GetTypeNames(mi.GetGenericArguments(), ",")) .Append(">"); } _exprBuilder.Append("("); PushIndent(); var n = attrs.Length != 0 ? 1 : 0; for (var i = n; i < ex.Arguments.Count; i++) { if (i != n) _exprBuilder.Append(","); _exprBuilder.AppendLine().Append(_indent); ex.Arguments[i].Visit(new Func<Expression,bool>(BuildExpression)); } PopIndent(); _exprBuilder.Append(")"); if (attrs.Length != 0) { PopIndent(); } return false; } case ExpressionType.Constant: { var c = (ConstantExpression)expr; if (c.Value is IQueryable) { var e = ((IQueryable)c.Value).Expression; if (_visitedExprs.Add(e)) { e.Visit(new Func<Expression,bool>(BuildExpression)); return false; } } _exprBuilder.Append(expr); return true; } case ExpressionType.Lambda: { var le = (LambdaExpression)expr; var ps = le.Parameters .Select(p => (GetTypeName(p.Type) + " " + p.Name).TrimStart()) .Aggregate("", (p1, p2) => p1 + ", " + p2, p => p.TrimStart(',', ' ')); _exprBuilder.Append("(").Append(ps).Append(") => "); le.Body.Visit(new Func<Expression,bool>(BuildExpression)); return false; } case ExpressionType.Conditional: { var e = (ConditionalExpression)expr; _exprBuilder.Append("("); e.Test.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(" ? "); e.IfTrue.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(" : "); e.IfFalse.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(")"); return false; } case ExpressionType.New: { var ne = (NewExpression)expr; if (IsAnonymous(ne.Type)) { if (ne.Members.Count == 1) { _exprBuilder.AppendFormat("new {{ {0} = ", ne.Members[0].Name); ne.Arguments[0].Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(" }}"); } else { _exprBuilder.AppendLine("new").Append(_indent).Append("{"); PushIndent(); for (var i = 0; i < ne.Members.Count; i++) { _exprBuilder.AppendLine().Append(_indent).AppendFormat("{0} = ", ne.Members[i].Name); ne.Arguments[i].Visit(new Func<Expression,bool>(BuildExpression)); if (i + 1 < ne.Members.Count) _exprBuilder.Append(","); } PopIndent(); _exprBuilder.AppendLine().Append(_indent).Append("}"); } } else { _exprBuilder.AppendFormat("new {0}(", GetTypeName(ne.Type)); for (var i = 0; i < ne.Arguments.Count; i++) { ne.Arguments[i].Visit(new Func<Expression,bool>(BuildExpression)); if (i + 1 < ne.Arguments.Count) _exprBuilder.Append(", "); } _exprBuilder.Append(")"); } return false; } case ExpressionType.MemberInit: { Func<MemberBinding,bool> modify = b => { switch (b.BindingType) { case MemberBindingType.Assignment : var ma = (MemberAssignment)b; _exprBuilder.AppendFormat("{0} = ", ma.Member.Name); ma.Expression.Visit(new Func<Expression,bool>(BuildExpression)); break; default: _exprBuilder.Append(b.ToString()); break; } return true; }; var e = (MemberInitExpression)expr; e.NewExpression.Visit(new Func<Expression,bool>(BuildExpression)); if (e.Bindings.Count == 1) { _exprBuilder.Append(" { "); modify(e.Bindings[0]); _exprBuilder.Append(" }"); } else { _exprBuilder.AppendLine().Append(_indent).Append("{"); PushIndent(); for (var i = 0; i < e.Bindings.Count; i++) { _exprBuilder.AppendLine().Append(_indent); modify(e.Bindings[i]); if (i + 1 < e.Bindings.Count) _exprBuilder.Append(","); } PopIndent(); _exprBuilder.AppendLine().Append(_indent).Append("}"); } return false; } case ExpressionType.NewArrayInit: { var e = (NewArrayExpression)expr; _exprBuilder.AppendFormat("new {0}[]", GetTypeName(e.Type.GetElementType())); if (e.Expressions.Count == 1) { _exprBuilder.Append(" { "); e.Expressions[0].Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(" }"); } else { _exprBuilder.AppendLine().Append(_indent).Append("{"); PushIndent(); for (var i = 0; i < e.Expressions.Count; i++) { _exprBuilder.AppendLine().Append(_indent); e.Expressions[i].Visit(new Func<Expression,bool>(BuildExpression)); if (i + 1 < e.Expressions.Count) _exprBuilder.Append(","); } PopIndent(); _exprBuilder.AppendLine().Append(_indent).Append("}"); } return false; } case ExpressionType.TypeIs: { var e = (TypeBinaryExpression)expr; _exprBuilder.Append("("); e.Expression.Visit(new Func<Expression, bool>(BuildExpression)); _exprBuilder.AppendFormat(" is {0})", e.TypeOperand); return false; } case ExpressionType.ListInit: { var e = (ListInitExpression)expr; e.NewExpression.Visit(new Func<Expression,bool>(BuildExpression)); if (e.Initializers.Count == 1) { _exprBuilder.Append(" { "); e.Initializers[0].Arguments[0].Visit(new Func<Expression, bool>(BuildExpression)); _exprBuilder.Append(" }"); } else { _exprBuilder.AppendLine().Append(_indent).Append("{"); PushIndent(); for (var i = 0; i < e.Initializers.Count; i++) { _exprBuilder.AppendLine().Append(_indent); e.Initializers[i].Arguments[0].Visit(new Func<Expression,bool>(BuildExpression)); if (i + 1 < e.Initializers.Count) _exprBuilder.Append(","); } PopIndent(); _exprBuilder.AppendLine().Append(_indent).Append("}"); } return false; } case ExpressionType.Invoke: { var e = (InvocationExpression)expr; _exprBuilder.Append("Expression.Invoke("); e.Expression.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Append(", ("); for (var i = 0; i < e.Arguments.Count; i++) { e.Arguments[i].Visit(new Func<Expression,bool>(BuildExpression)); if (i + 1 < e.Arguments.Count) _exprBuilder.Append(", "); } _exprBuilder.Append("))"); return false; } default: _exprBuilder.AppendLine("// Unknown expression.").Append(_indent).Append(expr); return false; } } readonly Dictionary<Type,string> _typeNames = new Dictionary<Type,string> { { typeof(object), "object" }, { typeof(bool), "bool" }, { typeof(int), "int" }, { typeof(string), "string" }, }; readonly StringBuilder _typeBuilder = new StringBuilder(); void BuildType(Type type) { if (type.Namespace != null && type.Namespace.StartsWith("System") || IsAnonymous(type) || type.Assembly == GetType().Assembly || type.IsGenericType && type.GetGenericTypeDefinition() != type) return; var name = type.Name;//.Replace('<', '_').Replace('>', '_').Replace('`', '_').Replace("__f__", ""); var idx = name.LastIndexOf("`"); if (idx > 0) name = name.Substring(0, idx); if (type.IsGenericType) type = type.GetGenericTypeDefinition(); var baseClasses = new[] { type.BaseType } .Where(t => t != null && t != typeof(object)) .Concat(type.GetInterfaces()).ToArray(); var ctors = type.GetConstructors().Select(c => { #if SILVERLIGHT var attrs = c.GetCustomAttributes(false).ToList(); #else var attrs = c.GetCustomAttributesData(); #endif var ps = c.GetParameters().Select(p => GetTypeName(p.ParameterType) + " " + p.Name).ToArray(); return string.Format( "{0}\n\t\tpublic {1}({2})\n\t\t{{\n\t\t\tthrow new NotImplementedException();\n\t\t}}", attrs.Count > 0 ? attrs.Select(a => "\n\t\t" + a.ToString()).Aggregate((a1,a2) => a1 + a2) : "", name, ps.Length == 0 ? "" : ps.Aggregate((s,t) => s + ", " + t)); }).ToList(); if (ctors.Count == 1 && ctors[0].IndexOf("()") >= 0) ctors.Clear(); var members = type.GetFields().Intersect(_usedMembers.OfType<FieldInfo>()).Select(f => { #if SILVERLIGHT var attrs = f.GetCustomAttributes(false).ToList(); #else var attrs = f.GetCustomAttributesData(); #endif return string.Format( "{0}\n\t\tpublic {1} {2};", attrs.Count > 0 ? attrs.Select(a => "\n\t\t" + a.ToString()).Aggregate((a1,a2) => a1 + a2) : "", GetTypeName(f.FieldType), f.Name); }) .Concat( type.GetProperties().Intersect(_usedMembers.OfType<PropertyInfo>()).Select(p => { #if SILVERLIGHT var attrs = p.GetCustomAttributes(false).ToList(); #else var attrs = p.GetCustomAttributesData(); #endif return string.Format( "{0}\n\t\t{3}{1} {2} {{ get; set; }}", attrs.Count > 0 ? attrs.Select(a => "\n\t\t" + a.ToString()).Aggregate((a1,a2) => a1 + a2) : "", GetTypeName(p.PropertyType), p.Name, type.IsInterface ? "" : "public "); })) .Concat( type.GetMethods().Intersect(_usedMembers.OfType<MethodInfo>()).Select(m => { #if SILVERLIGHT var attrs = m.GetCustomAttributes(false).ToList(); #else var attrs = m.GetCustomAttributesData(); #endif var ps = m.GetParameters().Select(p => GetTypeName(p.ParameterType) + " " + p.Name).ToArray(); return string.Format( "{0}\n\t\t{5}{4}{1} {2}({3})\n\t\t{{\n\t\t\tthrow new NotImplementedException();\n\t\t}}", attrs.Count > 0 ? attrs.Select(a => "\n\t\t" + a.ToString()).Aggregate((a1,a2) => a1 + a2) : "", GetTypeName(m.ReturnType), m.Name, ps.Length == 0 ? "" : ps.Aggregate((s,t) => s + ", " + t), m.IsStatic ? "static " : m.IsVirtual ? "virtual " : m.IsAbstract ? "abstract " : "", type.IsInterface ? "" : "public "); })) .ToArray(); { #if SILVERLIGHT var attrs = type.GetCustomAttributes(false).ToList(); #else var attrs = type.GetCustomAttributesData(); #endif _typeBuilder.AppendFormat( type.IsGenericType ? @" namespace {0} {{{8} {6}{7}{1} {2}<{3}>{5} {{{4}{9} }} }} " : @" namespace {0} {{{8} {6}{7}{1} {2}{5} {{{4}{9} }} }} ", type.Namespace, type.IsInterface ? "interface" : type.IsClass ? "class" : "struct", name, type.IsGenericType ? GetTypeNames(type.GetGenericArguments(), ",") : null, ctors.Count == 0 ? "" : ctors.Aggregate((s,t) => s + "\n" + t), baseClasses.Length == 0 ? "" : " : " + GetTypeNames(baseClasses), type.IsPublic ? "public " : "", type.IsAbstract && !type.IsInterface ? "abstract " : "", attrs.Count > 0 ? attrs.Select(a => "\n\t" + a.ToString()).Aggregate((a1,a2) => a1 + a2) : "", members.Length > 0 ? (ctors.Count != 0 ? "\n" : "") + members.Aggregate((f1,f2) => f1 + "\n" + f2) : ""); } } string GetTypeNames(IEnumerable<Type> types, string separator = ", ") { return types.Select(GetTypeName).Aggregate("", (t1,t2) => t1 + separator + t2, p => p.TrimStart(separator.ToCharArray())); } bool IsAnonymous(Type type) { return type.Name.StartsWith("<>f__AnonymousType"); } string GetTypeName(Type type) { if (type == null || type == typeof(object)) return null; if (type.IsGenericParameter) return type.ToString(); string name; if (_typeNames.TryGetValue(type, out name)) return name; if (IsAnonymous(type)) { _typeNames[type] = null; return null; } if (type.IsGenericType) { var args = type.GetGenericArguments(); name = ""; if (type.Namespace != "System") name = type.Namespace + "."; name += type.Name; var idx = name.LastIndexOf("`"); if (idx > 0) name = name.Substring(0, idx); if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) { name = string.Format("{0}?", GetTypeName(args[0])); } else { name = string.Format("{0}<{1}>", name, args.Select(GetTypeName).Aggregate("", (s,t) => s + "," + t, p => p.TrimStart(','))); } _typeNames[type] = name; return name; } if (type.Namespace == "System") return type.Name; return type.ToString(); } readonly HashSet<MemberInfo> _usedMembers = new HashSet<MemberInfo>(); void VisitMembers(Expression expr) { switch (expr.NodeType) { case ExpressionType.Call : { var ex = (MethodCallExpression)expr; _usedMembers.Add(ex.Method); if (ex.Method.IsGenericMethod) { var gmd = ex.Method.GetGenericMethodDefinition(); if (gmd != ex.Method) _usedMembers.Add(gmd); var ga = ex.Method.GetGenericArguments(); foreach (var type in ga) _usedMembers.Add(type); } break; } case ExpressionType.MemberAccess : { var ex = (MemberExpression)expr; _usedMembers.Add(ex.Member); break; } case ExpressionType.MemberInit : { var ex = (MemberInitExpression)expr; Action<IEnumerable<MemberBinding>> visit = null; visit = bs => { foreach (var b in bs) { _usedMembers.Add(b.Member); switch (b.BindingType) { case MemberBindingType.MemberBinding : visit(((MemberMemberBinding)b).Bindings); break; }} }; visit(ex.Bindings); break; } } } readonly HashSet<Type> _usedTypes = new HashSet<Type>(); void AddType(Type type) { if (type == null || type == typeof(object) || type.IsGenericParameter || _usedTypes.Contains(type)) return; _usedTypes.Add(type); if (type.IsGenericType) foreach (var arg in type.GetGenericArguments()) AddType(arg); if (type.IsGenericType && type.GetGenericTypeDefinition() != type) AddType(type.GetGenericTypeDefinition()); AddType(type.BaseType); foreach (var i in type.GetInterfaces()) AddType(i); } void VisitTypes(Expression expr) { AddType(expr.Type); switch (expr.NodeType) { case ExpressionType.Call : { var ex = (MethodCallExpression)expr; var mi = ex.Method; AddType(mi.DeclaringType); AddType(mi.ReturnType); foreach (var arg in mi.GetGenericArguments()) AddType(arg); break; } } } public string GenerateSource(Expression expr) { string fileName = null; StreamWriter sw = null; try { var dir = Path.Combine(Path.GetTempPath(), "bltookit\\"); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); var number = 0;//DateTime.Now.Ticks; fileName = Path.Combine(dir, "ExpressionTest." + number + ".cs"); expr.Visit(new Action<Expression>(VisitMembers)); expr.Visit(new Action<Expression>(VisitTypes)); foreach (var type in _usedTypes.OrderBy(t => t.Namespace).ThenBy(t => t.Name)) BuildType(type); expr.Visit(new Func<Expression,bool>(BuildExpression)); _exprBuilder.Replace("<>h__TransparentIdentifier", "tp"); sw = File.CreateText(fileName); sw.WriteLine(@"//--------------------------------------------------------------------------------------------------- // This code was generated by BLToolkit. //--------------------------------------------------------------------------------------------------- using System; using System.Linq.Expressions; using NUnit.Framework; {0} namespace Data.Linq.UserTests {{ [TestFixture] public class UserTest : TestBase {{ [Test] public void Test([DataContexts] string context) {{ // {1} using (var db = GetDataContext(context)) {{ {2}; }} }} }} }} ", _typeBuilder, expr, _exprBuilder); } catch(Exception ex) { if (sw != null) { sw.WriteLine(); sw.WriteLine(ex.GetType()); sw.WriteLine(ex.Message); sw.WriteLine(ex.StackTrace); } } finally { if (sw != null) sw.Close(); } return fileName; } } }