Mercurial > pub > bltoolkit
diff Source/Linq/ExpressionHelper.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Linq/ExpressionHelper.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,2298 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +using BLToolkit.Data.Linq.Builder; + +// ReSharper disable ConditionIsAlwaysTrueOrFalse +// ReSharper disable HeuristicUnreachableCode + +namespace BLToolkit.Linq +{ + using Data.Linq; + using Reflection; + + public static class ExpressionHelper + { + #region IsConstant + + public static bool IsConstant(Type type) + { + if (type.IsEnum) + return true; + + switch (Type.GetTypeCode(type)) + { + case TypeCode.Int16 : + case TypeCode.Int32 : + case TypeCode.Int64 : + case TypeCode.UInt16 : + case TypeCode.UInt32 : + case TypeCode.UInt64 : + case TypeCode.SByte : + case TypeCode.Byte : + case TypeCode.Decimal : + case TypeCode.Double : + case TypeCode.Single : + case TypeCode.Boolean : + case TypeCode.String : + case TypeCode.Char : return true; + } + + if (TypeHelper.IsNullableType(type)) + return IsConstant(type.GetGenericArguments()[0]); + + return false; + } + + #endregion + + #region Compare + + internal static bool Compare(Expression expr1, Expression expr2, Dictionary<Expression,QueryableAccessor> queryableAccessorDic) + { + return Compare(expr1, expr2, new HashSet<Expression>(), queryableAccessorDic); + } + + static bool Compare( + Expression expr1, + Expression expr2, + HashSet<Expression> visited, + Dictionary<Expression,QueryableAccessor> queryableAccessorDic) + { + if (expr1 == expr2) + return true; + + if (expr1 == null || expr2 == null || expr1.NodeType != expr2.NodeType || expr1.Type != expr2.Type) + return false; + + switch (expr1.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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 e1 = (BinaryExpression)expr1; + var e2 = (BinaryExpression)expr2; + return + e1.Method == e2.Method && + Compare(e1.Conversion, e2.Conversion, visited, queryableAccessorDic) && + Compare(e1.Left, e2.Left, visited, queryableAccessorDic) && + Compare(e1.Right, e2.Right, visited, queryableAccessorDic); + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + { + var e1 = (UnaryExpression)expr1; + var e2 = (UnaryExpression)expr2; + return e1.Method == e2.Method && Compare(e1.Operand, e2.Operand, visited, queryableAccessorDic); + } + + case ExpressionType.Call: + { + var e1 = (MethodCallExpression)expr1; + var e2 = (MethodCallExpression)expr2; + + if (e1.Arguments.Count != e2.Arguments.Count || e1.Method != e2.Method) + return false; + + if (queryableAccessorDic.Count > 0) + { + QueryableAccessor qa; + + if (queryableAccessorDic.TryGetValue(expr1, out qa)) + return Compare(qa.Queryable.Expression, qa.Accessor(expr2).Expression, visited, queryableAccessorDic); + } + + if (!Compare(e1.Object, e2.Object, visited, queryableAccessorDic)) + return false; + + for (var i = 0; i < e1.Arguments.Count; i++) + if (!Compare(e1.Arguments[i], e2.Arguments[i], visited, queryableAccessorDic)) + return false; + + return true; + } + + case ExpressionType.Conditional: + { + var e1 = (ConditionalExpression)expr1; + var e2 = (ConditionalExpression)expr2; + return + Compare(e1.Test, e2.Test, visited, queryableAccessorDic) && + Compare(e1.IfTrue, e2.IfTrue, visited, queryableAccessorDic) && + Compare(e1.IfFalse, e2.IfFalse, visited, queryableAccessorDic); + } + + case ExpressionType.Constant: + { + var e1 = (ConstantExpression)expr1; + var e2 = (ConstantExpression)expr2; + + if (e1.Value == null && e2.Value == null) + return true; + + if (IsConstant(e1.Type)) + return Equals(e1.Value, e2.Value); + + if (e1.Value == null || e2.Value == null) + return false; + + if (e1.Value is IQueryable) + { + var eq1 = ((IQueryable)e1.Value).Expression; + var eq2 = ((IQueryable)e2.Value).Expression; + + if (visited.Add(eq1)) + return Compare(eq1, eq2, visited, queryableAccessorDic); + } + + return true; + } + + case ExpressionType.Invoke: + { + var e1 = (InvocationExpression)expr1; + var e2 = (InvocationExpression)expr2; + + if (e1.Arguments.Count != e2.Arguments.Count || !Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic)) + return false; + + for (var i = 0; i < e1.Arguments.Count; i++) + if (!Compare(e1.Arguments[i], e2.Arguments[i], visited, queryableAccessorDic)) + return false; + + return true; + } + + case ExpressionType.Lambda: + { + var e1 = (LambdaExpression)expr1; + var e2 = (LambdaExpression)expr2; + + if (e1.Parameters.Count != e2.Parameters.Count || !Compare(e1.Body, e2.Body, visited, queryableAccessorDic)) + return false; + + for (var i = 0; i < e1.Parameters.Count; i++) + if (!Compare(e1.Parameters[i], e2.Parameters[i], visited, queryableAccessorDic)) + return false; + + return true; + } + + case ExpressionType.ListInit: + { + var e1 = (ListInitExpression)expr1; + var e2 = (ListInitExpression)expr2; + + if (e1.Initializers.Count != e2.Initializers.Count || !Compare(e1.NewExpression, e2.NewExpression, visited, queryableAccessorDic)) + return false; + + for (var i = 0; i < e1.Initializers.Count; i++) + { + var i1 = e1.Initializers[i]; + var i2 = e2.Initializers[i]; + + if (i1.Arguments.Count != i2.Arguments.Count || i1.AddMethod != i2.AddMethod) + return false; + + for (var j = 0; j < i1.Arguments.Count; j++) + if (!Compare(i1.Arguments[j], i2.Arguments[j], visited, queryableAccessorDic)) + return false; + } + + return true; + } + + case ExpressionType.MemberAccess: + { + var e1 = (MemberExpression)expr1; + var e2 = (MemberExpression)expr2; + + if (e1.Member == e2.Member) + { + if (e1.Expression == e2.Expression || e1.Expression.Type == e2.Expression.Type) + { + if (queryableAccessorDic.Count > 0) + { + QueryableAccessor qa; + + if (queryableAccessorDic.TryGetValue(expr1, out qa)) + return + Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic) && + Compare(qa.Queryable.Expression, qa.Accessor(expr2).Expression, visited, queryableAccessorDic); + } + } + + return Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic); + } + + return false; + } + + case ExpressionType.MemberInit: + { + var e1 = (MemberInitExpression)expr1; + var e2 = (MemberInitExpression)expr2; + + if (e1.Bindings.Count != e2.Bindings.Count || !Compare(e1.NewExpression, e2.NewExpression, visited, queryableAccessorDic)) + return false; + + Func<MemberBinding,MemberBinding,bool> compareBindings = null; compareBindings = (b1,b2) => + { + if (b1 == b2) + return true; + + if (b1 == null || b2 == null || b1.BindingType != b2.BindingType || b1.Member != b2.Member) + return false; + + switch (b1.BindingType) + { + case MemberBindingType.Assignment: + return Compare(((MemberAssignment)b1).Expression, ((MemberAssignment)b2).Expression, visited, queryableAccessorDic); + + case MemberBindingType.ListBinding: + var ml1 = (MemberListBinding)b1; + var ml2 = (MemberListBinding)b2; + + if (ml1.Initializers.Count != ml2.Initializers.Count) + return false; + + for (var i = 0; i < ml1.Initializers.Count; i++) + { + var ei1 = ml1.Initializers[i]; + var ei2 = ml2.Initializers[i]; + + if (ei1.AddMethod != ei2.AddMethod || ei1.Arguments.Count != ei2.Arguments.Count) + return false; + + for (var j = 0; j < ei1.Arguments.Count; j++) + if (!Compare(ei1.Arguments[j], ei2.Arguments[j], visited, queryableAccessorDic)) + return false; + } + + break; + + case MemberBindingType.MemberBinding: + var mm1 = (MemberMemberBinding)b1; + var mm2 = (MemberMemberBinding)b2; + + if (mm1.Bindings.Count != mm2.Bindings.Count) + return false; + + for (var i = 0; i < mm1.Bindings.Count; i++) + if (!compareBindings(mm1.Bindings[i], mm2.Bindings[i])) + return false; + + break; + } + + return true; + }; + + for (var i = 0; i < e1.Bindings.Count; i++) + { + var b1 = e1.Bindings[i]; + var b2 = e2.Bindings[i]; + + if (!compareBindings(b1, b2)) + return false; + } + + return true; + } + + case ExpressionType.New: + { + var e1 = (NewExpression)expr1; + var e2 = (NewExpression)expr2; + + if (e1.Arguments.Count != e2.Arguments.Count) + return false; + + if (e1.Members == null && e2.Members != null) + return false; + + if (e1.Members != null && e2.Members == null) + return false; + + if (e1.Constructor != e2.Constructor) + return false; + + if (e1.Members != null) + { + if (e1.Members.Count != e2.Members.Count) + return false; + + for (var i = 0; i < e1.Members.Count; i++) + if (e1.Members[i] != e2.Members[i]) + return false; + } + + for (var i = 0; i < e1.Arguments.Count; i++) + if (!Compare(e1.Arguments[i], e2.Arguments[i], visited, queryableAccessorDic)) + return false; + + return true; + } + + case ExpressionType.NewArrayBounds: + case ExpressionType.NewArrayInit: + { + var e1 = (NewArrayExpression)expr1; + var e2 = (NewArrayExpression)expr2; + + if (e1.Expressions.Count != e2.Expressions.Count) + return false; + + for (var i = 0; i < e1.Expressions.Count; i++) + if (!Compare(e1.Expressions[i], e2.Expressions[i], visited, queryableAccessorDic)) + return false; + + return true; + } + + case ExpressionType.Parameter: + { + var e1 = (ParameterExpression)expr1; + var e2 = (ParameterExpression)expr2; + return e1.Name == e2.Name; + } + + case ExpressionType.TypeIs: + { + var e1 = (TypeBinaryExpression)expr1; + var e2 = (TypeBinaryExpression)expr2; + return e1.TypeOperand == e2.TypeOperand && Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic); + } + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block: + { + var e1 = (BlockExpression)expr1; + var e2 = (BlockExpression)expr2; + + for (var i = 0; i < e1.Expressions.Count; i++) + if (!Compare(e1.Expressions[i], e2.Expressions[i], visited, queryableAccessorDic)) + return false; + + for (var i = 0; i < e1.Variables.Count; i++) + if (!Compare(e1.Variables[i], e2.Variables[i], visited, queryableAccessorDic)) + return false; + + return true; + } + +#endif + } + + throw new InvalidOperationException(); + } + + #endregion + + #region Path + + static Expression ConvertTo(Expression expr, Type type) + { + return Expression.Convert(expr, type); + } + + static void Path<T>(IEnumerable<T> source, HashSet<Expression> visited, Expression path, MethodInfo property, Action<T,Expression> func) + where T : class + { + var prop = Expression.Property(path, property); + var i = 0; + foreach (var item in source) + func(item, Expression.Call(prop, ReflectionHelper.IndexExpressor<T>.Item, new Expression[] { Expression.Constant(i++) })); + } + + static void Path<T>(IEnumerable<T> source, HashSet<Expression> visited, Expression path, MethodInfo property, Action<Expression,Expression> func) + where T : Expression + { + var prop = Expression.Property(path, property); + var i = 0; + foreach (var item in source) + Path(item, visited, Expression.Call(prop, ReflectionHelper.IndexExpressor<T>.Item, new Expression[] { Expression.Constant(i++) }), func); + } + + static void Path(Expression expr, HashSet<Expression> visited, Expression path, MethodInfo property, Action<Expression,Expression> func) + { + Path(expr, visited, Expression.Property(path, property), func); + } + + public static void Path(this Expression expr, Expression path, Action<Expression,Expression> func) + { + Path(expr, new HashSet<Expression>(), path, func); + } + + static void Path( + this Expression expr, + HashSet<Expression> visited, + Expression path, + Action<Expression,Expression> func) + { + if (expr == null) + return; + + switch (expr.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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: + { + path = ConvertTo(path, typeof(BinaryExpression)); + var e = (BinaryExpression)expr; + + Path(e.Conversion, visited, path, ReflectionHelper.Binary.Conversion, func); + Path(e.Left, visited, path, ReflectionHelper.Binary.Left, func); + Path(e.Right, visited, path, ReflectionHelper.Binary.Right, func); + + break; + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + Path( + ((UnaryExpression)expr).Operand, + visited, + path = ConvertTo(path, typeof(UnaryExpression)), + ReflectionHelper.Unary.Operand, + func); + break; + + case ExpressionType.Call: + { + path = ConvertTo(path, typeof(MethodCallExpression)); + var e = (MethodCallExpression)expr; + + Path(e.Object, visited, path, ReflectionHelper.MethodCall.Object, func); + Path(e.Arguments, visited, path, ReflectionHelper.MethodCall.Arguments, func); + + break; + } + + case ExpressionType.Conditional: + { + path = ConvertTo(path, typeof(ConditionalExpression)); + var e = (ConditionalExpression)expr; + + Path(e.Test, visited, path, ReflectionHelper.Conditional.Test, func); + Path(e.IfTrue, visited, path, ReflectionHelper.Conditional.IfTrue, func); + Path(e.IfFalse, visited, path, ReflectionHelper.Conditional.IfFalse, func); + + break; + } + + case ExpressionType.Invoke: + { + path = ConvertTo(path, typeof(InvocationExpression)); + var e = (InvocationExpression)expr; + + Path(e.Expression, visited, path, ReflectionHelper.Invocation.Expression, func); + Path(e.Arguments, visited, path, ReflectionHelper.Invocation.Arguments, func); + + break; + } + + case ExpressionType.Lambda: + { + path = ConvertTo(path, typeof(LambdaExpression)); + var e = (LambdaExpression)expr; + + Path(e.Body, visited, path, ReflectionHelper.LambdaExpr.Body, func); + Path(e.Parameters, visited, path, ReflectionHelper.LambdaExpr.Parameters, func); + + break; + } + + case ExpressionType.ListInit: + { + path = ConvertTo(path, typeof(ListInitExpression)); + var e = (ListInitExpression)expr; + + Path(e.NewExpression, visited, path, ReflectionHelper.ListInit.NewExpression, func); + Path(e.Initializers, visited, path, ReflectionHelper.ListInit.Initializers, + (ex,p) => Path(ex.Arguments, visited, p, ReflectionHelper.ElementInit.Arguments, func)); + + break; + } + + case ExpressionType.MemberAccess: + Path( + ((MemberExpression)expr).Expression, + visited, + path = ConvertTo(path, typeof(MemberExpression)), + ReflectionHelper.Member.Expression, + func); + break; + + case ExpressionType.MemberInit: + { + Action<MemberBinding,Expression> modify = null; modify = (b,pinf) => + { + switch (b.BindingType) + { + case MemberBindingType.Assignment: + Path( + ((MemberAssignment)b).Expression, + visited, + ConvertTo(pinf, typeof(MemberAssignment)), + ReflectionHelper.MemberAssignmentBind.Expression, + func); + break; + + case MemberBindingType.ListBinding: + Path( + ((MemberListBinding)b).Initializers, + visited, + ConvertTo(pinf, typeof(MemberListBinding)), + ReflectionHelper.MemberListBind.Initializers, + (p,psi) => Path(p.Arguments, visited, psi, ReflectionHelper.ElementInit.Arguments, func)); + break; + + case MemberBindingType.MemberBinding: + Path( + ((MemberMemberBinding)b).Bindings, + visited, + ConvertTo(pinf, typeof(MemberMemberBinding)), + ReflectionHelper.MemberMemberBind.Bindings, + modify); + break; + } + }; + + path = ConvertTo(path, typeof(MemberInitExpression)); + var e = (MemberInitExpression)expr; + + Path(e.NewExpression, visited, path, ReflectionHelper.MemberInit.NewExpression, func); + Path(e.Bindings, visited, path, ReflectionHelper.MemberInit.Bindings, modify); + + break; + } + + case ExpressionType.New: + Path( + ((NewExpression)expr).Arguments, + visited, + path = ConvertTo(path, typeof(NewExpression)), + ReflectionHelper.New.Arguments, + func); + break; + + case ExpressionType.NewArrayBounds: + Path( + ((NewArrayExpression)expr).Expressions, + visited, + path = ConvertTo(path, typeof(NewArrayExpression)), + ReflectionHelper.NewArray.Expressions, + func); + break; + + case ExpressionType.NewArrayInit: + Path( + ((NewArrayExpression)expr).Expressions, + visited, + path = ConvertTo(path, typeof(NewArrayExpression)), + ReflectionHelper.NewArray.Expressions, + func); + break; + + case ExpressionType.TypeIs: + Path( + ((TypeBinaryExpression)expr).Expression, + visited, + path = ConvertTo(path, typeof(TypeBinaryExpression)), + ReflectionHelper.TypeBinary.Expression, + func); + break; + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block: + { + path = ConvertTo(path, typeof(BlockExpression)); + var e = (BlockExpression)expr; + + Path(e.Expressions, visited, path, ReflectionHelper.Block.Expressions, func); + Path(e.Variables, visited, path, ReflectionHelper.Block.Variables, func); // ? + + break; + } + +#endif + + case ExpressionType.Constant : + { + path = ConvertTo(path, typeof(ConstantExpression)); + var e = (ConstantExpression)expr; + var iq = e.Value as IQueryable; + + if (iq != null && !visited.Contains(iq.Expression)) + { + visited.Add(iq.Expression); + + Expression p = Expression.Property(path, ReflectionHelper.Constant.Value); + p = ConvertTo(p, typeof(IQueryable)); + Path(iq.Expression, visited, p, ReflectionHelper.QueryableInt.Expression, func); + } + + break; + } + + case ExpressionType.Parameter: path = ConvertTo(path, typeof(ParameterExpression)); break; + } + + func(expr, path); + } + + #endregion + + #region Visit + + static void Visit<T>(IEnumerable<T> source, Action<T> func) + { + foreach (var item in source) + func(item); + } + + static void Visit<T>(IEnumerable<T> source, Action<Expression> func) + where T : Expression + { + foreach (var item in source) + Visit(item, func); + } + + public static void Visit(this Expression expr, Action<Expression> func) + { + if (expr == null) + return; + + switch (expr.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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; + + Visit(e.Conversion, func); + Visit(e.Left, func); + Visit(e.Right, func); + + break; + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + Visit(((UnaryExpression)expr).Operand, func); + break; + + case ExpressionType.Call: + { + var e = (MethodCallExpression)expr; + + Visit(e.Object, func); + Visit(e.Arguments, func); + + break; + } + + case ExpressionType.Conditional: + { + var e = (ConditionalExpression)expr; + + Visit(e.Test, func); + Visit(e.IfTrue, func); + Visit(e.IfFalse, func); + + break; + } + + case ExpressionType.Invoke: + { + var e = (InvocationExpression)expr; + + Visit(e.Expression, func); + Visit(e.Arguments, func); + + break; + } + + case ExpressionType.Lambda: + { + var e = (LambdaExpression)expr; + + Visit(e.Body, func); + Visit(e.Parameters, func); + + break; + } + + case ExpressionType.ListInit: + { + var e = (ListInitExpression)expr; + + Visit(e.NewExpression, func); + Visit(e.Initializers, ex => Visit(ex.Arguments, func)); + + break; + } + + case ExpressionType.MemberAccess: Visit(((MemberExpression)expr).Expression, func); break; + + case ExpressionType.MemberInit: + { + Action<MemberBinding> modify = null; modify = b => + { + switch (b.BindingType) + { + case MemberBindingType.Assignment : Visit(((MemberAssignment)b). Expression, func); break; + case MemberBindingType.ListBinding : Visit(((MemberListBinding)b).Initializers, p => Visit(p.Arguments, func)); break; + case MemberBindingType.MemberBinding : Visit(((MemberMemberBinding)b).Bindings, modify); break; + } + }; + + var e = (MemberInitExpression)expr; + + Visit(e.NewExpression, func); + Visit(e.Bindings, modify); + + break; + } + + case ExpressionType.New : Visit(((NewExpression) expr).Arguments, func); break; + case ExpressionType.NewArrayBounds : Visit(((NewArrayExpression) expr).Expressions, func); break; + case ExpressionType.NewArrayInit : Visit(((NewArrayExpression) expr).Expressions, func); break; + case ExpressionType.TypeIs : Visit(((TypeBinaryExpression)expr).Expression, func); break; + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block: + { + var e = (BlockExpression)expr; + + Visit(e.Expressions, func); + Visit(e.Variables, func); + + break; + } + +#endif + + case (ExpressionType)ChangeTypeExpression.ChangeTypeType : + Visit(((ChangeTypeExpression)expr).Expression, func); break; + } + + func(expr); + } + + static void Visit<T>(IEnumerable<T> source, Func<T,bool> func) + { + foreach (var item in source) + func(item); + } + + static void Visit<T>(IEnumerable<T> source, Func<Expression,bool> func) + where T : Expression + { + foreach (var item in source) + Visit(item, func); + } + + public static void Visit(this Expression expr, Func<Expression,bool> func) + { + if (expr == null || !func(expr)) + return; + + switch (expr.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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; + + Visit(e.Conversion, func); + Visit(e.Left, func); + Visit(e.Right, func); + + break; + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + Visit(((UnaryExpression)expr).Operand, func); + break; + + case ExpressionType.Call: + { + var e = (MethodCallExpression)expr; + + Visit(e.Object, func); + Visit(e.Arguments, func); + + break; + } + + case ExpressionType.Conditional: + { + var e = (ConditionalExpression)expr; + + Visit(e.Test, func); + Visit(e.IfTrue, func); + Visit(e.IfFalse, func); + + break; + } + + case ExpressionType.Invoke: + { + var e = (InvocationExpression)expr; + + Visit(e.Expression, func); + Visit(e.Arguments, func); + + break; + } + + case ExpressionType.Lambda: + { + var e = (LambdaExpression)expr; + + Visit(e.Body, func); + Visit(e.Parameters, func); + + break; + } + + case ExpressionType.ListInit: + { + var e = (ListInitExpression)expr; + + Visit(e.NewExpression, func); + Visit(e.Initializers, ex => Visit(ex.Arguments, func)); + + break; + } + + case ExpressionType.MemberAccess: Visit(((MemberExpression)expr).Expression, func); break; + + case ExpressionType.MemberInit: + { + Func<MemberBinding,bool> modify = null; modify = b => + { + switch (b.BindingType) + { + case MemberBindingType.Assignment : Visit(((MemberAssignment)b). Expression, func); break; + case MemberBindingType.ListBinding : Visit(((MemberListBinding)b).Initializers, p => Visit(p.Arguments, func)); break; + case MemberBindingType.MemberBinding : Visit(((MemberMemberBinding)b).Bindings, modify); break; + } + + return true; + }; + + var e = (MemberInitExpression)expr; + + Visit(e.NewExpression, func); + Visit(e.Bindings, modify); + + break; + } + + case ExpressionType.New : Visit(((NewExpression) expr).Arguments, func); break; + case ExpressionType.NewArrayBounds : Visit(((NewArrayExpression) expr).Expressions, func); break; + case ExpressionType.NewArrayInit : Visit(((NewArrayExpression) expr).Expressions, func); break; + case ExpressionType.TypeIs : Visit(((TypeBinaryExpression)expr).Expression, func); break; + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block: + { + var e = (BlockExpression)expr; + + Visit(e.Expressions, func); + Visit(e.Variables, func); + + break; + } + +#endif + + case (ExpressionType)ChangeTypeExpression.ChangeTypeType : + Visit(((ChangeTypeExpression)expr).Expression, func); + break; + } + } + + #endregion + + #region Find + + static Expression Find<T>(IEnumerable<T> source, Func<T,Expression> func) + { + foreach (var item in source) + { + var ex = func(item); + if (ex != null) + return ex; + } + + return null; + } + + static Expression Find<T>(IEnumerable<T> source, Func<Expression,bool> func) + where T : Expression + { + foreach (var item in source) + { + var f = Find(item, func); + if (f != null) + return f; + } + + return null; + } + + public static Expression Find(this Expression expr, Func<Expression,bool> func) + { + if (expr == null || func(expr)) + return expr; + + switch (expr.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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; + + return + Find(e.Conversion, func) ?? + Find(e.Left, func) ?? + Find(e.Right, func); + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + return Find(((UnaryExpression)expr).Operand, func); + + case ExpressionType.Call: + { + var e = (MethodCallExpression)expr; + + return + Find(e.Object, func) ?? + Find(e.Arguments, func); + } + + case ExpressionType.Conditional: + { + var e = (ConditionalExpression)expr; + + return + Find(e.Test, func) ?? + Find(e.IfTrue, func) ?? + Find(e.IfFalse, func); + } + + case ExpressionType.Invoke: + { + var e = (InvocationExpression)expr; + + return + Find(e.Expression, func) ?? + Find(e.Arguments, func); + } + + case ExpressionType.Lambda: + { + var e = (LambdaExpression)expr; + + return + Find(e.Body, func) ?? + Find(e.Parameters, func); + } + + case ExpressionType.ListInit: + { + var e = (ListInitExpression)expr; + + return + Find(e.NewExpression, func) ?? + Find(e.Initializers, ex => Find(ex.Arguments, func)); + } + + case ExpressionType.MemberAccess: + return Find(((MemberExpression)expr).Expression, func); + + case ExpressionType.MemberInit: + { + Func<MemberBinding,Expression> modify = null; modify = b => + { + switch (b.BindingType) + { + case MemberBindingType.Assignment : return Find(((MemberAssignment)b). Expression, func); + case MemberBindingType.ListBinding : return Find(((MemberListBinding)b). Initializers, p => Find(p.Arguments, func)); + case MemberBindingType.MemberBinding : return Find(((MemberMemberBinding)b).Bindings, modify); + } + + return null; + }; + + var e = (MemberInitExpression)expr; + + return + Find(e.NewExpression, func) ?? + Find(e.Bindings, modify); + } + + case ExpressionType.New : return Find(((NewExpression) expr).Arguments, func); + case ExpressionType.NewArrayBounds : return Find(((NewArrayExpression) expr).Expressions, func); + case ExpressionType.NewArrayInit : return Find(((NewArrayExpression) expr).Expressions, func); + case ExpressionType.TypeIs : return Find(((TypeBinaryExpression)expr).Expression, func); + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block: + { + var e = (BlockExpression)expr; + + return + Find(e.Expressions, func) ?? + Find(e.Variables, func); + } + +#endif + + case (ExpressionType)ChangeTypeExpression.ChangeTypeType : + return Find(((ChangeTypeExpression)expr).Expression, func); + } + + return null; + } + + #endregion + + #region Convert + + static IEnumerable<T> Convert<T>(IEnumerable<T> source, Func<T,T> func) + where T : class + { + var modified = false; + var list = new List<T>(); + + foreach (var item in source) + { + var e = func(item); + list.Add(e); + modified = modified || e != item; + } + + return modified ? list : source; + } + + static IEnumerable<T> Convert<T>(IEnumerable<T> source, Func<Expression,Expression> func) + where T : Expression + { + var modified = false; + var list = new List<T>(); + + foreach (var item in source) + { + var e = Convert(item, func); + list.Add((T)e); + modified = modified || e != item; + } + + return modified? list: source; + } + + public static Expression Convert(this Expression expr, Func<Expression,Expression> func) + { + if (expr == null) + return null; + + switch (expr.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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 ex = func(expr); + if (ex != expr) + return ex; + + var e = (BinaryExpression)expr; + var c = Convert(e.Conversion, func); + var l = Convert(e.Left, func); + var r = Convert(e.Right, func); + +#if FW3 + return c != e.Conversion || l != e.Left || r != e.Right ? + Expression.MakeBinary(expr.NodeType, l, r, e.IsLiftedToNull, e.Method, (LambdaExpression)c): + expr; +#else + return e.Update(l, (LambdaExpression)c, r); +#endif + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + { + var ex = func(expr); + if (ex != expr) + return ex; + + var e = (UnaryExpression)expr; + var o = Convert(e.Operand, func); + +#if FW3 + return o != e.Operand ? + Expression.MakeUnary(expr.NodeType, o, e.Type, e.Method) : + expr; +#else + return e.Update(o); +#endif + } + + case ExpressionType.Call: + { + var ex = func(expr); + if (ex != expr) + return ex; + + var e = (MethodCallExpression)expr; + var o = Convert(e.Object, func); + var a = Convert(e.Arguments, func); + +#if FW3 + return o != e.Object || a != e.Arguments ? + Expression.Call(o, e.Method, a) : + expr; +#else + return e.Update(o, a); +#endif + } + + case ExpressionType.Conditional: + { + var ex = func(expr); + if (ex != expr) + return ex; + + var e = (ConditionalExpression)expr; + var s = Convert(e.Test, func); + var t = Convert(e.IfTrue, func); + var f = Convert(e.IfFalse, func); + +#if FW3 + return s != e.Test || t != e.IfTrue || f != e.IfFalse ? + Expression.Condition(s, t, f) : + expr; +#else + return e.Update(s, t, f); +#endif + } + + case ExpressionType.Invoke: + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = (InvocationExpression)expr; + var ex = Convert(e.Expression, func); + var a = Convert(e.Arguments, func); + +#if FW3 + return ex != e.Expression || a != e.Arguments ? Expression.Invoke(ex, a) : expr; +#else + return e.Update(ex, a); +#endif + } + + case ExpressionType.Lambda: + { + var ex = func(expr); + if (ex != expr) + return ex; + + var e = (LambdaExpression)expr; + var b = Convert(e.Body, func); + var p = Convert(e.Parameters, func); + + return b != e.Body || p != e.Parameters ? Expression.Lambda(ex.Type, b, p.ToArray()) : expr; + } + + case ExpressionType.ListInit: + { + var ex = func(expr); + if (ex != expr) + return ex; + + var e = (ListInitExpression)expr; + var n = Convert(e.NewExpression, func); + var i = Convert(e.Initializers, p => + { + var args = Convert(p.Arguments, func); + return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; + }); + +#if FW3 + return n != e.NewExpression || i != e.Initializers ? + Expression.ListInit((NewExpression)n, i) : + expr; +#else + return e.Update((NewExpression)n, i); +#endif + } + + case ExpressionType.MemberAccess: + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = (MemberExpression)expr; + var ex = Convert(e.Expression, func); + +#if FW3 + return ex != e.Expression ? Expression.MakeMemberAccess(ex, e.Member) : expr; +#else + return e.Update(ex); +#endif + } + + case ExpressionType.MemberInit: + { + var exp = func(expr); + if (exp != expr) + return exp; + + Func<MemberBinding,MemberBinding> modify = null; modify = b => + { + switch (b.BindingType) + { + case MemberBindingType.Assignment: + { + var ma = (MemberAssignment)b; + var ex = Convert(ma.Expression, func); + + if (ex != ma.Expression) + ma = Expression.Bind(ma.Member, ex); + + return ma; + } + + case MemberBindingType.ListBinding: + { + var ml = (MemberListBinding)b; + var i = Convert(ml.Initializers, p => + { + var args = Convert(p.Arguments, func); + return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; + }); + + if (i != ml.Initializers) + ml = Expression.ListBind(ml.Member, i); + + return ml; + } + + case MemberBindingType.MemberBinding: + { + var mm = (MemberMemberBinding)b; + var bs = Convert(mm.Bindings, modify); + + if (bs != mm.Bindings) + mm = Expression.MemberBind(mm.Member); + + return mm; + } + } + + return b; + }; + + var e = (MemberInitExpression)expr; + var ne = Convert(e.NewExpression, func); + var bb = Convert(e.Bindings, modify); + +#if FW3 + return ne != e.NewExpression || bb != e.Bindings ? + Expression.MemberInit((NewExpression)ne, bb) : + expr; +#else + return e.Update((NewExpression)ne, bb); +#endif + } + + case ExpressionType.New: + { + var ex = func(expr); + if (ex != expr) + return ex; + + var e = (NewExpression)expr; + var a = Convert(e.Arguments, func); + +#if FW3 + return a != e.Arguments ? + e.Members == null ? + Expression.New(e.Constructor, a) : + Expression.New(e.Constructor, a, e.Members) : + expr; +#else + return e.Update(a); +#endif + } + + case ExpressionType.NewArrayBounds: + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = (NewArrayExpression)expr; + var ex = Convert(e.Expressions, func); + +#if FW3 + return ex != e.Expressions ? Expression.NewArrayBounds(e.Type, ex) : expr; +#else + return e.Update(ex); +#endif + } + + case ExpressionType.NewArrayInit: + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = (NewArrayExpression)expr; + var ex = Convert(e.Expressions, func); + +#if FW3 + return ex != e.Expressions ? + Expression.NewArrayInit(e.Type.GetElementType(), ex) : + expr; +#else + return e.Update(ex); + +#endif + } + + case ExpressionType.TypeIs: + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = (TypeBinaryExpression)expr; + var ex = Convert(e.Expression, func); + +#if FW3 + return ex != e.Expression ? Expression.TypeIs(ex, e.Type) : expr; +#else + return e.Update(ex); +#endif + } + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block: + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = (BlockExpression)expr; + var ex = Convert(e.Expressions, func); + var v = Convert(e.Variables, func); + + return e.Update(v, ex); + } + +#endif + + case ExpressionType.Constant : return func(expr); + case ExpressionType.Parameter: return func(expr); + + case (ExpressionType)ChangeTypeExpression.ChangeTypeType : + { + var exp = func(expr); + if (exp != expr) + return exp; + + var e = expr as ChangeTypeExpression; + var ex = Convert(e.Expression, func); + + if (ex == e.Expression) + return expr; + + if (ex.Type == e.Type) + return ex; + + return new ChangeTypeExpression(ex, e.Type); + } + } + + throw new InvalidOperationException(); + } + + #endregion + + #region Convert2 + + public struct ConvertInfo + { + public ConvertInfo(Expression expression, bool stop) + { + Expression = expression; + Stop = stop; + } + + public ConvertInfo(Expression expression) + { + Expression = expression; + Stop = false; + } + + public Expression Expression; + public bool Stop; + } + + static IEnumerable<T> Convert2<T>(IEnumerable<T> source, Func<T,T> func) + where T : class + { + var modified = false; + var list = new List<T>(); + + foreach (var item in source) + { + var e = func(item); + list.Add(e); + modified = modified || e != item; + } + + return modified ? list : source; + } + + static IEnumerable<T> Convert2<T>(IEnumerable<T> source, Func<Expression,ConvertInfo> func) + where T : Expression + { + var modified = false; + var list = new List<T>(); + + foreach (var item in source) + { + var e = Convert2(item, func); + list.Add((T)e); + modified = modified || e != item; + } + + return modified ? list : source; + } + +#if FW3 + static IEnumerable<Expression> ConvertMethodArguments(IEnumerable<Expression> source, MethodBase method) + { + return ConvertMethodArguments(source, method, null); + } + + static IEnumerable<Expression> ConvertMethodArguments(IEnumerable<Expression> source, MethodBase method, IList<MemberInfo> initMembers) +#else + static IEnumerable<Expression> ConvertMethodArguments(IEnumerable<Expression> source, MethodBase method, IList<MemberInfo> initMembers = null) +#endif + { + var list = new List<Expression>(); + + var targetTypes = new List<Type>(); + foreach (var param in method.GetParameters()) + { + targetTypes.Add(param.ParameterType); + } + if (initMembers != null) + { + foreach (var mi in initMembers) + { + if (mi is PropertyInfo) + { + targetTypes.Add(((PropertyInfo)mi).PropertyType); + } + else if (mi is FieldInfo) + { + targetTypes.Add(((FieldInfo)mi).FieldType); + } + } + } + + var idx = 0; + foreach (var item in source) + { + var targetType = targetTypes[idx]; + if (item.Type != targetType) + { + list.Add(Expression.Convert(item, targetType)); + } + else + { + list.Add(item); + } + idx++; + } + + return list; + } + + public static Expression Convert2(this Expression expr, Func<Expression,ConvertInfo> func) + { + if (expr == null) + return null; + + ConvertInfo ci; + + switch (expr.NodeType) + { + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: +#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: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as BinaryExpression; + var c = Convert2(e.Conversion, func); + var l = Convert2(e.Left, func); + var r = Convert2(e.Right, func); + + return c != e.Conversion || l != e.Left || r != e.Right ? + Expression.MakeBinary(expr.NodeType, l, r, e.IsLiftedToNull, e.Method, (LambdaExpression)c): + expr; + } + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + case ExpressionType.UnaryPlus: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as UnaryExpression; + var o = Convert2(e.Operand, func); + + return o != e.Operand ? + Expression.MakeUnary(expr.NodeType, o, e.Type, e.Method) : + expr; + } + + case ExpressionType.Call: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as MethodCallExpression; + var o = Convert2(e.Object, func); + var a = Convert2(e.Arguments, func); + + return o != e.Object || a != e.Arguments ? + Expression.Call(o, e.Method, ConvertMethodArguments(a, e.Method)) : + expr; + } + + case ExpressionType.Conditional: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as ConditionalExpression; + var s = Convert2(e.Test, func); + var t = Convert2(e.IfTrue, func); + var f = Convert2(e.IfFalse, func); + + return s != e.Test || t != e.IfTrue || f != e.IfFalse ? + Expression.Condition(s, t, f) : + expr; + } + + case ExpressionType.Invoke: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as InvocationExpression; + var ex = Convert2(e.Expression, func); + var a = Convert2(e.Arguments, func); + + return ex != e.Expression || a != e.Arguments ? Expression.Invoke(ex, a) : expr; + } + + case ExpressionType.Lambda: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as LambdaExpression; + var b = Convert2(e.Body, func); + var p = Convert2(e.Parameters, func); + + return b != e.Body || p != e.Parameters ? Expression.Lambda(ci.Expression.Type, b, p.ToArray()) : expr; + } + + case ExpressionType.ListInit: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as ListInitExpression; + var n = Convert2(e.NewExpression, func); + var i = Convert2(e.Initializers, p => + { + var args = Convert2(p.Arguments, func); + return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; + }); + + return n != e.NewExpression || i != e.Initializers ? + Expression.ListInit((NewExpression)n, i) : + expr; + } + + case ExpressionType.MemberAccess: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as MemberExpression; + var ex = Convert2(e.Expression, func); + + return ex != e.Expression ? Expression.MakeMemberAccess(ex, e.Member) : expr; + } + + case ExpressionType.MemberInit: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + Func<MemberBinding,MemberBinding> modify = null; modify = b => + { + switch (b.BindingType) + { + case MemberBindingType.Assignment: + { + var ma = (MemberAssignment)b; + var ex = Convert2(ma.Expression, func); + + if (ex != ma.Expression) + { + if (ex.Type != ma.Expression.Type) + { + ex = Expression.Convert(ex, ma.Expression.Type); + } + ma = Expression.Bind(ma.Member, ex); + } + + return ma; + } + + case MemberBindingType.ListBinding: + { + var ml = (MemberListBinding)b; + var i = Convert(ml.Initializers, p => + { + var args = Convert2(p.Arguments, func); + return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; + }); + + if (i != ml.Initializers) + ml = Expression.ListBind(ml.Member, i); + + return ml; + } + + case MemberBindingType.MemberBinding: + { + var mm = (MemberMemberBinding)b; + var bs = Convert(mm.Bindings, modify); + + if (bs != mm.Bindings) + mm = Expression.MemberBind(mm.Member); + + return mm; + } + } + + return b; + }; + + var e = expr as MemberInitExpression; + var ne = Convert2(e.NewExpression, func); + var bb = Convert2(e.Bindings, modify); + + return ne != e.NewExpression || bb != e.Bindings ? + Expression.MemberInit((NewExpression)ne, bb) : + expr; + } + + case ExpressionType.New: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as NewExpression; + var a = Convert2(e.Arguments, func); + + return a != e.Arguments ? + e.Members == null ? + Expression.New(e.Constructor, ConvertMethodArguments(a, e.Constructor)) : + Expression.New(e.Constructor, ConvertMethodArguments(a, e.Constructor, e.Members), e.Members) : + expr; + } + + case ExpressionType.NewArrayBounds: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as NewArrayExpression; + var ex = Convert2(e.Expressions, func); + + return ex != e.Expressions ? Expression.NewArrayBounds(e.Type, ex) : expr; + } + + case ExpressionType.NewArrayInit: + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as NewArrayExpression; + var et = e.Type.GetElementType(); + var ex = Convert2(e.Expressions, func) + .Select(ee => et == typeof(object) && ee.Type.IsValueType ? + Expression.Convert(ee, typeof(object)) : + ee); + + return ex != e.Expressions ? + Expression.NewArrayInit(et, ex) : + expr; + } + + case ExpressionType.TypeIs : + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as TypeBinaryExpression; + var ex = Convert2(e.Expression, func); + + return ex != e.Expression ? Expression.TypeIs(ex, e.Type) : expr; + } + +#if FW4 || SILVERLIGHT + + case ExpressionType.Block : + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as BlockExpression; + var ex = Convert2(e.Expressions, func); + var v = Convert2(e.Variables, func); + + return ex != e.Expressions || v != e.Variables ? Expression.Block(e.Type, v, ex) : expr; + } + +#endif + + case ExpressionType.Constant : return func(expr).Expression; + case ExpressionType.Parameter: return func(expr).Expression; + + case (ExpressionType)ChangeTypeExpression.ChangeTypeType : + { + ci = func(expr); + if (ci.Stop || ci.Expression != expr) + return ci.Expression; + + var e = expr as ChangeTypeExpression; + var ex = Convert2(e.Expression, func); + + if (ex == e.Expression) + return expr; + + if (ex.Type == e.Type) + return ex; + + return new ChangeTypeExpression(ex, e.Type); + } + } + + throw new InvalidOperationException(); + } + + #endregion + + #region Helpers + + static public Expression Unwrap(this Expression ex) + { + if (ex == null) + return null; + + switch (ex.NodeType) + { + case ExpressionType.Quote : return ((UnaryExpression)ex).Operand.Unwrap(); + case ExpressionType.ConvertChecked : + case ExpressionType.Convert : + { + var ue = (UnaryExpression)ex; + + if (!ue.Operand.Type.IsEnum) + return ue.Operand.Unwrap(); + + break; + } + } + + return ex; + } + + static public Dictionary<Expression,Expression> GetExpressionAccessors(this Expression expression, Expression path) + { + var accessors = new Dictionary<Expression,Expression>(); + + expression.Path(path, (e,p) => + { + switch (e.NodeType) + { + case ExpressionType.Call : + case ExpressionType.MemberAccess : + case ExpressionType.New : + if (!accessors.ContainsKey(e)) + accessors.Add(e, p); + break; + + case ExpressionType.Constant : + if (!accessors.ContainsKey(e)) + accessors.Add(e, Expression.Property(p, ReflectionHelper.Constant.Value)); + break; + + case ExpressionType.ConvertChecked : + case ExpressionType.Convert : + if (!accessors.ContainsKey(e)) + { + var ue = (UnaryExpression)e; + + switch (ue.Operand.NodeType) + { + case ExpressionType.Call : + case ExpressionType.MemberAccess : + case ExpressionType.New : + case ExpressionType.Constant : + + accessors.Add(e, p); + break; + } + } + + break; + } + }); + + return accessors; + } + + static public Expression GetRootObject(this Expression expr) + { + if (expr == null) + return null; + + switch (expr.NodeType) + { + case ExpressionType.Call : + { + var e = (MethodCallExpression)expr; + + if (e.Object != null) + return GetRootObject(e.Object); + + if (e.Arguments != null && e.Arguments.Count > 0 && e.IsQueryable()) + return GetRootObject(e.Arguments[0]); + + break; + } + + case ExpressionType.MemberAccess : + { + var e = (MemberExpression)expr; + + if (e.Expression != null) + return GetRootObject(e.Expression.Unwrap()); + + break; + } + } + + return expr; + } + + static public List<Expression> GetMembers(this Expression expr) + { + if (expr == null) + return new List<Expression>(); + + List<Expression> list; + + switch (expr.NodeType) + { + case ExpressionType.Call : + { + var e = (MethodCallExpression)expr; + + if (e.Object != null) + list = GetMembers(e.Object); + else if (e.Arguments != null && e.Arguments.Count > 0 && e.IsQueryable()) + list = GetMembers(e.Arguments[0]); + else + list = new List<Expression>(); + + break; + } + + case ExpressionType.MemberAccess : + { + var e = (MemberExpression)expr; + + if (e.Expression != null) + list = GetMembers(e.Expression.Unwrap()); + else + list = new List<Expression>(); + + break; + } + + default : + list = new List<Expression>(); + break; + } + + list.Add(expr); + + return list; + } + + static public bool IsQueryable(this MethodCallExpression method) + { + var type = method.Method.DeclaringType; + + return type == typeof(Queryable) || type == typeof(Enumerable) || type == typeof(LinqExtensions); + } + + static public bool IsQueryable(this MethodCallExpression method, string name) + { + return method.Method.Name == name && method.IsQueryable(); + } + + static public bool IsQueryable(this MethodCallExpression method, params string[] names) + { + if (method.IsQueryable()) + foreach (var name in names) + if (method.Method.Name == name) + return true; + + return false; + } + + static Expression FindLevel(Expression expression, int level, ref int current) + { + switch (expression.NodeType) + { + case ExpressionType.Call : + { + var call = (MethodCallExpression)expression; + var expr = call.Object; + + if (expr == null && call.IsQueryable() && call.Arguments.Count > 0) + expr = call.Arguments[0]; + + if (expr != null) + { + var ex = FindLevel(expr, level, ref current); + + if (level == current) + return ex; + + current++; + } + + break; + } + + case ExpressionType.MemberAccess: + { + var e = ((MemberExpression)expression); + + if (e.Expression != null) + { + var expr = FindLevel(e.Expression.Unwrap(), level, ref current); + + if (level == current) + return expr; + + current++; + } + + break; + } + } + + return expression; + } + + static public Expression GetLevelExpression(this Expression expression, int level) + { + var current = 0; + var expr = FindLevel(expression, level, ref current); + + if (expr == null || current != level) + throw new InvalidOperationException(); + + return expr; + } + + #endregion + } +}