2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Sugar / Implementation / ExpressionLanguageParser.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 System.Reflection;\r
32 \r
33 using DbLinq.Data.Linq.Sugar;\r
34 using DbLinq.Data.Linq.Sugar.ExpressionMutator;\r
35 \r
36 namespace DbLinq.Data.Linq.Sugar.Implementation\r
37 {\r
38     /// <summary>\r
39     /// Analyzes language patterns and replace them with standard expressions\r
40     /// </summary>\r
41     internal class ExpressionLanguageParser : IExpressionLanguageParser\r
42     {\r
43         public virtual Expression Parse(Expression expression, BuilderContext builderContext)\r
44         {\r
45             return expression.Recurse(e => Analyze(e, builderContext));\r
46         }\r
47 \r
48         protected delegate Expression Analyzer(Expression expression);\r
49 \r
50         protected IEnumerable<Analyzer> Analyzers;\r
51         private readonly object analyzersLock = new object();\r
52 \r
53         protected virtual IEnumerable<Analyzer> GetAnalyzers()\r
54         {\r
55             lock (analyzersLock)\r
56             {\r
57                 if (Analyzers == null)\r
58                 {\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
62                                 where m != null\r
63                                 select m;\r
64                     Analyzers = Analyzers.ToList(); // result is faster from here\r
65                 }\r
66                 return Analyzers;\r
67             }\r
68         }\r
69 \r
70         protected virtual Expression Analyze(Expression expression, BuilderContext builderContext)\r
71         {\r
72             foreach (var analyze in GetAnalyzers())\r
73             {\r
74                 var e = analyze(expression);\r
75                 if (e != null)\r
76                     return e;\r
77             }\r
78             return expression;\r
79         }\r
80 \r
81         /// <summary>\r
82         /// Tests for Convert.ToBoolean()\r
83         /// </summary>\r
84         /// <param name="expression"></param>\r
85         /// <returns></returns>\r
86         protected virtual Expression AnalyzeConvertToBoolean(Expression expression)\r
87         {\r
88             var methodCallExpression = expression as MethodCallExpression;\r
89             if (methodCallExpression != null)\r
90             {\r
91                 if (methodCallExpression.Method.DeclaringType.Name == "Convert")\r
92                 {\r
93                     if (methodCallExpression.Method.Name == "ToBoolean")\r
94                         return Expression.Convert(methodCallExpression.Arguments[0], methodCallExpression.Type);\r
95                 }\r
96             }\r
97             return null;\r
98         }\r
99 \r
100         /// <summary>\r
101         /// Used to determine if the Expression is a VB CompareString\r
102         /// Returns an equivalent Expression if true\r
103         /// </summary>\r
104         /// <param name="expression"></param>\r
105         /// <returns></returns>\r
106         protected virtual Expression AnalyzeCompareString(Expression expression)\r
107         {\r
108             bool equals;\r
109             var testedExpression = GetComparedToZero(expression, out equals);\r
110             if (testedExpression != null)\r
111             {\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
116                 {\r
117                     return Expression.Equal(methodExpression.Arguments[0], methodExpression.Arguments[1]);\r
118                 }\r
119             }\r
120             return null;\r
121         }\r
122 \r
123         protected virtual Expression AnalyzeLikeString(Expression expression)\r
124         {\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
129             {\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
132             }\r
133             return null;\r
134         }\r
135 \r
136         /// <summary>\r
137         /// Determines if an expression is a comparison to 0\r
138         /// </summary>\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
143         {\r
144             equals = expression.NodeType == ExpressionType.Equal;\r
145             if (equals || expression.NodeType == ExpressionType.NotEqual)\r
146             {\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
152             }\r
153             return null;\r
154         }\r
155 \r
156         /// <summary>\r
157         /// Determines if an expression is constant value 0\r
158         /// </summary>\r
159         /// <param name="expression"></param>\r
160         /// <returns></returns>\r
161         protected static bool IsZero(Expression expression)\r
162         {\r
163             if (expression.NodeType == ExpressionType.Constant)\r
164             {\r
165                 var unaryExpression = (ConstantExpression)expression;\r
166                 return (unaryExpression.Value as int? ?? 0) == 0; // you too, have fun with C# operators\r
167             }\r
168             return false;\r
169         }\r
170     }\r
171 }\r