PageRenderTime 2284ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/Signum.Windows/Fluent/FrameworkFactoryGenerator.cs

http://github.com/signumframework/signumframework
C# | 217 lines | 165 code | 50 blank | 2 comment | 41 complexity | 33ca88c1dc4f0ea96ed0dd52013daaf0 MD5 | raw file
Possible License(s): LGPL-3.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using w = System.Windows;
  7. using Signum.Utilities;
  8. using Signum.Utilities.Reflection;
  9. using System.Reflection;
  10. using System.Windows.Controls;
  11. using Signum.Utilities.ExpressionTrees;
  12. using System.ComponentModel;
  13. using System.Windows.Data;
  14. using System.Windows.Markup;
  15. namespace Signum.Windows
  16. {
  17. static class FrameworkElementFactoryGenerator
  18. {
  19. public static w.FrameworkElementFactory Generate(Expression<Func<w.FrameworkElement>> expression)
  20. {
  21. ParameterExpression rootFactory = Expression.Parameter(typeof(w.FrameworkElementFactory), "f");
  22. var list = Process(expression.Body, rootFactory);
  23. var variables = list.Where(a => a.NodeType == ExpressionType.Assign).Select(e => (ParameterExpression)((BinaryExpression)e).Left);
  24. list.Add(rootFactory);
  25. var lambda = Expression.Lambda<Func<w.FrameworkElementFactory>>(
  26. Expression.Block(variables, list));
  27. return lambda.Compile()();
  28. }
  29. static ConstructorInfo ci = ReflectionTools.GetConstuctorInfo(() => new w.FrameworkElementFactory(typeof(Border)));
  30. static MethodInfo miAddHandler = ReflectionTools.GetMethodInfo((w.FrameworkElementFactory fef) => fef.AddHandler(null, null));
  31. static MethodInfo miRemoveHandler = ReflectionTools.GetMethodInfo((w.FrameworkElementFactory fef) => fef.RemoveHandler(null, null));
  32. static MethodInfo miSetBinding = ReflectionTools.GetMethodInfo((w.FrameworkElementFactory fef) => fef.SetBinding(null, null));
  33. static MethodInfo miSetValue = ReflectionTools.GetMethodInfo((w.FrameworkElementFactory fef) => fef.SetValue(null, null));
  34. static MethodInfo miSetResourceReference = ReflectionTools.GetMethodInfo((w.FrameworkElementFactory fef) => fef.SetResourceReference(null, null));
  35. static MethodInfo miAppendChild = ReflectionTools.GetMethodInfo((w.FrameworkElementFactory fef) => fef.AppendChild(null));
  36. static List<Expression> Process(Expression expression, ParameterExpression factory)
  37. {
  38. if (expression.NodeType == ExpressionType.New)
  39. {
  40. if(((NewExpression)expression).Arguments.Any())
  41. throw new InvalidOperationException("No arguments in constructo allowed");
  42. return new List<Expression>
  43. {
  44. Expression.Assign(factory, Expression.New(ci, Expression.Constant(expression.Type)))
  45. };
  46. }
  47. if(expression.NodeType == ExpressionType.MemberInit)
  48. {
  49. MemberInitExpression mie = (MemberInitExpression)expression;
  50. var list = Process(mie.NewExpression, factory);
  51. foreach (MemberBinding mb in mie.Bindings)
  52. {
  53. switch (mb.BindingType)
  54. {
  55. case MemberBindingType.Assignment:
  56. {
  57. MemberAssignment ma = (MemberAssignment)mb;
  58. PropertyInfo pi = ma.Member as PropertyInfo;
  59. if (IsDefaultMember(pi))
  60. list.AddRange(ProcessChild(ma.Expression, factory));
  61. else
  62. {
  63. DependencyPropertyDescriptor desc = DependencyPropertyDescriptor.FromName(pi.Name, pi.DeclaringType, pi.DeclaringType);
  64. if (desc == null)
  65. throw new InvalidOperationException("{0} is not a DependencyProperty".Formato(pi.PropertyName()));
  66. list.Add(Expression.Call(factory, miSetValue, Expression.Constant(desc.DependencyProperty), Expression.Convert(ma.Expression, typeof(object))));
  67. }
  68. }break;
  69. case MemberBindingType.ListBinding:
  70. {
  71. MemberListBinding bindings = (MemberListBinding)mb;
  72. DefaultMemberAttribute dma = bindings.Member.DeclaringType.SingleAttribute<DefaultMemberAttribute>();
  73. //if(!IsDefaultMember(bindings.Member))
  74. // throw new InvalidOperationException("Add items only work for the DefaultMember");
  75. foreach (var item in bindings.Initializers)
  76. {
  77. if(item.Arguments.Count != 1)
  78. throw new InvalidOperationException("Add Method {0} not supported".Formato(item.AddMethod.MethodName()));
  79. list.AddRange(ProcessChild(item.Arguments.SingleEx(), factory));
  80. }
  81. }break;
  82. case MemberBindingType.MemberBinding:
  83. throw new InvalidOperationException("MemberBinding not supported");
  84. }
  85. }
  86. return list;
  87. }
  88. if (expression.NodeType == ExpressionType.Call)
  89. {
  90. MethodCallExpression call = (MethodCallExpression)expression;
  91. MethodInfo mi = call.Method;
  92. if (mi.DeclaringType != typeof(Fluent) || mi.ReturnType != mi.GetParameters()[0].ParameterType)
  93. throw new InvalidOperationException("Method {0} not supported".Formato(mi.MethodName()));
  94. var list = Process(call.Arguments[0], factory);
  95. list.Add(ProcessMethod(factory, call));
  96. return list;
  97. }
  98. if (expression.NodeType == ExpressionType.Convert)
  99. {
  100. return Process(((UnaryExpression)expression).Operand, factory);
  101. }
  102. throw new InvalidOperationException("Expression {0} not supported");
  103. }
  104. private static List<Expression> ProcessChild(Expression single, ParameterExpression factory)
  105. {
  106. if (!typeof(w.FrameworkElement).IsAssignableFrom(single.Type) && !typeof(w.FrameworkContentElement).IsAssignableFrom(single.Type))
  107. throw new InvalidOperationException("Can not make a {0} from a {1}".Formato(typeof(w.FrameworkElementFactory).Name, single.Type));
  108. ParameterExpression newFactory = Expression.Parameter(typeof(w.FrameworkElementFactory));
  109. var list = Process(single, newFactory);
  110. list.Add(Expression.Call(factory, miAppendChild, newFactory));
  111. return list;
  112. }
  113. private static bool IsDefaultMember(MemberInfo mi)
  114. {
  115. var dma = mi.DeclaringType.SingleAttribute<ContentPropertyAttribute>();
  116. return dma != null && dma.Name == mi.Name;
  117. }
  118. static Expression ProcessMethod(ParameterExpression factory, MethodCallExpression call)
  119. {
  120. switch (call.Method.Name)
  121. {
  122. case "Set": return Expression.Call(factory, miSetValue, call.Arguments[1], call.Arguments[2]);
  123. case "Hide": return Expression.Call(factory, miSetValue, Expression.Constant(w.UIElement.VisibilityProperty), Expression.Constant(w.Visibility.Hidden));
  124. case "Collapse": return Expression.Call(factory, miSetValue, Expression.Constant(w.UIElement.VisibilityProperty), Expression.Constant(w.Visibility.Collapsed));
  125. case "Visible": return Expression.Call(factory, miSetValue, Expression.Constant(w.UIElement.VisibilityProperty), Expression.Constant(w.Visibility.Visible));
  126. case "ReadOnly": return Expression.Call(factory, miSetValue, Expression.Constant(Common.IsReadOnlyProperty), Expression.Constant(true));
  127. case "Editable": return Expression.Call(factory, miSetValue, Expression.Constant(Common.IsReadOnlyProperty), Expression.Constant(false));
  128. case "Handle": return Expression.Call(factory, miAddHandler, call.Arguments[1], Expression.Convert(call.Arguments[2], typeof(Delegate)));
  129. case "ResourceReference": return Expression.Call(factory, miSetResourceReference, call.Arguments[1], call.Arguments[2]);
  130. case "Bind": return Expression.Call(factory, miSetBinding, call.Arguments[1], GetBinding(call.Method.GetGenericArguments().Length == 2, call.Arguments.Skip(2).ToArray()));
  131. }
  132. throw new InvalidOperationException("Methods {0} not supported".Formato(call.Method.Name));
  133. }
  134. static ConstructorInfo ciBinding = ReflectionTools.GetConstuctorInfo(() => new Binding(""));
  135. static PropertyInfo ciSource = ReflectionTools.GetPropertyInfo((Binding b) => b.Source);
  136. static PropertyInfo ciConverter = ReflectionTools.GetPropertyInfo((Binding b) => b.Converter);
  137. static Expression GetBinding(bool hasExpression, Expression[] expression)
  138. {
  139. if (typeof(BindingBase).IsAssignableFrom(expression[0].Type))
  140. return expression[0];
  141. bool noSource = hasExpression? typeof(LambdaExpression).IsAssignableFrom(expression[0].Type) :
  142. typeof(string).IsAssignableFrom(expression[0].Type);
  143. Expression path = hasExpression? (noSource ? GetPathConstant(expression[0]): GetPathConstant(expression[1])):
  144. (noSource ? expression[0]: expression[1]);
  145. Expression source = noSource? null: expression[0];
  146. Expression converter = expression.Length == (noSource? 2:3) ? expression.Last(): null;
  147. NewExpression newExpr = Expression.New(ciBinding, path);
  148. if (source == null && converter == null)
  149. return newExpr;
  150. List<MemberBinding> binding = new List<MemberBinding>();
  151. if (source != null)
  152. binding.Add(Expression.Bind(ciSource, source));
  153. if (converter != null)
  154. binding.Add(Expression.Bind(ciConverter, converter));
  155. return Expression.MemberInit(newExpr, binding);
  156. }
  157. static ConstantExpression GetPathConstant(Expression expression)
  158. {
  159. if (expression.NodeType == ExpressionType.Quote)
  160. expression = ((UnaryExpression)expression).Operand;
  161. if (expression.NodeType != ExpressionType.Lambda)
  162. throw new InvalidOperationException();
  163. string str = RouteVisitor.GetRoute((LambdaExpression)expression);
  164. return Expression.Constant(str);
  165. }
  166. }
  167. }