2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Sugar / ExpressionMutator / ExpressionMutatorExtensions.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Collections.Generic;\r
29 using System.Linq;\r
30 using System.Linq.Expressions;\r
31 using DbLinq.Data.Linq.Sugar.ExpressionMutator;\r
32 using DbLinq.Data.Linq.Sugar.Expressions;\r
33 \r
34 namespace DbLinq.Data.Linq.Sugar.ExpressionMutator\r
35 {\r
36     /// <summary>\r
37     /// Extensions to Expression, to enumerate and dynamically change operands in a uniformized way\r
38     /// </summary>\r
39     internal static class ExpressionMutatorExtensions\r
40     {\r
41         /// <summary>\r
42         /// Enumerates all subexpressions related to this one\r
43         /// </summary>\r
44         /// <param name="expression"></param>\r
45         /// <returns></returns>\r
46         public static IEnumerable<Expression> GetOperands(this Expression expression)\r
47         {\r
48             if (expression is MutableExpression)\r
49                 return new List<Expression>(((MutableExpression)expression).Operands);\r
50             return ExpressionMutatorFactory.GetMutator(expression).Operands;\r
51         }\r
52 \r
53         /// <summary>\r
54         /// Changes all operands\r
55         /// </summary>\r
56         /// <typeparam name="T"></typeparam>\r
57         /// <param name="expression"></param>\r
58         /// <param name="operands"></param>\r
59         /// <param name="checkForChanges"></param>\r
60         /// <returns>A potentially new expression with new operands</returns>\r
61         public static T ChangeOperands<T>(this T expression, IList<Expression> operands, bool checkForChanges)\r
62             where T : Expression\r
63         {\r
64             bool haveOperandsChanged = checkForChanges && HaveOperandsChanged(expression, operands);\r
65             if (!haveOperandsChanged)\r
66                 return expression;\r
67             var mutableExpression = expression as IMutableExpression;\r
68             if (mutableExpression != null)\r
69                 return (T)mutableExpression.Mutate(operands);\r
70             return (T)ExpressionMutatorFactory.GetMutator(expression).Mutate(operands);\r
71         }\r
72 \r
73         /// <summary>\r
74         /// Determines if operands have changed for a given expression\r
75         /// </summary>\r
76         /// <typeparam name="T"></typeparam>\r
77         /// <param name="expression"></param>\r
78         /// <param name="operands"></param>\r
79         /// <returns></returns>\r
80         private static bool HaveOperandsChanged<T>(T expression, IList<Expression> operands)\r
81             where T : Expression\r
82         {\r
83             var oldOperands = GetOperands(expression).ToList();\r
84             if (operands.Count != oldOperands.Count)\r
85                 return true;\r
86 \r
87             for (int operandIndex = 0; operandIndex < operands.Count; operandIndex++)\r
88             {\r
89                 if (operands[operandIndex] != oldOperands[operandIndex])\r
90                 {\r
91                     return true;\r
92                 }\r
93             }\r
94             return false;\r
95         }\r
96 \r
97         /// <summary>\r
98         /// Changes all operands\r
99         /// </summary>\r
100         /// <typeparam name="T"></typeparam>\r
101         /// <param name="expression"></param>\r
102         /// <param name="operands"></param>\r
103         /// <returns>A potentially new expression with new operands</returns>\r
104         public static T ChangeOperands<T>(this T expression, IList<Expression> operands)\r
105             where T : Expression\r
106         {\r
107             return ChangeOperands(expression, operands, true);\r
108         }\r
109 \r
110         /// <summary>\r
111         /// Changes all operands\r
112         /// </summary>\r
113         /// <typeparam name="T"></typeparam>\r
114         /// <param name="expression"></param>\r
115         /// <param name="operands"></param>\r
116         /// <returns>A potentially new expression with new operands</returns>\r
117         public static T ChangeOperands<T>(this T expression, params Expression[] operands)\r
118             where T : Expression\r
119         {\r
120             return ChangeOperands(expression, operands, true);\r
121         }\r
122 \r
123         /// <summary>\r
124         /// Returns the expression result\r
125         /// </summary>\r
126         /// <param name="expression"></param>\r
127         /// <returns></returns>\r
128         public static object Evaluate(this Expression expression)\r
129         {\r
130             var executableExpression = expression as IExecutableExpression;\r
131             if (executableExpression != null)\r
132                 return executableExpression.Execute();\r
133             try\r
134             {\r
135                 // here, we may have non-evaluable expressions, so we "try"/"catch"\r
136                 // (maybe should we find something better)\r
137                 var lambda = Expression.Lambda(expression);\r
138                 var compiled = lambda.Compile();\r
139                 var value = compiled.DynamicInvoke();\r
140                 return value;\r
141             }\r
142             catch\r
143             {\r
144                 throw new ArgumentException();\r
145             }\r
146         }\r
147 \r
148         /// <summary>\r
149         /// Down-top pattern analysis.\r
150         /// </summary>\r
151         /// <param name="expression">The original expression</param>\r
152         /// <param name="analyzer"></param>\r
153         /// <returns>A new QueryExpression or the original one</returns>\r
154         public static Expression Recurse(this Expression expression, Func<Expression, Expression> analyzer)\r
155         {\r
156             var newOperands = new List<Expression>();\r
157             // first, work on children (down)\r
158             foreach (var operand in GetOperands(expression))\r
159             {\r
160                 if (operand != null)\r
161                     newOperands.Add(Recurse(operand, analyzer));\r
162                 else\r
163                     newOperands.Add(null);\r
164             }\r
165             // then on expression itself (top)\r
166             return analyzer(expression.ChangeOperands(newOperands));\r
167         }\r
168     }\r
169 }