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.Generic;
29 using System.IO;
30 using System.Linq.Expressions;
31 
32 namespace DbLinq.Util
33 {
34     internal static class TextWriterExtension
35     {
36         /// <summary>
37         /// Writes an Expression to the given textWriter
38         /// </summary>
39         /// <param name="textWriter"></param>
40         /// <param name="expression"></param>
WriteExpression(this TextWriter textWriter, Expression expression)41         public static void WriteExpression(this TextWriter textWriter, Expression expression)
42         {
43             try
44             {
45                 var rawLines = new List<string>(Write(expression, string.Empty, 0));
46                 rawLines.Insert(0, string.Empty);
47                 var lines = rawLines.ToArray();
48                 textWriter.WriteLine(string.Join(Environment.NewLine, lines));
49             }
50             // we just ignore NREs
51             catch (NullReferenceException)
52             {
53             }
54         }
55 
56         /// <summary>
57         /// Expression visitor. Calls correct method, depending on expression real type
58         /// </summary>
59         /// <param name="expression"></param>
60         /// <param name="header"></param>
61         /// <param name="depth"></param>
62         /// <returns></returns>
Write(Expression expression, string header, int depth)63         private static IList<string> Write(Expression expression, string header, int depth)
64         {
65             if (expression == null)
66                 return new[] { WriteLiteral("(null)", header, depth) };
67             if (expression is BinaryExpression)
68                 return WriteEx((BinaryExpression)expression, header, depth);
69             if (expression is ConditionalExpression)
70                 return WriteEx((ConditionalExpression)expression, header, depth);
71             if (expression is ConstantExpression)
72                 return WriteEx((ConstantExpression)expression, header, depth);
73             if (expression is InvocationExpression)
74                 return WriteEx((InvocationExpression)expression, header, depth);
75             if (expression is LambdaExpression)
76                 return WriteEx((LambdaExpression)expression, header, depth);
77             if (expression is MemberExpression)
78                 return WriteEx((MemberExpression)expression, header, depth);
79             if (expression is MethodCallExpression)
80                 return WriteEx((MethodCallExpression)expression, header, depth);
81             if (expression is NewExpression)
82                 return WriteEx((NewExpression)expression, header, depth);
83             if (expression is NewArrayExpression)
84                 return WriteEx((NewArrayExpression)expression, header, depth);
85             if (expression is MemberInitExpression)
86                 return WriteEx((MemberInitExpression)expression, header, depth);
87             if (expression is ListInitExpression)
88                 return WriteEx((ListInitExpression)expression, header, depth);
89             if (expression is ParameterExpression)
90                 return WriteEx((ParameterExpression)expression, header, depth);
91             if (expression is TypeBinaryExpression)
92                 return WriteEx((TypeBinaryExpression)expression, header, depth);
93             if (expression is UnaryExpression)
94                 return WriteEx((UnaryExpression)expression, header, depth);
95 
96             return new[] { WriteHeader(expression, header, depth) };
97         }
98 
99         #region typed Expression writer
100 
WriteEx(BinaryExpression expression, string header, int depth)101         private static IList<string> WriteEx(BinaryExpression expression, string header, int depth)
102         {
103             var lines = new List<string> { WriteHeader(expression, header, depth++) };
104             lines.AddRange(Write(expression.Left, "Left ", depth));
105             lines.AddRange(Write(expression.Right, "Right", depth));
106             return lines;
107         }
108 
WriteEx(ConditionalExpression expression, string header, int depth)109         private static IList<string> WriteEx(ConditionalExpression expression, string header, int depth)
110         {
111             var lines = new List<string> { WriteHeader(expression, header, depth++) };
112             lines.AddRange(Write(expression.Test, "If  ", depth));
113             lines.AddRange(Write(expression.IfTrue, "Then", depth));
114             lines.AddRange(Write(expression.IfFalse, "Else", depth));
115             return lines;
116         }
117 
WriteEx(ConstantExpression expression, string header, int depth)118         private static IList<string> WriteEx(ConstantExpression expression, string header, int depth)
119         {
120             var lines = new List<string>
121                             {
122                                 WriteHeader(expression, header, depth++),
123                                 WriteLiteral(expression.Value, "Value", depth)
124                             };
125             return lines;
126         }
127 
WriteEx(InvocationExpression expression, string header, int depth)128         private static IList<string> WriteEx(InvocationExpression expression, string header, int depth)
129         {
130             var lines = new List<string> { WriteHeader(expression, header, depth++) };
131             lines.AddRange(Write(expression.Expression, "Call", depth));
132             for (int i = 0; i < expression.Arguments.Count; i++)
133                 lines.AddRange(Write(expression.Arguments[i], string.Format("#{0:0##}", i), depth));
134             return lines;
135         }
136 
WriteEx(LambdaExpression expression, string header, int depth)137         private static IList<string> WriteEx(LambdaExpression expression, string header, int depth)
138         {
139             var lines = new List<string> { WriteHeader(expression, header, depth++) };
140             lines.AddRange(Write(expression.Body, "Call", depth));
141             for (int i = 0; i < expression.Parameters.Count; i++)
142                 lines.AddRange(Write(expression.Parameters[i], string.Format("#{0:0##}", i), depth));
143             return lines;
144         }
145 
WriteEx(MemberExpression expression, string header, int depth)146         private static IList<string> WriteEx(MemberExpression expression, string header, int depth)
147         {
148             var lines = new List<string> { WriteHeader(expression, header, depth++) };
149             lines.AddRange(Write(expression.Expression, "Object", depth));
150             lines.Add(WriteLiteral(expression.Member.Name, "Member", depth));
151             return lines;
152         }
153 
WriteEx(MethodCallExpression expression, string header, int depth)154         private static IList<string> WriteEx(MethodCallExpression expression, string header, int depth)
155         {
156             var lines = new List<string> { WriteHeader(expression, header, depth++) };
157             lines.AddRange(Write(expression.Object, "Object", depth));
158             lines.Add(WriteLiteral(expression.Method.Name, "Method", depth));
159             for (int i = 0; i < expression.Arguments.Count; i++)
160                 lines.AddRange(Write(expression.Arguments[i], string.Format("#{0:0####}", i), depth));
161             return lines;
162         }
163 
WriteEx(NewExpression expression, string header, int depth)164         private static IList<string> WriteEx(NewExpression expression, string header, int depth)
165         {
166             var lines = new List<string>
167                             {
168                                 WriteHeader(expression, header, depth++),
169                                 WriteLiteral(expression.Constructor.Name, "Ctor", depth)
170                             };
171             for (int i = 0; i < expression.Arguments.Count; i++)
172                 lines.AddRange(Write(expression.Arguments[i], string.Format("#{0:0##}", i), depth));
173             if (expression.Members != null)
174             {
175                 for (int i = 0; i < expression.Members.Count; i++)
176                     lines.Add(WriteLiteral(expression.Members[i].Name, string.Format("M{0:0##}", i), depth));
177             }
178             return lines;
179         }
180 
WriteEx(NewArrayExpression expression, string header, int depth)181         private static IList<string> WriteEx(NewArrayExpression expression, string header, int depth)
182         {
183             var lines = new List<string> { WriteHeader(expression, header, depth++) };
184             for (int i = 0; i < expression.Expressions.Count; i++)
185                 lines.AddRange(Write(expression.Expressions[i], string.Format("#{0:0##}", i), depth));
186             return lines;
187         }
188 
WriteEx(MemberInitExpression expression, string header, int depth)189         private static IList<string> WriteEx(MemberInitExpression expression, string header, int depth)
190         {
191             var lines = new List<string> { WriteHeader(expression, header, depth++) };
192             lines.AddRange(Write(expression.NewExpression, "New", depth));
193             for (int i = 0; i < expression.Bindings.Count; i++)
194                 lines.Add(WriteLiteral(expression.Bindings[i].Member.Name, string.Format("B{0:0##}", i), depth));
195             return lines;
196         }
197 
WriteEx(ListInitExpression expression, string header, int depth)198         private static IList<string> WriteEx(ListInitExpression expression, string header, int depth)
199         {
200             var lines = new List<string> { WriteHeader(expression, header, depth++) };
201             lines.AddRange(Write(expression.NewExpression, "New", depth));
202             for (int i = 0; i < expression.Initializers.Count; i++)
203             {
204                 lines.Add(WriteLiteral(expression.Initializers[i].AddMethod.Name, string.Format("Method{0:0##}", i), depth));
205                 for (int j = 0; j < expression.Initializers[i].Arguments.Count; j++)
206                 {
207                     lines.AddRange(Write(expression.Initializers[i].Arguments[j], string.Format("#{0:0##}", j), depth + 1));
208                 }
209             }
210             return lines;
211         }
212 
WriteEx(ParameterExpression expression, string header, int depth)213         private static IList<string> WriteEx(ParameterExpression expression, string header, int depth)
214         {
215             var lines = new List<string>
216                             {
217                                 WriteHeader(expression, header, depth++),
218                                 WriteLiteral(expression.Name, "Parameter", depth)
219                             };
220             return lines;
221         }
222 
WriteEx(TypeBinaryExpression expression, string header, int depth)223         private static IList<string> WriteEx(TypeBinaryExpression expression, string header, int depth)
224         {
225             var lines = new List<string> { WriteHeader(expression, header, depth++) };
226             lines.AddRange(Write(expression.Expression, "Expression", depth));
227             lines.Add(WriteLiteral(expression.TypeOperand.Name, "Type      ", depth));
228             return lines;
229         }
230 
WriteEx(UnaryExpression expression, string header, int depth)231         private static IList<string> WriteEx(UnaryExpression expression, string header, int depth)
232         {
233             var lines = new List<string>
234                             {
235                                 WriteHeader(expression, header, depth++),
236                                 WriteLiteral(expression.Method != null ? expression.Method.Name : null, "Method ", depth)
237                             };
238             lines.AddRange(Write(expression.Operand, "Operand", depth));
239             return lines;
240         }
241 
WriteHeader(Expression expression, string header, int depth)242         private static string WriteHeader(Expression expression, string header, int depth)
243         {
244             return string.Format("{0}{1} {2} ({3})", GetPrefix(depth), header, expression.NodeType, expression.GetType().Name);
245         }
246 
WriteLiteral(object value, string header, int depth)247         private static string WriteLiteral(object value, string header, int depth)
248         {
249             return string.Format("{0}{1}: {2}", GetPrefix(depth), header, value);
250         }
251 
GetPrefix(int depth)252         private static string GetPrefix(int depth)
253         {
254             return string.Empty.PadRight(depth * 2, '.');
255         }
256 
257         #endregion
258     }
259 }
260