Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / EntityModel / Emitters / ClientApiGenerator.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ClientApiGenerator.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.CodeDom;
12 using System.CodeDom.Compiler;
13 using System.Collections;
14 using System.Collections.Generic;
15 using System.Data;
16 using System.Data.EntityModel.Emitters;
17 using SOM = System.Data.EntityModel.SchemaObjectModel;
18 using System.Diagnostics;
19 using System.Data.Metadata.Edm;
20 using System.Data.Entity.Design;
21 using System.IO;
22 using System.Data.EntityModel.SchemaObjectModel;
23 using System.Data.Entity.Design.SsdlGenerator;
24 using System.Linq;
25 using System.Data.Entity.Design.Common;
26 using System.Runtime.Versioning;
27
28 namespace System.Data.EntityModel
29 {
30     /// <summary>
31     /// Summary description for ClientApiGenerator.
32     /// </summary>
33     internal sealed class ClientApiGenerator
34     {
35         #region Instance Fields
36         private string _codeNamespace = null;
37         private CodeCompileUnit _compileUnit = null;
38         private bool _isLanguageCaseSensitive = true;
39
40         private EdmItemCollection _edmItemCollection = null;
41         private Schema _sourceSchema = null;
42         private FixUpCollection _fixUps = null;
43         private AttributeEmitter _attributeEmitter = null;
44         EntityClassGenerator _generator;
45         List<EdmSchemaError> _errors;
46         TypeReference _typeReference = new TypeReference();
47
48         #endregion
49
50         #region Public Methods
51         public ClientApiGenerator(Schema sourceSchema, EdmItemCollection edmItemCollection, EntityClassGenerator generator, List<EdmSchemaError> errors)
52         {
53             Debug.Assert(sourceSchema != null, "sourceSchema is null");
54             Debug.Assert(edmItemCollection != null, "edmItemCollection is null");
55             Debug.Assert(generator != null, "generator is null");
56             Debug.Assert(errors != null, "errors is null");
57
58             _edmItemCollection = edmItemCollection;
59             _sourceSchema = sourceSchema;
60             _generator = generator;
61             _errors = errors;
62             _attributeEmitter = new AttributeEmitter(_typeReference);
63         }
64
65         /// <summary>
66         /// Parses a source Schema and outputs client-side generated code to
67         /// the output TextWriter.
68         /// </summary>
69         /// <param name="schema">The source Schema</param>
70         /// <param name="output">The TextWriter in which to write the output</param>
71         /// <param name="outputUri">The Uri for the output. Can be null.</param>
72         /// <returns>A list of GeneratorErrors.</returns>
73         [ResourceExposure(ResourceScope.None)] //No resource is exposed.
74         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] //For Path.GetTempPath method. 
75                                                                             //We use tha path to create a temp file stream which is consistent with the resource consumption of machine.
76
77         internal void GenerateCode(LazyTextWriterCreator target, string targetLocation)
78         {
79             Debug.Assert(target != null, "target parameter is null");
80
81             IndentedTextWriter indentedTextWriter = null;
82             System.IO.Stream tempFileStream = null;
83             System.IO.StreamReader reader = null;
84             System.IO.StreamWriter writer = null;
85             TempFileCollection tempFiles = null;
86             try
87             {
88                 CodeDomProvider provider = null;
89                 switch (Language)
90                 {
91                     case LanguageOption.GenerateCSharpCode:
92                         provider = new Microsoft.CSharp.CSharpCodeProvider();
93                         break;
94
95                     case LanguageOption.GenerateVBCode:
96                         provider = new Microsoft.VisualBasic.VBCodeProvider();
97                         break;
98                 }
99
100                 _isLanguageCaseSensitive = (provider.LanguageOptions & LanguageOptions.CaseInsensitive) == 0;
101
102                 new NamespaceEmitter(this, _codeNamespace, target.TargetFilePath).Emit();
103
104                 // if there were errors we don't need the output file
105                 if (RealErrorsExist)
106                 {
107                     return;
108                 }
109
110                 if (FixUps.Count == 0 || !FixUpCollection.IsLanguageSupported(Language))
111                 {
112                     indentedTextWriter = new IndentedTextWriter(target.GetOrCreateTextWriter(), "\t");
113                 }
114                 else
115                 {
116                     // need to write to a temporary file so we can do fixups...
117                     tempFiles = new TempFileCollection(Path.GetTempPath());
118                     string filename = Path.Combine(tempFiles.TempDir, "EdmCodeGenFixup-" + Guid.NewGuid().ToString() + ".tmp");
119                     tempFiles.AddFile(filename, false);
120                     tempFileStream = new System.IO.FileStream(filename, System.IO.FileMode.CreateNew, System.IO.FileAccess.ReadWrite,
121                         System.IO.FileShare.None);
122                     indentedTextWriter = new IndentedTextWriter(new System.IO.StreamWriter(tempFileStream), "\t");
123                 }
124
125                 CodeGeneratorOptions styleOptions = new CodeGeneratorOptions();
126                 styleOptions.BracingStyle = "C";
127                 styleOptions.BlankLinesBetweenMembers = false;
128                 styleOptions.VerbatimOrder = true;
129                 provider.GenerateCodeFromCompileUnit(CompileUnit, indentedTextWriter, styleOptions);
130
131                 // if we wrote to a temp file need to post process the file...
132                 if (tempFileStream != null)
133                 {
134                     indentedTextWriter.Flush();
135                     tempFileStream.Seek(0, System.IO.SeekOrigin.Begin);
136                     reader = new System.IO.StreamReader(tempFileStream);
137                     FixUps.Do(reader, target.GetOrCreateTextWriter(), Language, SourceObjectNamespaceName != string.Empty);
138                 }
139             }
140             catch (System.UnauthorizedAccessException ex)
141             {
142                 AddError(ModelBuilderErrorCode.SecurityError, EdmSchemaErrorSeverity.Error, ex);
143             }
144             catch (System.IO.FileNotFoundException ex)
145             {
146                 AddError(ModelBuilderErrorCode.FileNotFound, EdmSchemaErrorSeverity.Error, ex);
147             }
148             catch (System.Security.SecurityException ex)
149             {
150                 AddError(ModelBuilderErrorCode.SecurityError, EdmSchemaErrorSeverity.Error, ex);
151             }
152             catch (System.IO.DirectoryNotFoundException ex)
153             {
154                 AddError(ModelBuilderErrorCode.DirectoryNotFound, EdmSchemaErrorSeverity.Error, ex);
155             }
156             catch (System.IO.IOException ex)
157             {
158                 AddError(ModelBuilderErrorCode.IOException, EdmSchemaErrorSeverity.Error, ex);
159             }
160             finally
161             {
162                 if (indentedTextWriter != null)
163                 {
164                     indentedTextWriter.Close();
165                 }
166                 if (tempFileStream != null)
167                 {
168                     tempFileStream.Close();
169                 }
170                 if (tempFiles != null)
171                 {
172                     tempFiles.Delete();
173                     ((IDisposable)tempFiles).Dispose();
174                 }
175                 if (reader != null)
176                 {
177                     reader.Close();
178                 }
179                 if (writer != null)
180                 {
181                     writer.Close();
182                 }
183             }
184         }
185
186         /// <summary>
187         /// Verification code invoked for types
188         /// </summary>
189         /// <param name="item">The type being generated</param>
190         internal void VerifyLanguageCaseSensitiveCompatibilityForType(GlobalItem item)
191         {
192             if (_isLanguageCaseSensitive)
193             {
194                 return; // no validation necessary
195             }
196
197             try
198             {
199                 _edmItemCollection.GetItem<GlobalItem>(
200                                                         item.Identity,
201                                                         true   // ignore case
202                                                     );
203             }
204             catch (InvalidOperationException)
205             {
206                 AddError(Strings.ItemExistsWithDifferentCase(item.BuiltInTypeKind.ToString(), item.Identity), ModelBuilderErrorCode.IncompatibleSettingForCaseSensitiveOption,
207                     EdmSchemaErrorSeverity.Error, item.Identity);
208             }
209         }
210
211
212         /// <summary>
213         /// Verification code invoked for properties
214         /// </summary>
215         /// <param name="item">The property or navigation property being generated</param>
216         internal void VerifyLanguageCaseSensitiveCompatibilityForProperty(EdmMember item)
217         {
218             if (_isLanguageCaseSensitive)
219             {
220                 return; // no validation necessary
221             }
222
223             Debug.Assert(item != null);
224
225             ReadOnlyMetadataCollection<EdmMember> members = item.DeclaringType.Members;
226             
227             HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
228
229             foreach (EdmMember member in members)
230             {
231                 if (set.Contains(member.Identity) &&
232                     item.Identity.Equals(member.Identity, StringComparison.OrdinalIgnoreCase))
233                 {
234                     AddError(Strings.PropertyExistsWithDifferentCase(item.Identity), ModelBuilderErrorCode.IncompatibleSettingForCaseSensitiveOption,
235                     EdmSchemaErrorSeverity.Error, item.DeclaringType.FullName, item.Identity);
236                 }
237                 else
238                 {
239                     set.Add(member.Identity);
240                 }
241             }
242         }
243
244         /// <summary>
245         /// Verification code invoked for entity sets
246         /// </summary>
247         /// <param name="item">The entity container being generated</param>
248         internal void VerifyLanguageCaseSensitiveCompatibilityForEntitySet(System.Data.Metadata.Edm.EntityContainer item)
249         {
250             if (_isLanguageCaseSensitive)
251             {
252                 return; // no validation necessary
253             }
254
255             Debug.Assert(item != null);
256
257             HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
258
259             foreach (EntitySetBase entitySetBase in item.BaseEntitySets)
260             {
261                 if (MetadataUtil.IsEntitySet(entitySetBase))
262                 {
263                     EntitySet entitySet = (EntitySet)entitySetBase;
264                     if (set.Contains(entitySet.Identity))
265                     {
266                         AddError(ModelBuilderErrorCode.IncompatibleSettingForCaseSensitiveOption,
267                         EdmSchemaErrorSeverity.Error, new InvalidOperationException(Strings.EntitySetExistsWithDifferentCase(entitySet.Identity)),
268                         item.Name);
269                     }
270                     else
271                     {
272                         set.Add(entitySet.Identity);
273                     }
274                 }
275             }
276         }
277
278         internal IEnumerable<EdmType> GetDirectSubTypes(EdmType edmType)
279         {
280             return _edmItemCollection.GetItems<EdmType>().Where(b => b.BaseType == edmType);
281         }
282
283         private static System.Data.EntityModel.SchemaObjectModel.SchemaElement GetSchemaElement(
284             System.Data.EntityModel.SchemaObjectModel.Schema schema, string itemIdentity)
285         {
286             List<System.Data.EntityModel.SchemaObjectModel.SchemaType> schemaTypes =
287                            schema.SchemaTypes.Where(p => p.Identity == itemIdentity).ToList();
288             if (null != schemaTypes && schemaTypes.Count > 0)
289             {
290                 return (System.Data.EntityModel.SchemaObjectModel.SchemaElement)schemaTypes.First();
291             }
292             else
293             {
294                 return null;
295             }
296
297         }
298
299         internal static void GetElementLocationInfo(System.Data.EntityModel.SchemaObjectModel.Schema schema, string itemIdentity, out int lineNumber, out int linePosition)
300         {
301             System.Data.EntityModel.SchemaObjectModel.SchemaElement element = GetSchemaElement(schema, itemIdentity);
302
303             if(null != element)
304             {
305                 lineNumber = element.LineNumber;
306                 linePosition = element.LinePosition;
307             }
308             else
309             {
310                 lineNumber = linePosition = -1;
311             }
312         }
313
314         internal static void GetElementLocationInfo(System.Data.EntityModel.SchemaObjectModel.Schema schema, string parentIdentity, string itemIdentity, out int lineNumber, out int linePosition)
315         {
316             lineNumber = linePosition = -1;
317
318             System.Data.EntityModel.SchemaObjectModel.SchemaElement element = GetSchemaElement(schema, parentIdentity);
319             System.Data.EntityModel.SchemaObjectModel.StructuredType elementWithProperty = 
320                 element as System.Data.EntityModel.SchemaObjectModel.StructuredType;
321
322             if (null != elementWithProperty && elementWithProperty.Properties.ContainsKey(itemIdentity))
323             {
324                     lineNumber = elementWithProperty.Properties[itemIdentity].LineNumber;
325                     linePosition = elementWithProperty.Properties[itemIdentity].LinePosition;
326             }
327             else if( null != element)
328             {
329                 lineNumber = element.LineNumber;
330                 linePosition = element.LinePosition;
331             }
332         }
333
334         #endregion
335
336         #region Internal Properties
337
338         internal LanguageOption Language
339         {
340             get
341             {
342                 return _generator.LanguageOption;
343             }
344         }
345
346         internal TypeReference TypeReference
347         {
348             get { return _typeReference; }
349         }
350
351         internal CodeCompileUnit CompileUnit
352         {
353             get
354             {
355                 if (_compileUnit == null)
356                     _compileUnit = new CodeCompileUnit();
357
358                 return _compileUnit;
359             }
360         }
361
362         public void AddError(string message, ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity)
363         {
364             _errors.Add(new EdmSchemaError(message, (int)errorCode, severity));
365         }
366
367         public void AddError(ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, Exception ex)
368         {
369             _errors.Add(new EdmSchemaError(ex.Message, (int)errorCode, severity, ex));
370         }
371
372         internal void AddError(string message, ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, Exception ex)
373         {
374             _errors.Add(new EdmSchemaError(message, (int)errorCode, severity, ex));
375         }
376
377         internal void AddError(ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, Exception ex, string itemIdentity)
378         {
379             int lineNumber, linePosition;
380             ClientApiGenerator.GetElementLocationInfo(this._sourceSchema, itemIdentity, out lineNumber, out linePosition);
381
382             _errors.Add(new EdmSchemaError(ex.Message, (int)errorCode, severity, this._sourceSchema.Location, lineNumber, linePosition, ex));
383         }
384
385         internal void AddError(string message, ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, string itemIdentity)
386         {
387             int lineNumber, linePosition;
388             ClientApiGenerator.GetElementLocationInfo(this._sourceSchema, itemIdentity, out lineNumber, out linePosition);
389
390             _errors.Add(new EdmSchemaError(message, (int)errorCode, severity, this._sourceSchema.Location, lineNumber, linePosition));
391         }
392
393         internal void AddError(string message, ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, string parentIdentity, string itemIdentity)
394         {
395             int lineNumber, linePosition;
396             ClientApiGenerator.GetElementLocationInfo(this._sourceSchema, parentIdentity, itemIdentity, out lineNumber, out linePosition);
397
398             _errors.Add(new EdmSchemaError(message, (int)errorCode, severity, this._sourceSchema.Location, lineNumber, linePosition));
399         }
400
401         /// <summary>
402         /// Check collection for any real errors (Severity != Warning)
403         /// </summary>
404         public bool RealErrorsExist
405         {
406             get
407             {
408                 foreach (EdmSchemaError error in _errors)
409                 {
410                     if (error.Severity != EdmSchemaErrorSeverity.Warning)
411                         return true;
412                 }
413                 return false;
414             }
415         }
416
417         public IEnumerable<GlobalItem> GetSourceTypes()
418         {
419             foreach (SOM.SchemaType type in _sourceSchema.SchemaTypes)
420             {
421                 if (type is SOM.ModelFunction)
422                 {
423                     continue;
424                 }
425
426                 yield return _edmItemCollection.GetItem<GlobalItem>(type.Identity);
427             }
428         }
429
430         public CodeTypeReference GetFullyQualifiedTypeReference(EdmType type)
431         {
432             string fullObjectName = CreateFullName(GetObjectNamespace(type.NamespaceName), type.Name);
433             return TypeReference.FromString(fullObjectName);
434         }
435
436         public CodeTypeReference GetFullyQualifiedTypeReference(EdmType type, bool addGlobalQualifier)
437         {
438             string fullObjectName = CreateFullName(GetObjectNamespace(type.NamespaceName), type.Name);
439             return TypeReference.FromString(fullObjectName, addGlobalQualifier);
440         }
441
442         private string CreateFullName(string namespaceName, string name)
443         {
444             if (string.IsNullOrEmpty(namespaceName))
445             {
446                 return name;
447             }
448
449             return namespaceName + "." + name;
450         }
451
452         public CodeTypeReference GetLeastPossibleQualifiedTypeReference(EdmType type)
453         {
454             string typeRef;
455
456             if (type.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
457             {
458                 return type.ClrType.IsValueType ? TypeReference.NullableForType(type.ClrType) : TypeReference.ForType(type.ClrType);
459             }
460             else
461             {
462                 if (type.NamespaceName == SourceEdmNamespaceName)
463                 {
464                     // we are already generating in this namespace, no need to qualify it
465                     typeRef = type.Name;
466                 }
467                 else
468                 {
469                     typeRef = CreateFullName(GetObjectNamespace(type.NamespaceName), type.Name);
470                 }
471             }
472
473             return TypeReference.FromString(typeRef);
474         }
475
476         public string SourceEdmNamespaceName
477         {
478             get
479             {
480                 return _sourceSchema.Namespace;
481             }
482         }
483
484         public string SourceObjectNamespaceName
485         {
486             get
487             {
488                 return GetObjectNamespace(SourceEdmNamespaceName);
489             }
490         }
491
492         private string GetObjectNamespace(string csdlNamespaceName)
493         {
494             Debug.Assert(csdlNamespaceName != null, "csdlNamespaceName is null");
495
496             string objectNamespace;
497             if (_generator.EdmToObjectNamespaceMap.TryGetObjectNamespace(csdlNamespaceName, out objectNamespace))
498             {
499                 return objectNamespace;
500             }
501
502             return csdlNamespaceName;
503         }
504
505
506         /// <summary>
507         /// 
508         /// </summary>
509         /// <value></value>
510         internal FixUpCollection FixUps
511         {
512             get
513             {
514                 if (_fixUps == null)
515                     _fixUps = new FixUpCollection();
516
517                 return _fixUps;
518             }
519         }
520
521         internal AttributeEmitter AttributeEmitter
522         {
523             get { return _attributeEmitter; }
524         }
525
526         internal bool IsLanguageCaseSensitive
527         {
528             get { return _isLanguageCaseSensitive; }
529         }
530
531         internal StringComparison LanguageAppropriateStringComparer
532         {
533             get
534             {
535                 if (IsLanguageCaseSensitive)
536                 {
537                     return StringComparison.Ordinal;
538                 }
539                 else
540                 {
541                     return StringComparison.OrdinalIgnoreCase;
542                 }
543             }
544         }
545
546         /// <summary>
547         /// Helper method that raises the TypeGenerated event
548         /// </summary>
549         /// <param name="eventArgs">The event arguments passed to the subscriber</param>
550         internal void RaiseTypeGeneratedEvent(TypeGeneratedEventArgs eventArgs)
551         {
552             _generator.RaiseTypeGeneratedEvent(eventArgs);
553         }
554
555         /// <summary>
556         /// Helper method that raises the PropertyGenerated event
557         /// </summary>
558         /// <param name="eventArgs">The event arguments passed to the subscriber</param>
559         internal void RaisePropertyGeneratedEvent(PropertyGeneratedEventArgs eventArgs)
560         {
561             _generator.RaisePropertyGeneratedEvent(eventArgs);
562         }
563
564         #endregion
565
566     }
567 }