File: FunctionStubFileWriter.cs

package info (click to toggle)
mono-reference-assemblies 3.12.1%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 604,240 kB
  • ctags: 625,505
  • sloc: cs: 3,967,741; xml: 2,793,081; ansic: 418,042; java: 60,435; sh: 14,833; makefile: 11,576; sql: 7,956; perl: 1,467; cpp: 1,446; yacc: 1,203; python: 598; asm: 422; sed: 16; php: 1
file content (485 lines) | stat: -rw-r--r-- 23,684 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Data.Spatial;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data.Metadata.Edm;

namespace LinqFunctionStubsGenerator
{
    /// <summary>
    /// Class that writes the files using the input function metadata.
    /// </summary>
    class FunctionStubFileWriter
    {
        private readonly IEnumerable<EdmFunction> _functions;
        private readonly Dictionary<String, String> _funcDictionaryToUse;
        private readonly Dictionary<String, String> _paramDictionaryToUse;

        /// <summary>
        /// Initializes member fields.
        /// </summary>
        /// <param name="functions">Metadata about functions</param>
        /// <param name="functionNames">Dictionary containing better function names</param>
        /// <param name="parameterNames">Dictionary containing better parameter names</param>
        public FunctionStubFileWriter(IEnumerable<EdmFunction> functions, Dictionary<String, String> functionNames, Dictionary<String, String> parameterNames)
        {
            _functions = functions;
            _funcDictionaryToUse = functionNames;
            _paramDictionaryToUse = parameterNames;
        }

        /// <summary>
        ///  Generates code and writes to the specified file.
        /// </summary>
        /// <param name="destinationFile">Filepath of the destination file</param>
        /// <param name="namespaceString">Namespace where the class will reside</param>
        /// <param name="className">Generated class name</param>
        /// <param name="attributeNamespace">The 'EdmFunction' attribute parameter</param>
        /// <param name="pascalCaseFunctionNames">If the input function names need to be pascal cased.</param>
        public void GenerateToFile(String destinationFile, string namespaceString, string className, string attributeNamespace, Boolean pascalCaseFunctionNames)
        {
            //Use passed in class information to generate the class definition.
            StringWriter newCode = GenerateCode(namespaceString, className, attributeNamespace, pascalCaseFunctionNames);
            
            //Write to file.
            try
            {
                using (StreamWriter writer = new StreamWriter(destinationFile, false))
                {
                    writer.Write(newCode.ToString());
                    writer.Close();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// Main code generator method.
        /// </summary>
        /// <param name="namespaceString">Namespace where the class will reside</param>
        /// <param name="className">Generated class name</param>
        /// <param name="attributeNamespace">The 'EdmFunction' attribute parameter</param>
        /// <param name="pascalCaseFunctionNames">If the input function names need to be pascal cased.</param>
        public StringWriter GenerateCode(string namespaceString, string className, string attributeNamespace, Boolean pascalCaseFunctionNames)
        {        
            StringWriter newCode = new StringWriter();
            Boolean isAggregateFunction;
            bool hasSByteParameterOrReturnType;
            bool hasStringInParameterName;
            String separator;

            GenerateFileHeader(newCode, className);
            GenerateUsingStatements(newCode);

            newCode.WriteLine("namespace " + namespaceString);
            newCode.WriteLine("{");

            GenerateClassHeader(newCode, className, attributeNamespace);

            foreach (System.Data.Metadata.Edm.EdmFunction function in _functions)
            {
                isAggregateFunction = false;
                hasSByteParameterOrReturnType = false;
                hasStringInParameterName = false;
                separator = "";
                
                String functionNameToUse = FindCorrectFunctionName(function.Name, pascalCaseFunctionNames);
                GenerateFunctionHeader(newCode,attributeNamespace,function.Name);
                Type returnType = ((PrimitiveType)(function.ReturnParameter.TypeUsage.EdmType)).ClrEquivalentType;

                //Suppress warning that 'SByte' is not CLS-compliant.
                if (returnType == typeof(SByte))
                {
                    hasSByteParameterOrReturnType = true;
                }
                StringBuilder functionSignatureString = new StringBuilder();
                AppendSpaces(functionSignatureString, 8);
                functionSignatureString.Append("public static ");
                WriteType(functionSignatureString, returnType);
                functionSignatureString.Append(functionNameToUse + "(");

                ReadOnlyMetadataCollection<FunctionParameter> functionParameters = function.Parameters;
                Type parameterType;                    
                foreach (System.Data.Metadata.Edm.FunctionParameter parameter in functionParameters)
                {
                    String parameterNameToUse = parameter.Name;
                    parameterNameToUse = FindCorrectParameterName(parameterNameToUse);
                    
                    //Detect aggregate functions. They have just one parameter and so stub can be generated here.
                    if (parameter.TypeUsage.EdmType.GetType() == typeof(System.Data.Metadata.Edm.CollectionType))
                    {
                        isAggregateFunction = true;
                        if (parameterNameToUse.ToLowerInvariant().Contains("string"))
                        {
                            hasStringInParameterName = true;
                        }
                        
                        System.Data.Metadata.Edm.CollectionType collectionType = (System.Data.Metadata.Edm.CollectionType)parameter.TypeUsage.EdmType;
                        parameterType = ((PrimitiveType)(collectionType.TypeUsage.EdmType)).ClrEquivalentType;
                        //Detect if there is an 'SByte' parameter to suppress non-CLS-compliance warning.
                        //Generate the attribute only once for each function.
                        if (parameterType == typeof(SByte))
                        {
                            hasSByteParameterOrReturnType = true;
                        }
                        
                        //Generate stub for non-nullable input parameters
                        functionSignatureString.Append("IEnumerable<" + parameterType.ToString());
                        //Supress fxcop message and CLS non-compliant attributes
                        GenerateFunctionAttributes(newCode, hasStringInParameterName, hasSByteParameterOrReturnType);
                        //Use the constructed function signature
                        newCode.Write(functionSignatureString.ToString());
                        GenerateAggregateFunctionStub(newCode,parameterType, returnType, parameterNameToUse, false);

                        //Generate stub for nullable input parameters
                        //Special Case: Do not generate nullable stub for input parameter of types Byte[]
                        //and String, since they are nullable.
                        if (!IsNullableType(parameterType))
                        {
                            GenerateFunctionHeader(newCode, attributeNamespace, function.Name);
                            //Supress fxcop message and CLS non-compliant attributes
                            GenerateFunctionAttributes(newCode, hasStringInParameterName, hasSByteParameterOrReturnType);
                            //Use the constructed function signature
                            newCode.Write(functionSignatureString.ToString());
                            GenerateAggregateFunctionStub(newCode, parameterType, returnType, parameterNameToUse, true);
                        }
                    } //End of processing parameters for aggregate functions.
                    //Process each parameter in case of non-aggregate functions.
                    else
                    {
                        parameterType = ((PrimitiveType)(parameter.TypeUsage.EdmType)).ClrEquivalentType;
                        functionSignatureString.Append(separator);
                        WriteType(functionSignatureString, parameterType);
                        functionSignatureString.Append(parameterNameToUse);
                        separator = ", ";
                        //Detect if there is an 'SByte' parameter to suppress non-CLS-compliance warning.
                        if (parameterType == typeof(SByte))
                        {
                            hasSByteParameterOrReturnType = true;
                        }
                        if (parameterNameToUse.ToLowerInvariant().Contains("string"))
                        {
                            hasStringInParameterName = true;
                        }
                    }
                } //End for each parameter
                
                //Generate stub for Non-aggregate functions after all input parameters are found.
                if (!isAggregateFunction)
                {   
                    //Supress fxcop supression and CLS non-compliant attributes
                    GenerateFunctionAttributes(newCode, hasStringInParameterName, hasSByteParameterOrReturnType);
                    newCode.WriteLine(functionSignatureString.ToString() + ")");
                    AppendSpaces(newCode, 8);
                    newCode.WriteLine("{");
                    WriteExceptionStatement(newCode);
                }
            } //End for each function

            AppendSpaces(newCode, 4);
            newCode.WriteLine("}");
            newCode.WriteLine("}");
            newCode.Close();
            return newCode;
        }

        /// <summary>
        /// Generates the content for aggregate function stubs.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        /// <param name="parameterType">Type of parameter</param>
        /// <param name="returnType">Return type of function</param>
        /// <param name="parameterNameToUse">Parameter name</param>
        /// <param name="isNullable">If this invokation is for generating the nullable/non-nullable stub</param>
        private void GenerateAggregateFunctionStub(StringWriter newCode, Type parameterType, Type returnType, string parameterNameToUse, bool isNullable)
        {
            GenerateQuestionMark(newCode, isNullable);
            newCode.Write("> ");
            newCode.WriteLine(parameterNameToUse + ")");
            AppendSpaces(newCode, 8);
            newCode.WriteLine("{");
            AppendSpaces(newCode, 12);
            newCode.Write("ObjectQuery<" + parameterType.ToString());
            GenerateQuestionMark(newCode, isNullable);
            newCode.Write("> objectQuerySource = " + parameterNameToUse);
            newCode.Write(" as ObjectQuery<" + parameterType.ToString());
            GenerateQuestionMark(newCode, isNullable);
            newCode.WriteLine(">;");
            
            AppendSpaces(newCode, 12);
            newCode.WriteLine("if (objectQuerySource != null)");
            AppendSpaces(newCode, 12);
            newCode.WriteLine("{");
            AppendSpaces(newCode, 16);
            newCode.Write("return ((IQueryable)objectQuerySource).Provider.Execute<" + returnType.ToString());

            //Special case: Byte[], String are nullable
            if (!IsNullableType(returnType))
            {
                newCode.Write("?");
            }
            newCode.Write(">(Expression.Call((MethodInfo)MethodInfo.GetCurrentMethod(),Expression.Constant(" + parameterNameToUse);
            newCode.WriteLine(")));");
            AppendSpaces(newCode, 12);
            newCode.WriteLine("}");
            WriteExceptionStatement(newCode);
        }

        /// <summary>
        /// Writes a question mark in generated code for nullable parameters
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        /// <param name="isNullable">Is this invokation for generating the nullable/non-nullable stub</param>
        public void GenerateQuestionMark(StringWriter newCode, bool isNullable)
        {
            if (isNullable)
            {
                newCode.Write("?");
            }
        }

        /// <summary>
        /// Generates fxcop suppression and CLS non-compliant attributes.
        /// </summary>
        /// <param name="newCode"></param>
        /// <param name="hasStringInParameterName"></param>
        /// <param name="hasSByteParameterOrReturnType"></param>
        private void GenerateFunctionAttributes(StringWriter newCode, bool hasStringInParameterName, bool hasSByteParameterOrReturnType)
        {
            //Supress fxcop message about 'string' in argument names.
            if (hasStringInParameterName)
            {
                GenerateFxcopSuppressionAttribute(newCode);
            }
            //Suppress warning that 'SByte' is not CLS-compliant, generate the attribute only once.
            if (hasSByteParameterOrReturnType)
            {
                GenerateSByteCLSNonComplaintAttribute(newCode);
            }
        }

        /// <summary>
        /// Generates the output file header.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        /// <param name="className">Generated class name</param>
        private  void GenerateFileHeader(StringWriter newCode, String className)
        {
            DateTime theTime = DateTime.Now;
            newCode.WriteLine("//------------------------------------------------------------------------------");
            newCode.WriteLine("// <auto-generated>");
            newCode.WriteLine("//     This code was generated by a tool.");
            newCode.Write("//     Generation date and time : ");
            newCode.WriteLine(theTime.Date.ToShortDateString() + " " + theTime.TimeOfDay.ToString());
            newCode.WriteLine("//");
            newCode.WriteLine("//     Changes to this file will be lost if the code is regenerated.");
            newCode.WriteLine("// </auto-generated>");
            newCode.WriteLine("//------------------------------------------------------------------------------");
            newCode.WriteLine();
        }

        /// <summary>
        /// Generates 'using' statements in the output C# file.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        private  void GenerateUsingStatements(StringWriter newCode)
        {
            newCode.WriteLine(@"using System;");
            newCode.WriteLine(@"using System.Collections.Generic;");
            newCode.WriteLine(@"using System.Data.Objects;");
            newCode.WriteLine(@"using System.Data.Objects.DataClasses;");
            newCode.WriteLine(@"using System.Linq;");
            newCode.WriteLine(@"using System.Linq.Expressions;");
            newCode.WriteLine(@"using System.Reflection;");
            newCode.WriteLine();
        }

        /// <summary>
        /// Generates the function header comment and 'EdmFunction' attribute for every function.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        /// <param name="attributeNamespace">Namespace parameter to the 'EdmFunction' attribute</param>
        /// <param name="functionName">Function name</param>
        private  void GenerateFunctionHeader(StringWriter newCode, String attributeNamespace, String functionName)
        {
                AppendSpaces(newCode, 8);
                newCode.WriteLine("/// <summary>");
                AppendSpaces(newCode, 8);
                newCode.WriteLine("/// Proxy for the function " + attributeNamespace + "." + functionName);
                AppendSpaces(newCode, 8);
                newCode.WriteLine("/// </summary>");
                AppendSpaces(newCode, 8);
                newCode.WriteLine("[EdmFunction(\""+attributeNamespace+"\", \""+functionName+"\")]");
        }

        /// <summary>
        /// Writes the function attribute to suppress warnings about non-CLS compliance due 
        /// to SByte arguments.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        private  void GenerateSByteCLSNonComplaintAttribute(StringWriter newCode)
        {
            AppendSpaces(newCode, 8);
            newCode.WriteLine("[CLSCompliant(false)]");
        }

        /// <summary>
        /// Writes the function attribute to suppress 'fxcop' errors about argument names
        /// like 'string1','characterString', etc.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        private  void GenerateFxcopSuppressionAttribute(StringWriter newCode)
        {
            AppendSpaces(newCode, 8);
            newCode.WriteLine("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Naming\", \"CA1720:IdentifiersShouldNotContainTypeNames\", MessageId = \"string\")]");
        }

        /// <summary>
        /// Returns if the given data type is nullable.
        /// </summary>
        /// <param name="parameterType">Input data type</param>
        /// <returns>True of input type is nullable, false otherwise</returns>
        private  Boolean IsNullableType(Type parameterType)
        {
            return parameterType == typeof (Byte[]) ||
                   parameterType == typeof (String) ||
                   typeof (DbGeometry).IsAssignableFrom(parameterType) ||
                   typeof (DbGeography).IsAssignableFrom(parameterType);
        }

        /// <summary>
        /// Generates type information in code according to whether it is nullable
        /// </summary>
        /// <param name="code">Buffer used to store the code constructed so far</param>
        /// <param name="parameterType">Input type</param>
        private  void WriteType(StringBuilder code, Type parameterType)
        {
            code.Append(parameterType.ToString());
            if (!IsNullableType(parameterType))
            {
                code.Append("?");
            }
            code.Append(" ");
        }

        /// <summary>
        /// Generates the exception statement which is thrown from each function stub.
        /// </summary>
        /// <param name="code">Buffer used to store the code constructed so far</param>
        private  void WriteExceptionStatement(StringWriter code)
        {
            AppendSpaces(code, 12);
            code.WriteLine("throw new NotSupportedException(\"This function can only be invoked from LINQ to Entities.\");");
            AppendSpaces(code, 8);
            code.WriteLine("}");
            code.WriteLine();
        }

        /// <summary>
        /// Appends spaces to code line, this is avoid tabs as required by coding standards.
        /// </summary>
        /// <param name="str">Buffer used to store the code constructed so far</param>
        /// <param name="num">Number of desired spaces</param>
        private  void AppendSpaces(StringWriter str, int num)
        {
            for (int i = 0; i < num; i++)
            {
                str.Write(" ");
            }
        }

        /// <summary>
        /// Appends spaces to code line, this is avoid tabs as required by coding standards.
        /// </summary>
        /// <param name="str">Buffer used to store the code constructed so far</param>
        /// <param name="num">Number of desired spaces</param>
        private  void AppendSpaces(StringBuilder str, int num)
        {
            for (int i = 0; i < num; i++)
            {
                str.Append(" ");
            }
        }

        /// <summary>
        /// Looks up dictionary to find better function name(that fxcop likes). If not in dictionary
        /// Pascal cases it if asked to.
        /// </summary>
        /// <param name="inputName">Function name from metadata</param>
        /// <param name="pascalCaseFunctionNames">If required to Pascal case function name</param>
        /// <returns>Better function name</returns>
        private  String FindCorrectFunctionName(String inputName, Boolean pascalCaseFunctionNames)
        {
            if (_funcDictionaryToUse == null)
            {
                return inputName;
            }
            
            String value;
            if (_funcDictionaryToUse.TryGetValue(inputName, out value))
            {
                return value;
            }
            else if (pascalCaseFunctionNames)
            {
                String interFunctionName = inputName.ToLower();
                char[] charFuncName = interFunctionName.ToCharArray();
                charFuncName[0] = System.Char.ToUpper(charFuncName[0]);
                return new String(charFuncName);
            }
            return inputName;
        }

        /// <summary>
        /// Looks up dictionary to find better parameter name(that fxcop likes and one that is friendlier). 
        /// </summary>
        /// <param name="inputName">Parameter name from metadata</param>
        /// <returns>Better parameter name</returns>
        private  String FindCorrectParameterName(String inputParameterName)
        {
            String value;

            if (_paramDictionaryToUse == null)
            {
                return inputParameterName;
            }
            if (_paramDictionaryToUse.TryGetValue(inputParameterName, out value))
            {
                return value;
            }
            return inputParameterName;
        }

        /// <summary>
        /// Generates header for the class.
        /// </summary>
        /// <param name="newCode">Buffer used to store the code constructed so far</param>
        /// <param name="className">Output class name</param>
        public  void GenerateClassHeader(StringWriter newCode, String className, String namespaceString)
        {
            AppendSpaces(newCode, 4);
            newCode.WriteLine("/// <summary>");
            AppendSpaces(newCode, 4);
            newCode.Write("/// Contains function stubs that expose " +namespaceString);
            newCode.WriteLine(" methods in Linq to Entities.");
            AppendSpaces(newCode, 4);
            newCode.WriteLine("/// </summary>");
            AppendSpaces(newCode, 4);
            newCode.Write("public static ");
            if (className.Equals("EntityFunctions", StringComparison.Ordinal))
            {
                newCode.Write("partial ");
            }
            newCode.WriteLine("class " + className);
            AppendSpaces(newCode, 4);
            newCode.WriteLine("{");
        }
    }
}