3af30235c2eff40a83d6c4f91876befd14a20967
[mono.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / EntityModel / Emitters / StructuredTypeEmitter.cs
1 //---------------------------------------------------------------------
2 // <copyright file="StructuredTypeEmitter.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.CodeDom;
13 using System.Data;
14 using System.Data.EntityModel.SchemaObjectModel;
15 using Som = System.Data.EntityModel.SchemaObjectModel;
16 using System.Data.Entity.Design;
17 using System.Data.Metadata.Edm;
18 using System.Diagnostics;
19 using System.Reflection;
20 using System.Data.Objects.DataClasses;
21 using System.Data.Entity.Design.Common;
22 using System.Data.Entity.Design.SsdlGenerator;
23
24
25 namespace System.Data.EntityModel.Emitters
26 {
27     /// <summary>
28     /// Summary description for StructuredTypeEmitter.
29     /// </summary>
30     internal abstract class StructuredTypeEmitter : SchemaTypeEmitter
31     {
32         #region Public Methods
33         private bool _usingStandardBaseClass = true;
34
35
36
37         /// <summary>
38         /// 
39         /// </summary>
40         /// <returns></returns>
41         public override CodeTypeDeclarationCollection EmitApiClass()
42         {
43             Validate(); // emitter-specific validation
44
45             CodeTypeReference baseType = this.GetBaseType();
46
47             // raise the TypeGenerated event
48             TypeGeneratedEventArgs eventArgs = new TypeGeneratedEventArgs(Item, baseType);
49             this.Generator.RaiseTypeGeneratedEvent(eventArgs);
50
51             // public [abstract] partial class ClassName
52             CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(Item.Name);
53             typeDecl.IsPartial = true;
54             typeDecl.TypeAttributes = System.Reflection.TypeAttributes.Class;
55             if (Item.Abstract)
56             {
57                 typeDecl.TypeAttributes |= System.Reflection.TypeAttributes.Abstract;
58             }
59
60             SetTypeVisibility(typeDecl);
61
62             EmitTypeAttributes(Item.Name, typeDecl, eventArgs.AdditionalAttributes);
63
64             // : baseclass
65             AssignBaseType(typeDecl, baseType, eventArgs.BaseType);
66
67             AddInterfaces(Item.Name, typeDecl, eventArgs.AdditionalInterfaces);
68
69             CommentEmitter.EmitSummaryComments(Item, typeDecl.Comments);
70
71             // Since abstract types cannot be instantiated, skip the factory method for abstract types
72             if ( (typeDecl.TypeAttributes & System.Reflection.TypeAttributes.Abstract) == 0)
73                 EmitFactoryMethod(typeDecl);
74
75             EmitProperties(typeDecl);
76
77             // additional members, if provided by the event subscriber
78             this.AddMembers(Item.Name, typeDecl, eventArgs.AdditionalMembers);
79
80             CodeTypeDeclarationCollection typeDecls = new CodeTypeDeclarationCollection();
81             typeDecls.Add(typeDecl);
82             return typeDecls;
83         }
84
85         #endregion
86
87         #region Protected Methods
88
89         /// <summary>
90         /// 
91         /// </summary>
92         /// <returns></returns>
93         protected virtual CodeTypeReference GetBaseType()
94         {
95             if (Item.BaseType == null)
96                 return null;
97
98             return Generator.GetLeastPossibleQualifiedTypeReference(Item.BaseType);
99         }
100
101         /// <summary>
102         /// 
103         /// </summary>
104         /// <param name="generator"></param>
105         /// <param name="structuredType"></param>
106         protected StructuredTypeEmitter(ClientApiGenerator generator, StructuralType structuralType)
107             : base(generator, structuralType)
108         {
109         }
110
111         /// <summary>
112         /// 
113         /// </summary>
114         /// <param name="typeDecl"></param>
115         protected override void EmitTypeAttributes(CodeTypeDeclaration typeDecl)
116         {
117             Generator.AttributeEmitter.EmitTypeAttributes(this, typeDecl);
118             base.EmitTypeAttributes(typeDecl);
119         }
120
121         /// <summary>
122         /// 
123         /// </summary>
124         /// <param name="typeDecl"></param>
125         protected virtual void EmitProperties(CodeTypeDeclaration typeDecl)
126         {
127
128             foreach (EdmProperty property in Item.GetDeclaredOnlyMembers<EdmProperty>())
129             {
130                 PropertyEmitter propertyEmitter = new PropertyEmitter(Generator, property, _usingStandardBaseClass);
131                 propertyEmitter.Emit(typeDecl);
132             }
133         }
134
135         protected abstract ReadOnlyMetadataCollection<EdmProperty> GetProperties();
136     
137         /// <summary>
138         /// Emit static factory method which creates an instance of the class and initializes
139         /// non-nullable properties (taken as arguments)
140         /// </summary>
141         /// <param name="typeDecl"></param>
142         protected virtual void EmitFactoryMethod(CodeTypeDeclaration typeDecl)
143         {
144             
145             
146             // build list of non-nullable properties
147             ReadOnlyMetadataCollection<EdmProperty> properties = GetProperties();
148             List<EdmProperty> parameters = new List<EdmProperty>(properties.Count);
149             foreach (EdmProperty property in properties)
150             {
151                 bool include = IncludeFieldInFactoryMethod(property);
152                 if (include)
153                 {
154                     parameters.Add(property);
155                 }
156             }
157
158             // if there are no parameters, we don't emit anything (1 is for the null element)
159             // nor do we emit everything if this is the Ref propertied ctor and the parameter list is the same as the many parametered ctor
160             if (parameters.Count < 1)
161             {
162                 return;
163             }
164
165             CodeMemberMethod method = new CodeMemberMethod();
166             Generator.AttributeEmitter.EmitGeneratedCodeAttribute(method);
167
168             CodeTypeReference typeRef = TypeReference.FromString(Item.Name);
169             UniqueIdentifierService uniqueIdentifierService = new UniqueIdentifierService(Generator.IsLanguageCaseSensitive, name => Utils.FixParameterName(name));
170             string instanceName = uniqueIdentifierService.AdjustIdentifier(Item.Name);
171
172             // public static Class CreateClass(...)
173             method.Attributes = MemberAttributes.Static|MemberAttributes.Public;
174             method.Name = "Create" + Item.Name;
175             if (NavigationPropertyEmitter.IsNameAlreadyAMemberName(Item, method.Name, Generator.LanguageAppropriateStringComparer))
176             {
177                 Generator.AddError(Strings.GeneratedFactoryMethodNameConflict(method.Name, Item.Name),
178                     ModelBuilderErrorCode.GeneratedFactoryMethodNameConflict,
179                     EdmSchemaErrorSeverity.Error, Item.FullName);
180             }
181
182             method.ReturnType = typeRef;
183             
184             // output method summary comments 
185             CommentEmitter.EmitSummaryComments(Strings.FactoryMethodSummaryComment(Item.Name), method.Comments);
186
187
188             // Class class = new Class();
189             CodeVariableDeclarationStatement createNewInstance = new CodeVariableDeclarationStatement(
190                 typeRef, instanceName, new CodeObjectCreateExpression(typeRef));
191             method.Statements.Add(createNewInstance);
192             CodeVariableReferenceExpression instanceRef = new CodeVariableReferenceExpression(instanceName);
193
194             // iterate over the properties figuring out which need included in the factory method
195             foreach (EdmProperty property in parameters)
196             {
197                 // CreateClass( ... , propType propName ...)
198                 PropertyEmitter propertyEmitter = new PropertyEmitter(Generator, property, UsingStandardBaseClass);
199                 CodeTypeReference propertyTypeReference = propertyEmitter.PropertyType;
200                 String parameterName = uniqueIdentifierService.AdjustIdentifier(propertyEmitter.PropertyName);
201                 CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(
202                     propertyTypeReference, parameterName);
203                 CodeArgumentReferenceExpression paramRef = new CodeArgumentReferenceExpression(paramDecl.Name);
204                 method.Parameters.Add(paramDecl);
205
206                 // add comment describing the parameter
207                 CommentEmitter.EmitParamComments(paramDecl, Strings.FactoryParamCommentGeneral(propertyEmitter.PropertyName), method.Comments);
208
209                 CodeExpression newPropertyValue;
210                 if (MetadataUtil.IsComplexType(propertyEmitter.Item.TypeUsage.EdmType))
211                 {
212                     List<CodeExpression> complexVerifyParameters = new List<CodeExpression>();
213                     complexVerifyParameters.Add(paramRef);
214                     complexVerifyParameters.Add(new CodePrimitiveExpression(propertyEmitter.PropertyName));
215                     
216                     newPropertyValue =
217                         new CodeMethodInvokeExpression(
218                             PropertyEmitter.CreateEdmStructuralObjectRef(TypeReference),
219                             Utils.VerifyComplexObjectIsNotNullName,
220                             complexVerifyParameters.ToArray());
221                 }
222                 else
223                 {
224                     newPropertyValue = paramRef;
225                 }
226
227                 // Scalar property:
228                 //     Property = param;
229                 // Complex property:
230                 //     Property = StructuralObject.VerifyComplexObjectIsNotNull(param, propertyName);
231
232                 method.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(instanceRef, propertyEmitter.PropertyName), newPropertyValue));            
233             }
234
235             // return class;
236             method.Statements.Add(new CodeMethodReturnStatement(instanceRef));
237
238             // actually add the method to the class
239             typeDecl.Members.Add(method);
240         }
241         
242
243         #endregion
244         
245         #region Protected Properties
246
247         internal new StructuralType Item
248         {
249             get
250             {
251                 return base.Item as StructuralType;
252             }
253         }
254
255         protected bool UsingStandardBaseClass
256         {
257             get { return _usingStandardBaseClass; }
258         }
259
260         #endregion
261             
262         #region Private Methods
263
264         /// <summary>
265         /// 
266         /// </summary>
267         /// <param name="property"></param>
268         /// <returns></returns>
269         private bool IncludeFieldInFactoryMethod(EdmProperty property)
270         {
271             if (property.Nullable)
272             {
273                 return false;
274             }
275
276             if (PropertyEmitter.HasDefault(property))
277             {
278                 return false;
279             }
280
281             if ((PropertyEmitter.GetGetterAccessibility(property) != MemberAttributes.Public &&
282                 PropertyEmitter.GetSetterAccessibility(property) != MemberAttributes.Public) ||
283                 // declared in a sub type, but not setter accessbile from this type
284                 (Item != property.DeclaringType && PropertyEmitter.GetSetterAccessibility(property) == MemberAttributes.Private)
285                )
286             {
287                 return false;
288             }
289
290             return true;
291         }
292
293         private void AssignBaseType(CodeTypeDeclaration typeDecl,
294                                     CodeTypeReference baseType,
295                                     CodeTypeReference eventReturnedBaseType)
296         {
297             if (eventReturnedBaseType != null && !eventReturnedBaseType.Equals(baseType))
298             {
299                 _usingStandardBaseClass = false;
300                 typeDecl.BaseTypes.Add(eventReturnedBaseType);
301             }
302             else 
303             {
304                 if (baseType != null)
305                 {
306                     typeDecl.BaseTypes.Add(baseType);
307                 }
308             }
309         }
310         #endregion
311     }
312 }