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.Generic;
\r
30 using System.Linq.Expressions;
\r
31 using System.Reflection;
\r
33 using DbLinq.Data.Linq.Sugar;
\r
34 using DbLinq.Data.Linq.Sugar.ExpressionMutator;
\r
36 namespace DbLinq.Data.Linq.Sugar.Implementation
\r
39 /// Analyzes language patterns and replace them with standard expressions
\r
41 internal class ExpressionLanguageParser : IExpressionLanguageParser
\r
43 public virtual Expression Parse(Expression expression, BuilderContext builderContext)
\r
45 return expression.Recurse(e => Analyze(e, builderContext));
\r
48 protected delegate Expression Analyzer(Expression expression);
\r
50 protected IEnumerable<Analyzer> Analyzers;
\r
51 private readonly object analyzersLock = new object();
\r
53 protected virtual IEnumerable<Analyzer> GetAnalyzers()
\r
55 lock (analyzersLock)
\r
57 if (Analyzers == null)
\r
59 // man, this is the kind of line I'm proud of :)
\r
60 Analyzers = from method in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
\r
61 let m = (Analyzer)Delegate.CreateDelegate(typeof(Analyzer), this, method, false)
\r
64 Analyzers = Analyzers.ToList(); // result is faster from here
\r
70 protected virtual Expression Analyze(Expression expression, BuilderContext builderContext)
\r
72 foreach (var analyze in GetAnalyzers())
\r
74 var e = analyze(expression);
\r
82 /// Tests for Convert.ToBoolean()
\r
84 /// <param name="expression"></param>
\r
85 /// <returns></returns>
\r
86 protected virtual Expression AnalyzeConvertToBoolean(Expression expression)
\r
88 var methodCallExpression = expression as MethodCallExpression;
\r
89 if (methodCallExpression != null)
\r
91 if (methodCallExpression.Method.DeclaringType.Name == "Convert")
\r
93 if (methodCallExpression.Method.Name == "ToBoolean")
\r
94 return Expression.Convert(methodCallExpression.Arguments[0], methodCallExpression.Type);
\r
101 /// Used to determine if the Expression is a VB CompareString
\r
102 /// Returns an equivalent Expression if true
\r
104 /// <param name="expression"></param>
\r
105 /// <returns></returns>
\r
106 protected virtual Expression AnalyzeCompareString(Expression expression)
\r
109 var testedExpression = GetComparedToZero(expression, out equals);
\r
110 if (testedExpression != null)
\r
112 var methodExpression = testedExpression as MethodCallExpression;
\r
113 if (methodExpression != null
\r
114 && methodExpression.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators"
\r
115 && methodExpression.Method.Name == "CompareString")
\r
117 return Expression.Equal(methodExpression.Arguments[0], methodExpression.Arguments[1]);
\r
123 protected virtual Expression AnalyzeLikeString(Expression expression)
\r
125 var methodExpression = expression as MethodCallExpression;
\r
126 if (methodExpression != null
\r
127 && methodExpression.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.LikeOperator"
\r
128 && methodExpression.Method.Name == "LikeString")
\r
130 var lambda = (Expression<Func<string, string, bool>>)((a, b) => a.StartsWith(b));
\r
131 return Expression.Invoke(lambda, methodExpression.Arguments[0], methodExpression.Arguments[1]);
\r
137 /// Determines if an expression is a comparison to 0
\r
139 /// <param name="expression"></param>
\r
140 /// <param name="equals">True if ==, False if !=</param>
\r
141 /// <returns>The compared Expression or null</returns>
\r
142 protected static Expression GetComparedToZero(Expression expression, out bool equals)
\r
144 equals = expression.NodeType == ExpressionType.Equal;
\r
145 if (equals || expression.NodeType == ExpressionType.NotEqual)
\r
147 var binaryExpression = (BinaryExpression)expression;
\r
148 if (IsZero(binaryExpression.Right))
\r
149 return binaryExpression.Left;
\r
150 if (IsZero(binaryExpression.Left))
\r
151 return binaryExpression.Right;
\r
157 /// Determines if an expression is constant value 0
\r
159 /// <param name="expression"></param>
\r
160 /// <returns></returns>
\r
161 protected static bool IsZero(Expression expression)
\r
163 if (expression.NodeType == ExpressionType.Constant)
\r
165 var unaryExpression = (ConstantExpression)expression;
\r
166 return (unaryExpression.Value as int? ?? 0) == 0; // you too, have fun with C# operators
\r