5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
\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
14 // The above copyright notice and this permission notice shall be included in
\r
15 // all copies or substantial portions of the Software.
\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
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
34 using System.Collections.ObjectModel;
\r
36 using DbLinq.Data.Linq.Sugar;
\r
37 using DbLinq.Data.Linq.Sugar.ExpressionMutator;
\r
38 using DbLinq.Data.Linq.Sugar.Expressions;
\r
40 namespace DbLinq.Data.Linq.Sugar.Expressions
\r
43 /// Holds new expression types (sql related), all well as their operands
\r
45 [DebuggerDisplay("SpecialExpression {SpecialNodeType}")]
\r
49 class SpecialExpression : OperandsMutableExpression, IExecutableExpression
\r
51 public SpecialExpressionType SpecialNodeType { get { return (SpecialExpressionType)NodeType; } }
\r
53 protected static Type GetSpecialExpressionTypeType(SpecialExpressionType specialExpressionType, IList<Expression> operands)
\r
56 if (operands.Count > 0)
\r
57 defaultType = operands[0].Type;
\r
60 switch (specialExpressionType) // SETuse
\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
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
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
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
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
125 throw Error.BadArgument("S0058: Unknown SpecialExpressionType value {0}", specialExpressionType);
\r
129 public SpecialExpression(SpecialExpressionType expressionType, params Expression[] operands)
\r
130 : base((ExpressionType)expressionType, GetSpecialExpressionTypeType(expressionType, operands), operands)
\r
134 public SpecialExpression(SpecialExpressionType expressionType, IList<Expression> operands)
\r
135 : base((ExpressionType)expressionType, GetSpecialExpressionTypeType(expressionType, operands), operands)
\r
139 protected override Expression Mutate2(IList<Expression> newOperands)
\r
141 return new SpecialExpression((SpecialExpressionType)NodeType, newOperands);
\r
144 public object Execute()
\r
146 switch (SpecialNodeType) // SETuse
\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
154 var values = new List<string>();
\r
155 foreach (var operand in operands)
\r
157 var value = operand.Evaluate();
\r
159 values.Add(System.Convert.ToString(value, CultureInfo.InvariantCulture));
\r
163 return string.Concat(values.ToArray());
\r
165 case SpecialExpressionType.Count:
\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
172 foreach (var dontCare in (IEnumerable)value)
\r
176 // TODO: by default, shall we answer 1 or throw an exception?
\r
179 case SpecialExpressionType.Exists:
\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
187 // TODO: by default, shall we answer 1 or throw an exception?
\r
190 case SpecialExpressionType.Min:
\r
192 decimal? min = null;
\r
193 foreach (var operand in operands)
\r
195 var value = System.Convert.ToDecimal(operand.Evaluate());
\r
196 if (!min.HasValue || value < min.Value)
\r
199 return System.Convert.ChangeType(min.Value, operands[0].Type);
\r
201 case SpecialExpressionType.Max:
\r
203 decimal? max = null;
\r
204 foreach (var operand in operands)
\r
206 var value = System.Convert.ToDecimal(operand.Evaluate());
\r
207 if (!max.HasValue || value > max.Value)
\r
210 return System.Convert.ChangeType(max.Value, operands[0].Type);
\r
212 case SpecialExpressionType.Sum:
\r
214 decimal sum = operands.Select(op => System.Convert.ToDecimal(op.Evaluate())).Sum();
\r
215 return System.Convert.ChangeType(sum, operands.First().Type);
\r
217 case SpecialExpressionType.Average:
\r
220 foreach (var operand in operands)
\r
221 sum += System.Convert.ToDecimal(operand.Evaluate());
\r
222 return sum / operands.Count;
\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
271 throw Error.BadArgument("S0116: Unknown SpecialExpressionType ({0})", SpecialNodeType);
\r
275 private object EvaluateMathCallInvoke(SpecialExpressionType SpecialNodeType, ReadOnlyCollection<Expression> operands)
\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
280 protected object EvaluateStatardMemberAccess(string propertyName, ReadOnlyCollection<Expression> operands)
\r
282 return operands[0].Type.GetProperty(propertyName).GetValue(operands.First().Evaluate(), null);
\r
284 protected object EvaluateStandardCallInvoke(string methodName, ReadOnlyCollection<Expression> operands)
\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