2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Sugar / Expressions / SpecialExpression.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;\r
29 using System.Collections.Generic;\r
30 using System.Diagnostics;\r
31 using System.Globalization;\r
32 using System.Linq.Expressions;\r
33 using System.Linq;\r
34 using System.Collections.ObjectModel;\r
35 \r
36 using DbLinq.Data.Linq.Sugar;\r
37 using DbLinq.Data.Linq.Sugar.ExpressionMutator;\r
38 using DbLinq.Data.Linq.Sugar.Expressions;\r
39 \r
40 namespace DbLinq.Data.Linq.Sugar.Expressions\r
41 {\r
42     /// <summary>\r
43     /// Holds new expression types (sql related), all well as their operands\r
44     /// </summary>\r
45     [DebuggerDisplay("SpecialExpression {SpecialNodeType}")]\r
46 #if !MONO_STRICT\r
47     public\r
48 #endif\r
49     class SpecialExpression : OperandsMutableExpression, IExecutableExpression\r
50     {\r
51         public SpecialExpressionType SpecialNodeType { get { return (SpecialExpressionType)NodeType; } }\r
52 \r
53         protected static Type GetSpecialExpressionTypeType(SpecialExpressionType specialExpressionType, IList<Expression> operands)\r
54         {\r
55             Type defaultType;\r
56             if (operands.Count > 0)\r
57                 defaultType = operands[0].Type;\r
58             else\r
59                 defaultType = null;\r
60             switch (specialExpressionType) // SETuse\r
61             {\r
62                 case SpecialExpressionType.IsNull:\r
63                 case SpecialExpressionType.IsNotNull:\r
64                     return typeof(bool);\r
65                 case SpecialExpressionType.Concat:\r
66                     return typeof(string);\r
67                 case SpecialExpressionType.Count:\r
68                     return typeof(int);\r
69                 case SpecialExpressionType.Exists:\r
70                     return typeof(bool);\r
71                 case SpecialExpressionType.Like:\r
72                     return typeof(bool);\r
73                 case SpecialExpressionType.Min:\r
74                 case SpecialExpressionType.Max:\r
75                 case SpecialExpressionType.Sum:\r
76                     return defaultType; // for such methods, the type is related to the operands type\r
77                 case SpecialExpressionType.Average:\r
78                     return typeof(double);\r
79                 case SpecialExpressionType.StringLength:\r
80                     return typeof(int);\r
81                 case SpecialExpressionType.ToUpper:\r
82                 case SpecialExpressionType.ToLower:\r
83                     return typeof(string);\r
84                 case SpecialExpressionType.In:\r
85                     return typeof(bool);\r
86                 case SpecialExpressionType.Substring:\r
87                     return defaultType;\r
88                 case SpecialExpressionType.Trim:\r
89                 case SpecialExpressionType.LTrim:\r
90                 case SpecialExpressionType.RTrim:\r
91                     return typeof(string);\r
92                 case SpecialExpressionType.StringInsert:\r
93                     return typeof(string);\r
94                 case SpecialExpressionType.Replace:\r
95                     return typeof(string);\r
96                 case SpecialExpressionType.Remove:\r
97                     return typeof(string);\r
98                 case SpecialExpressionType.IndexOf:\r
99                     return typeof(int);\r
100                 case SpecialExpressionType.Year:\r
101                 case SpecialExpressionType.Month:\r
102                 case SpecialExpressionType.Day:\r
103                 case SpecialExpressionType.Hour:\r
104                 case SpecialExpressionType.Second:\r
105                 case SpecialExpressionType.Minute:\r
106                 case SpecialExpressionType.Millisecond:\r
107                     return typeof(int);\r
108                 case SpecialExpressionType.Now:\r
109                 case SpecialExpressionType.Date:\r
110                     return typeof(DateTime);\r
111                 case SpecialExpressionType.DateDiffInMilliseconds:\r
112                     return typeof(long);\r
113                 case SpecialExpressionType.Abs:\r
114                 case SpecialExpressionType.Exp:\r
115                 case SpecialExpressionType.Floor:\r
116                 case SpecialExpressionType.Ln:\r
117                 case SpecialExpressionType.Log:\r
118                 case SpecialExpressionType.Pow:\r
119                 case SpecialExpressionType.Round:\r
120                 case SpecialExpressionType.Sign:\r
121                 case SpecialExpressionType.Sqrt:\r
122                     return defaultType;\r
123 \r
124                 default:\r
125                     throw Error.BadArgument("S0058: Unknown SpecialExpressionType value {0}", specialExpressionType);\r
126             }\r
127         }\r
128 \r
129         public SpecialExpression(SpecialExpressionType expressionType, params Expression[] operands)\r
130             : base((ExpressionType)expressionType, GetSpecialExpressionTypeType(expressionType, operands), operands)\r
131         {\r
132         }\r
133 \r
134         public SpecialExpression(SpecialExpressionType expressionType, IList<Expression> operands)\r
135             : base((ExpressionType)expressionType, GetSpecialExpressionTypeType(expressionType, operands), operands)\r
136         {\r
137         }\r
138 \r
139         protected override Expression Mutate2(IList<Expression> newOperands)\r
140         {\r
141             return new SpecialExpression((SpecialExpressionType)NodeType, newOperands);\r
142         }\r
143 \r
144         public object Execute()\r
145         {\r
146             switch (SpecialNodeType) // SETuse\r
147             {\r
148                 case SpecialExpressionType.IsNull:\r
149                     return operands[0].Evaluate() == null;\r
150                 case SpecialExpressionType.IsNotNull:\r
151                     return operands[0].Evaluate() != null;\r
152                 case SpecialExpressionType.Concat:\r
153                     {\r
154                         var values = new List<string>();\r
155                         foreach (var operand in operands)\r
156                         {\r
157                             var value = operand.Evaluate();\r
158                             if (value != null)\r
159                                 values.Add(System.Convert.ToString(value, CultureInfo.InvariantCulture));\r
160                             else\r
161                                 values.Add(null);\r
162                         }\r
163                         return string.Concat(values.ToArray());\r
164                     }\r
165                 case SpecialExpressionType.Count:\r
166                     {\r
167                         var value = operands[0].Evaluate();\r
168                         // TODO: string is IEnumerable. See what we do here\r
169                         if (value is IEnumerable)\r
170                         {\r
171                             int count = 0;\r
172                             foreach (var dontCare in (IEnumerable)value)\r
173                                 count++;\r
174                             return count;\r
175                         }\r
176                         // TODO: by default, shall we answer 1 or throw an exception?\r
177                         return 1;\r
178                     }\r
179                 case SpecialExpressionType.Exists:\r
180                     {\r
181                         var value = operands[0].Evaluate();\r
182                         // TODO: string is IEnumerable. See what we do here\r
183                         if (value is IEnumerable)\r
184                         {\r
185                             return true;\r
186                         }\r
187                         // TODO: by default, shall we answer 1 or throw an exception?\r
188                         return false;\r
189                     }\r
190                 case SpecialExpressionType.Min:\r
191                     {\r
192                         decimal? min = null;\r
193                         foreach (var operand in operands)\r
194                         {\r
195                             var value = System.Convert.ToDecimal(operand.Evaluate());\r
196                             if (!min.HasValue || value < min.Value)\r
197                                 min = value;\r
198                         }\r
199                         return System.Convert.ChangeType(min.Value, operands[0].Type);\r
200                     }\r
201                 case SpecialExpressionType.Max:\r
202                     {\r
203                         decimal? max = null;\r
204                         foreach (var operand in operands)\r
205                         {\r
206                             var value = System.Convert.ToDecimal(operand.Evaluate());\r
207                             if (!max.HasValue || value > max.Value)\r
208                                 max = value;\r
209                         }\r
210                         return System.Convert.ChangeType(max.Value, operands[0].Type);\r
211                     }\r
212                 case SpecialExpressionType.Sum:\r
213                     {\r
214                         decimal sum = operands.Select(op => System.Convert.ToDecimal(op.Evaluate())).Sum();\r
215                         return System.Convert.ChangeType(sum, operands.First().Type);\r
216                     }\r
217                 case SpecialExpressionType.Average:\r
218                     {\r
219                         decimal sum = 0;\r
220                         foreach (var operand in operands)\r
221                             sum += System.Convert.ToDecimal(operand.Evaluate());\r
222                         return sum / operands.Count;\r
223                     }\r
224                 case SpecialExpressionType.StringLength:\r
225                     return operands[0].Evaluate().ToString().Length;\r
226                 case SpecialExpressionType.ToUpper:\r
227                     return operands[0].Evaluate().ToString().ToUpper();\r
228                 case SpecialExpressionType.ToLower:\r
229                     return operands[0].Evaluate().ToString().ToLower();\r
230                 case SpecialExpressionType.Substring:\r
231                     return EvaluateStandardCallInvoke("SubString", operands);\r
232                 case SpecialExpressionType.In:\r
233                     throw new NotImplementedException();\r
234                 case SpecialExpressionType.Replace:\r
235                     return EvaluateStandardCallInvoke("Replace", operands);\r
236                 case SpecialExpressionType.Remove:\r
237                     return EvaluateStandardCallInvoke("Remove", operands);\r
238                 case SpecialExpressionType.IndexOf:\r
239                     return EvaluateStandardCallInvoke("IndexOf", operands);\r
240                 case SpecialExpressionType.Year:\r
241                     return ((DateTime)operands[0].Evaluate()).Year;\r
242                 case SpecialExpressionType.Month:\r
243                     return ((DateTime)operands[0].Evaluate()).Month;\r
244                 case SpecialExpressionType.Day:\r
245                     return ((DateTime)operands[0].Evaluate()).Day;\r
246                 case SpecialExpressionType.Hour:\r
247                     return ((DateTime)operands[0].Evaluate()).Hour;\r
248                 case SpecialExpressionType.Minute:\r
249                     return ((DateTime)operands[0].Evaluate()).Minute;\r
250                 case SpecialExpressionType.Second:\r
251                     return ((DateTime)operands[0].Evaluate()).Second;\r
252                 case SpecialExpressionType.Millisecond:\r
253                     return ((DateTime)operands[0].Evaluate()).Millisecond;\r
254                 case SpecialExpressionType.Now:\r
255                     return DateTime.Now;\r
256                 case SpecialExpressionType.Date:\r
257                     return ((DateTime)operands[0].Evaluate());\r
258                 case SpecialExpressionType.DateDiffInMilliseconds:\r
259                     return ((DateTime)operands[0].Evaluate()) - ((DateTime)operands[1].Evaluate());\r
260                 case SpecialExpressionType.Abs:\r
261                 case SpecialExpressionType.Exp:\r
262                 case SpecialExpressionType.Floor:\r
263                 case SpecialExpressionType.Ln:\r
264                 case SpecialExpressionType.Log:\r
265                 case SpecialExpressionType.Pow:\r
266                 case SpecialExpressionType.Round:\r
267                 case SpecialExpressionType.Sign:\r
268                 case SpecialExpressionType.Sqrt:\r
269                     return EvaluateMathCallInvoke(SpecialNodeType, operands);\r
270                 default:\r
271                     throw Error.BadArgument("S0116: Unknown SpecialExpressionType ({0})", SpecialNodeType);\r
272             }\r
273         }\r
274 \r
275         private object EvaluateMathCallInvoke(SpecialExpressionType SpecialNodeType, ReadOnlyCollection<Expression> operands)\r
276         {\r
277             return typeof(Math).GetMethod(SpecialNodeType.ToString(), operands.Skip(1).Select(op => op.Type).ToArray())\r
278                     .Invoke(null, operands.Skip(1).Select(op => op.Evaluate()).ToArray());\r
279         }\r
280         protected object EvaluateStatardMemberAccess(string propertyName, ReadOnlyCollection<Expression> operands)\r
281         {\r
282             return operands[0].Type.GetProperty(propertyName).GetValue(operands.First().Evaluate(), null);\r
283         }\r
284         protected object EvaluateStandardCallInvoke(string methodName, ReadOnlyCollection<Expression> operands)\r
285         {\r
286             return operands[0].Type.GetMethod(methodName,\r
287                                        operands.Skip(1).Select(op => op.Type).ToArray())\r
288                                        .Invoke(operands[0].Evaluate(),\r
289                                                operands.Skip(1).Select(op => op.Evaluate()).ToArray());\r
290         }\r
291     }\r
292 }