1 #region MIT license
2 //
3 // MIT license
4 //
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 // THE SOFTWARE.
24 //
25 #endregion
26 
27 using System;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.Globalization;
32 using System.Linq.Expressions;
33 using System.Linq;
34 using System.Collections.ObjectModel;
35 
36 using DbLinq.Data.Linq.Sugar;
37 using DbLinq.Data.Linq.Sugar.ExpressionMutator;
38 using DbLinq.Data.Linq.Sugar.Expressions;
39 
40 namespace DbLinq.Data.Linq.Sugar.Expressions
41 {
42     /// <summary>
43     /// Holds new expression types (sql related), all well as their operands
44     /// </summary>
45     [DebuggerDisplay("SpecialExpression {SpecialNodeType}")]
46 #if !MONO_STRICT
47     public
48 #endif
49     class SpecialExpression : OperandsMutableExpression, IExecutableExpression
50     {
51         public SpecialExpressionType SpecialNodeType { get { return (SpecialExpressionType)NodeType; } }
52 
GetSpecialExpressionTypeType(SpecialExpressionType specialExpressionType, IList<Expression> operands)53         protected static Type GetSpecialExpressionTypeType(SpecialExpressionType specialExpressionType, IList<Expression> operands)
54         {
55             Type defaultType;
56             if (operands.Count > 0)
57                 defaultType = operands[0].Type;
58             else
59                 defaultType = null;
60             switch (specialExpressionType) // SETuse
61             {
62                 case SpecialExpressionType.IsNull:
63                 case SpecialExpressionType.IsNotNull:
64                     return typeof(bool);
65                 case SpecialExpressionType.Concat:
66                     return typeof(string);
67                 case SpecialExpressionType.Count:
68                     return typeof(int);
69                 case SpecialExpressionType.Exists:
70                     return typeof(bool);
71                 case SpecialExpressionType.Like:
72                     return typeof(bool);
73                 case SpecialExpressionType.Min:
74                 case SpecialExpressionType.Max:
75                 case SpecialExpressionType.Sum:
76                     return defaultType; // for such methods, the type is related to the operands type
77                 case SpecialExpressionType.Average:
78                     return typeof(double);
79                 case SpecialExpressionType.StringLength:
80                     return typeof(int);
81                 case SpecialExpressionType.ToUpper:
82                 case SpecialExpressionType.ToLower:
83                     return typeof(string);
84                 case SpecialExpressionType.In:
85                     return typeof(bool);
86                 case SpecialExpressionType.Substring:
87                     return defaultType;
88                 case SpecialExpressionType.Trim:
89                 case SpecialExpressionType.LTrim:
90                 case SpecialExpressionType.RTrim:
91                     return typeof(string);
92                 case SpecialExpressionType.StringInsert:
93                     return typeof(string);
94                 case SpecialExpressionType.Replace:
95                     return typeof(string);
96                 case SpecialExpressionType.Remove:
97                     return typeof(string);
98                 case SpecialExpressionType.IndexOf:
99                     return typeof(int);
100                 case SpecialExpressionType.Year:
101                 case SpecialExpressionType.Month:
102                 case SpecialExpressionType.Day:
103                 case SpecialExpressionType.Hour:
104                 case SpecialExpressionType.Second:
105                 case SpecialExpressionType.Minute:
106                 case SpecialExpressionType.Millisecond:
107                     return typeof(int);
108                 case SpecialExpressionType.Now:
109                 case SpecialExpressionType.Date:
110                     return typeof(DateTime);
111                 case SpecialExpressionType.DateDiffInMilliseconds:
112                     return typeof(long);
113                 case SpecialExpressionType.Abs:
114                 case SpecialExpressionType.Exp:
115                 case SpecialExpressionType.Floor:
116                 case SpecialExpressionType.Ln:
117                 case SpecialExpressionType.Log:
118                 case SpecialExpressionType.Pow:
119                 case SpecialExpressionType.Round:
120                 case SpecialExpressionType.Sign:
121                 case SpecialExpressionType.Sqrt:
122                     return defaultType;
123 
124                 default:
125                     throw Error.BadArgument("S0058: Unknown SpecialExpressionType value {0}", specialExpressionType);
126             }
127         }
128 
SpecialExpression(SpecialExpressionType expressionType, params Expression[] operands)129         public SpecialExpression(SpecialExpressionType expressionType, params Expression[] operands)
130             : base((ExpressionType)expressionType, GetSpecialExpressionTypeType(expressionType, operands), operands)
131         {
132         }
133 
SpecialExpression(SpecialExpressionType expressionType, IList<Expression> operands)134         public SpecialExpression(SpecialExpressionType expressionType, IList<Expression> operands)
135             : base((ExpressionType)expressionType, GetSpecialExpressionTypeType(expressionType, operands), operands)
136         {
137         }
138 
Mutate2(IList<Expression> newOperands)139         protected override Expression Mutate2(IList<Expression> newOperands)
140         {
141             return new SpecialExpression((SpecialExpressionType)NodeType, newOperands);
142         }
143 
Execute()144         public object Execute()
145         {
146             switch (SpecialNodeType) // SETuse
147             {
148                 case SpecialExpressionType.IsNull:
149                     return operands[0].Evaluate() == null;
150                 case SpecialExpressionType.IsNotNull:
151                     return operands[0].Evaluate() != null;
152                 case SpecialExpressionType.Concat:
153                     {
154                         var values = new List<string>();
155                         foreach (var operand in operands)
156                         {
157                             var value = operand.Evaluate();
158                             if (value != null)
159                                 values.Add(System.Convert.ToString(value, CultureInfo.InvariantCulture));
160                             else
161                                 values.Add(null);
162                         }
163                         return string.Concat(values.ToArray());
164                     }
165                 case SpecialExpressionType.Count:
166                     {
167                         var value = operands[0].Evaluate();
168                         // TODO: string is IEnumerable. See what we do here
169                         if (value is IEnumerable)
170                         {
171                             int count = 0;
172                             foreach (var dontCare in (IEnumerable)value)
173                                 count++;
174                             return count;
175                         }
176                         // TODO: by default, shall we answer 1 or throw an exception?
177                         return 1;
178                     }
179                 case SpecialExpressionType.Exists:
180                     {
181                         var value = operands[0].Evaluate();
182                         // TODO: string is IEnumerable. See what we do here
183                         if (value is IEnumerable)
184                         {
185                             return true;
186                         }
187                         // TODO: by default, shall we answer 1 or throw an exception?
188                         return false;
189                     }
190                 case SpecialExpressionType.Min:
191                     {
192                         decimal? min = null;
193                         foreach (var operand in operands)
194                         {
195                             var value = System.Convert.ToDecimal(operand.Evaluate());
196                             if (!min.HasValue || value < min.Value)
197                                 min = value;
198                         }
199                         return System.Convert.ChangeType(min.Value, operands[0].Type);
200                     }
201                 case SpecialExpressionType.Max:
202                     {
203                         decimal? max = null;
204                         foreach (var operand in operands)
205                         {
206                             var value = System.Convert.ToDecimal(operand.Evaluate());
207                             if (!max.HasValue || value > max.Value)
208                                 max = value;
209                         }
210                         return System.Convert.ChangeType(max.Value, operands[0].Type);
211                     }
212                 case SpecialExpressionType.Sum:
213                     {
214                         decimal sum = operands.Select(op => System.Convert.ToDecimal(op.Evaluate())).Sum();
215                         return System.Convert.ChangeType(sum, operands.First().Type);
216                     }
217                 case SpecialExpressionType.Average:
218                     {
219                         decimal sum = 0;
220                         foreach (var operand in operands)
221                             sum += System.Convert.ToDecimal(operand.Evaluate());
222                         return sum / operands.Count;
223                     }
224                 case SpecialExpressionType.StringLength:
225                     return operands[0].Evaluate().ToString().Length;
226                 case SpecialExpressionType.ToUpper:
227                     return operands[0].Evaluate().ToString().ToUpper();
228                 case SpecialExpressionType.ToLower:
229                     return operands[0].Evaluate().ToString().ToLower();
230                 case SpecialExpressionType.Substring:
231                     return EvaluateStandardCallInvoke("SubString", operands);
232                 case SpecialExpressionType.In:
233                     throw new NotImplementedException();
234                 case SpecialExpressionType.Replace:
235                     return EvaluateStandardCallInvoke("Replace", operands);
236                 case SpecialExpressionType.Remove:
237                     return EvaluateStandardCallInvoke("Remove", operands);
238                 case SpecialExpressionType.IndexOf:
239                     return EvaluateStandardCallInvoke("IndexOf", operands);
240                 case SpecialExpressionType.Year:
241                     return ((DateTime)operands[0].Evaluate()).Year;
242                 case SpecialExpressionType.Month:
243                     return ((DateTime)operands[0].Evaluate()).Month;
244                 case SpecialExpressionType.Day:
245                     return ((DateTime)operands[0].Evaluate()).Day;
246                 case SpecialExpressionType.Hour:
247                     return ((DateTime)operands[0].Evaluate()).Hour;
248                 case SpecialExpressionType.Minute:
249                     return ((DateTime)operands[0].Evaluate()).Minute;
250                 case SpecialExpressionType.Second:
251                     return ((DateTime)operands[0].Evaluate()).Second;
252                 case SpecialExpressionType.Millisecond:
253                     return ((DateTime)operands[0].Evaluate()).Millisecond;
254                 case SpecialExpressionType.Now:
255                     return DateTime.Now;
256                 case SpecialExpressionType.Date:
257                     return ((DateTime)operands[0].Evaluate());
258                 case SpecialExpressionType.DateDiffInMilliseconds:
259                     return ((DateTime)operands[0].Evaluate()) - ((DateTime)operands[1].Evaluate());
260                 case SpecialExpressionType.Abs:
261                 case SpecialExpressionType.Exp:
262                 case SpecialExpressionType.Floor:
263                 case SpecialExpressionType.Ln:
264                 case SpecialExpressionType.Log:
265                 case SpecialExpressionType.Pow:
266                 case SpecialExpressionType.Round:
267                 case SpecialExpressionType.Sign:
268                 case SpecialExpressionType.Sqrt:
269                     return EvaluateMathCallInvoke(SpecialNodeType, operands);
270                 default:
271                     throw Error.BadArgument("S0116: Unknown SpecialExpressionType ({0})", SpecialNodeType);
272             }
273         }
274 
EvaluateMathCallInvoke(SpecialExpressionType SpecialNodeType, ReadOnlyCollection<Expression> operands)275         private object EvaluateMathCallInvoke(SpecialExpressionType SpecialNodeType, ReadOnlyCollection<Expression> operands)
276         {
277             return typeof(Math).GetMethod(SpecialNodeType.ToString(), operands.Skip(1).Select(op => op.Type).ToArray())
278                     .Invoke(null, operands.Skip(1).Select(op => op.Evaluate()).ToArray());
279         }
EvaluateStatardMemberAccess(string propertyName, ReadOnlyCollection<Expression> operands)280         protected object EvaluateStatardMemberAccess(string propertyName, ReadOnlyCollection<Expression> operands)
281         {
282             return operands[0].Type.GetProperty(propertyName).GetValue(operands.First().Evaluate(), null);
283         }
EvaluateStandardCallInvoke(string methodName, ReadOnlyCollection<Expression> operands)284         protected object EvaluateStandardCallInvoke(string methodName, ReadOnlyCollection<Expression> operands)
285         {
286             return operands[0].Type.GetMethod(methodName,
287                                        operands.Skip(1).Select(op => op.Type).ToArray())
288                                        .Invoke(operands[0].Evaluate(),
289                                                operands.Skip(1).Select(op => op.Evaluate()).ToArray());
290         }
291     }
292 }