Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / EntityModel / Emitters / PropertyEmitter.cs
1 //---------------------------------------------------------------------
2 // <copyright file="PropertyEmitter.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 using System.CodeDom;
11 using System.Collections.Generic;
12 using System.Data.Entity.Design;
13 using System.Data.Entity.Design.Common;
14 using System.Data.Entity.Design.SsdlGenerator;
15 using System.Data.Metadata.Edm;
16 using System.Data.Objects.ELinq;
17 using System.Diagnostics;
18 using System.Globalization;
19
20 namespace System.Data.EntityModel.Emitters
21 {
22     internal sealed class PropertyEmitter : PropertyEmitterBase
23     {
24         private CodeFieldReferenceExpression _fieldRef = null;
25         private CodeFieldReferenceExpression _complexPropertyInitializedFieldRef = null;
26
27         // statics
28         private const string NestedStoreObjectCollection = "InlineObjectCollection";
29         private const string DetachFromParentMethodName = "DetachFromParent";
30
31         #region Public Methods
32
33         public PropertyEmitter(ClientApiGenerator generator, EdmProperty property, bool declaringTypeUsesStandardBaseType)
34             : base(generator, property, declaringTypeUsesStandardBaseType)
35         {
36         }
37
38         /// <summary>
39         /// Emit the declaration of the property for the class.
40         /// </summary>
41         /// <returns>The Property declaration pieces of the CodeDom.</returns>
42         public CodeMemberProperty EmitPropertyDeclaration(CodeTypeReference propertyReturnType)
43         {
44             MemberAttributes scope = AccessibilityFromGettersAndSetters(Item);
45             CodeMemberProperty memberProperty = EmitPropertyDeclaration(scope, propertyReturnType, IsVirtualProperty, HidesBaseClassProperty);
46
47             memberProperty.HasSet = true;
48             memberProperty.HasGet = true;
49
50             return memberProperty;
51         }
52
53         /// <summary>
54         /// Main method for Emitting property code.
55         /// </summary>
56         /// <param name="typeDecl">The CodeDom representation of the type that the property is being added to.</param>
57         protected override void EmitProperty(CodeTypeDeclaration typeDecl)
58         {
59             CodeTypeReference typeRef = PropertyType;
60
61             // raise the PropertyGenerated event
62             //
63             PropertyGeneratedEventArgs eventArgs = new PropertyGeneratedEventArgs(Item, FieldName, typeRef);
64             this.Generator.RaisePropertyGeneratedEvent(eventArgs);
65
66             // the event subscriber cannot change the return type of the property
67             //
68             DisallowReturnTypeChange(typeRef, eventArgs.ReturnType);
69
70             CodeMemberProperty memberProperty = EmitPropertyDeclaration(eventArgs.ReturnType);
71             if (memberProperty == null)
72             {
73                 return;
74             }
75
76             EmitCustomAttributes(memberProperty, eventArgs.AdditionalAttributes);
77
78             EmitPropertyGetter(memberProperty, eventArgs.AdditionalGetStatements);
79             EmitPropertySetter(memberProperty, eventArgs.AdditionalSetStatements);
80             typeDecl.Members.Add(memberProperty);
81
82             EmitField(typeDecl, eventArgs.ReturnType);
83
84             EmitPropertyOnChangePartialMethods(typeDecl, eventArgs.ReturnType);
85         }
86
87         /// <summary>
88         /// Emit these methods as "abstract" and fix them up later to be "partial".
89         /// CodeDOM does not support partial methods
90         /// </summary>
91         /// <param name="typeDecl"></param>
92         private void EmitPropertyOnChangePartialMethods(CodeTypeDeclaration typeDecl, CodeTypeReference returnType)
93         {
94             CodeMemberMethod onChangingDomMethod = new CodeMemberMethod();
95             Generator.AttributeEmitter.EmitGeneratedCodeAttribute(onChangingDomMethod);
96             onChangingDomMethod.Name = OnChangingPartialMethodName(PropertyName);
97             onChangingDomMethod.ReturnType = new CodeTypeReference(typeof(void));
98             onChangingDomMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
99             onChangingDomMethod.Parameters.Add(new CodeParameterDeclarationExpression(returnType, "value"));
100             typeDecl.Members.Add(onChangingDomMethod);
101
102             CodeMemberMethod onChangedDomMethod = new CodeMemberMethod();
103             Generator.AttributeEmitter.EmitGeneratedCodeAttribute(onChangedDomMethod);
104             onChangedDomMethod.Name = OnChangedPartialMethodName(PropertyName);
105             onChangedDomMethod.ReturnType = new CodeTypeReference(typeof(void));
106             onChangedDomMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
107             typeDecl.Members.Add(onChangedDomMethod);
108
109             Generator.FixUps.Add(new FixUp(PropertyClassName + "." + OnChangingPartialMethodName(PropertyName), FixUpType.MarkAbstractMethodAsPartial));
110             Generator.FixUps.Add(new FixUp(PropertyClassName + "." + OnChangedPartialMethodName(PropertyName), FixUpType.MarkAbstractMethodAsPartial));
111         }
112
113         private void EmitField(CodeTypeDeclaration typeDecl, CodeTypeReference fieldType)
114         {
115             CodeMemberField memberField = new CodeMemberField(fieldType, FieldName);
116             Generator.AttributeEmitter.EmitGeneratedCodeAttribute(memberField);
117
118             memberField.Attributes = MemberAttributes.Private;
119             if (HasDefault(Item))
120             {
121                 memberField.InitExpression = GetDefaultValueExpression(Item);
122             }
123
124             typeDecl.Members.Add(memberField);
125
126             if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
127             {
128                 CodeMemberField complexInitField = new CodeMemberField(TypeReference.ForType(typeof(bool)), ComplexPropertyInitializedFieldName);
129                 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(complexInitField);
130                 complexInitField.Attributes = MemberAttributes.Private;
131                 typeDecl.Members.Add(complexInitField);
132             }
133         }
134
135         /// <summary>
136         /// Get a reference to the base class DataObject
137         /// </summary>
138         public static CodeTypeReferenceExpression CreateEdmStructuralObjectRef(TypeReference typeReference)
139         {
140             return new CodeTypeReferenceExpression(typeReference.ForType(typeof(System.Data.Objects.DataClasses.StructuralObject)));
141         }
142
143         #endregion
144
145         #region Public Properties
146
147         public CodeTypeReference PropertyType
148         {
149             get
150             {
151                 CodeTypeReference typeRef = GetType(Item, false);
152                 return typeRef;
153             }
154         }
155
156         public new EdmProperty Item
157         {
158             get
159             {
160                 return base.Item as EdmProperty;
161             }
162         }
163
164         #endregion
165
166         #region Internal Methods
167
168         /// <summary>
169         /// Name of the associated Entity property for Ref(T) properties
170         /// </summary>
171         public string EntityPropertyName
172         {
173             get
174             {
175                 return Item.Name;
176             }
177         }
178
179         #endregion
180
181         #region Private Methods
182
183         /// <summary>
184         /// 
185         /// </summary>
186         /// <param name="memberProperty"></param>
187         /// <param name="additionalAttributes">Additional attributes to emit</param>
188         private void EmitCustomAttributes(CodeMemberProperty memberProperty,
189                                           List<CodeAttributeDeclaration> additionalAttributes)
190         {
191             Generator.AttributeEmitter.EmitPropertyAttributes(this, memberProperty, additionalAttributes);
192         }
193
194         private void EmitPropertyGetter(CodeMemberProperty memberProperty, List<CodeStatement> additionalGetStatements)
195         {
196             CodeStatementCollection statements = memberProperty.GetStatements;
197
198             // we need to insert user-specified code before other/existing code, including
199             // the return statement
200             if (additionalGetStatements != null && additionalGetStatements.Count > 0)
201             {
202                 try
203                 {
204                     CodeStatementCollection getStatements = new CodeStatementCollection();
205                     getStatements.AddRange(additionalGetStatements.ToArray());
206                     if (statements != null && statements.Count > 0)
207                     {
208                         getStatements.AddRange(statements);
209                     }
210                     statements.Clear();
211                     statements.AddRange(getStatements);
212                 }
213                 catch (ArgumentNullException e)
214                 {
215                     Generator.AddError(Strings.InvalidGetStatementSuppliedForProperty(Item.Name),
216                                        ModelBuilderErrorCode.InvalidGetStatementSuppliedForProperty,
217                                        EdmSchemaErrorSeverity.Error,
218                                        e);
219                 }
220             }
221
222             MemberAttributes access = memberProperty.Attributes & MemberAttributes.AccessMask;
223
224             AddGetterSetterFixUp(Generator.FixUps, PropertyFQName, GetGetterAccessibility(Item), access, true);
225
226             EmitPropertyGetterBody(statements);
227         }
228
229         private void EmitPropertyGetterBody(CodeStatementCollection statements)
230         {
231             // If the SchemaElement.Type isn't a ComplexType it better be PrimitiveType.
232             if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
233             {
234                 //Since Complex Collections are not supported by
235                 //the stack, we don't need to do anything special 
236                 //like doing an Attach or Detatch like the way we do for complex types.
237                 if (GetCollectionKind(Item.TypeUsage) == CollectionKind.None)
238                 {
239                     // _field = GetValidValue( _field, FieldPropertyInfo, _fieldInitialized);
240                     statements.Add(
241                         new CodeAssignStatement(FieldRef,
242                             new CodeMethodInvokeExpression(
243                                     ThisRef,
244                                     Utils.GetValidValueMethodName,
245                                     new CodeDirectionExpression(FieldDirection.In, FieldRef),
246                                     new CodePrimitiveExpression(PropertyName),
247                                     new CodePrimitiveExpression(Item.Nullable),
248                                     ComplexPropertyInitializedFieldRef)));
249
250                     // this._complexPropertyInitialized = true;
251                     statements.Add(
252                         new CodeAssignStatement(
253                             ComplexPropertyInitializedFieldRef,
254                             new CodePrimitiveExpression(true)));
255                 }
256                 // return _field;
257                 statements.Add(new CodeMethodReturnStatement(FieldRef));
258             }
259             else
260             {
261                 PrimitiveType primitiveType = Item.TypeUsage.EdmType as PrimitiveType;
262                 if (primitiveType != null && primitiveType.ClrEquivalentType == typeof(byte[]))
263                 {
264                     // return GetValidValue(_field);
265                     statements.Add(
266                         new CodeMethodReturnStatement(
267                             new CodeMethodInvokeExpression(
268                                 CreateEdmStructuralObjectRef(TypeReference),
269                                 Utils.GetValidValueMethodName,
270                                 this.FieldRef)));
271                 }
272                 else
273                 {
274                     // for everything else just return the field.
275                     statements.Add(new CodeMethodReturnStatement(FieldRef));
276                 }
277             }
278         }
279
280
281         /// <summary>
282         /// 
283         /// </summary>
284         /// <param name="memberProperty"></param>
285         /// <param name="additionalSetStatements">Additional statements to emit</param>
286         private void EmitPropertySetter(CodeMemberProperty memberProperty, List<CodeStatement> additionalSetStatements)
287         {
288             CodeStatementCollection statements = memberProperty.SetStatements;
289
290             MemberAttributes access = memberProperty.Attributes & MemberAttributes.AccessMask;
291
292             AddGetterSetterFixUp(Generator.FixUps, PropertyFQName, GetSetterAccessibility(Item), access, false);
293
294             EmitPropertySetterBody(statements, additionalSetStatements);
295         }
296
297         /// <summary>
298         /// This is a control function to delegate the creation of the 
299         /// setter statments to the correct code path
300         /// </summary>
301         /// <param name="statements">The collection that the setter statements should be added to.</param>
302         /// <param name="additionalSetStatements">Additional statements to emit</param>
303         private void EmitPropertySetterBody(CodeStatementCollection statements, List<CodeStatement> additionalSetStatements)
304         {
305             // Invoke the partial method "On[PropertyName]Changing();
306             statements.Add(
307                 new CodeMethodInvokeExpression(
308                     ThisRef,
309                     OnChangingPartialMethodName(PropertyName), new CodePropertySetValueReferenceExpression()));
310
311             // ReportPropertyChanging( _piFieldName );
312             statements.Add(
313                 new CodeMethodInvokeExpression(
314                     ThisRef,
315                     Utils.ReportPropertyChangingMethodName,
316                     new CodePrimitiveExpression(PropertyName)));
317
318             // insert additional statements following the PropertyChanging event
319             if (additionalSetStatements != null && additionalSetStatements.Count > 0)
320             {
321                 try
322                 {
323                     statements.AddRange(additionalSetStatements.ToArray());
324                 }
325                 catch (ArgumentNullException e)
326                 {
327                     Generator.AddError(Strings.InvalidSetStatementSuppliedForProperty(Item.Name),
328                                        ModelBuilderErrorCode.InvalidSetStatementSuppliedForProperty,
329                                        EdmSchemaErrorSeverity.Error,
330                                        e);
331                 }
332             }
333
334             if (MetadataUtil.IsPrimitiveType(Item.TypeUsage.EdmType))
335             {
336                 EmitScalarTypePropertySetStatements(statements, CollectionKind.None);
337             }
338             else if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
339             {
340                 // ComplexTypes have a completely different set pattern:
341                 EmitComplexTypePropertySetStatements(statements, CollectionKind.None);
342             }
343             else if (MetadataUtil.IsCollectionType(Item.TypeUsage.EdmType))
344             {
345                 if (MetadataUtil.IsComplexType(((CollectionType)Item.TypeUsage.EdmType).TypeUsage.EdmType))
346                 {
347                     EmitComplexTypePropertySetStatements(statements, GetCollectionKind(Item.TypeUsage));
348                 }
349                 else
350                 {
351                     Debug.Assert(MetadataUtil.IsPrimitiveType(((CollectionType)Item.TypeUsage.EdmType).TypeUsage.EdmType),
352                         "Collections should be of primitive types or complex types");
353                     EmitScalarTypePropertySetStatements(statements, GetCollectionKind(Item.TypeUsage));
354                 }
355
356             }
357             else if (MetadataUtil.IsEnumerationType(Item.TypeUsage.EdmType))
358             {
359                 // this.fieldName = value;
360                 statements.Add(
361                     new CodeAssignStatement(
362                             FieldRef,
363                             new CodePropertySetValueReferenceExpression()));
364
365             }
366
367             // ReportPropertyChanged( _piFieldName );
368             statements.Add(
369                 new CodeMethodInvokeExpression(
370                     ThisRef,
371                     Utils.ReportPropertyChangedMethodName,
372                     new CodePrimitiveExpression(PropertyName)));
373
374             // Invoke the partial method "On[PropertyName]Changed();
375             statements.Add(
376                 new CodeMethodInvokeExpression(
377                     ThisRef,
378                     OnChangedPartialMethodName(PropertyName)));
379         }
380
381         /// <summary>
382         /// Do the fixups to allow get and set statements in properties
383         /// to have different accessibility than the property itself.
384         /// </summary>
385         /// <param name="accessibility">The accessibility for the getter or setter</param>
386         /// <param name="propertyAccessibility">The property's accessibility</param>
387         /// <param name="isGetter">True if this is a getter, false if a setter</param>
388         internal static void AddGetterSetterFixUp(FixUpCollection fixups, string propertyFqName, MemberAttributes accessibility, MemberAttributes propertyAccessibility, bool isGetter)
389         {
390             Debug.Assert(GetAccessibilityRank(accessibility) >= 0, "bad accessibility");
391
392             // Private
393             if (accessibility == MemberAttributes.Private && propertyAccessibility != MemberAttributes.Private)
394             {
395                 if (isGetter)
396                 {
397                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsPrivate));
398                 }
399                 else
400                 {
401                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsPrivate));
402                 }
403             }
404
405             // Internal
406             if (accessibility == MemberAttributes.Assembly && propertyAccessibility != MemberAttributes.Assembly)
407             {
408                 if (isGetter)
409                 {
410                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsInternal));
411                 }
412                 else
413                 {
414                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsInternal));
415                 }
416             }
417
418             // Public
419             if (accessibility == MemberAttributes.Public && propertyAccessibility != MemberAttributes.Public)
420             {
421                 if (isGetter)
422                 {
423                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsPublic));
424                 }
425                 else
426                 {
427                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsPublic));
428                 }
429             }
430
431             // Protected
432             if (accessibility == MemberAttributes.Family && propertyAccessibility != MemberAttributes.Family)
433             {
434                 if (isGetter)
435                 {
436                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsProtected));
437                 }
438                 else
439                 {
440                     fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsProtected));
441                 }
442             }
443
444         }
445
446         /// <summary>
447         /// Emit the set statements for a property that is a scalar type
448         /// </summary>
449         /// <param name="statements">The statement collection to add the set statements to.</param>
450         private void EmitScalarTypePropertySetStatements(CodeStatementCollection statements,
451             CollectionKind collectionKind)
452         {
453             Debug.Assert(statements != null, "statments can't be null");
454             Debug.Assert(((MetadataUtil.IsPrimitiveType(Item.TypeUsage.EdmType)) || (MetadataUtil.IsCollectionType(Item.TypeUsage.EdmType)))
455                 , "Must be a primitive type or collection type property");
456
457             CodePropertySetValueReferenceExpression valueRef = new CodePropertySetValueReferenceExpression();
458             //Since collections are not supported by
459             //the stack, we don't need to do anything special 
460             //like doing an Attach or Detatch like the way we do for complex types.
461             if (collectionKind == CollectionKind.None)
462             {
463
464                 PrimitiveType primitiveType = (PrimitiveType)Item.TypeUsage.EdmType;
465
466                 // basic pattern
467                 // this.fieldName = SetValidValue( value );
468                 //
469                 List<CodeExpression> parameters = new List<CodeExpression>();
470                 parameters.Add(valueRef);
471
472
473                 // pattern for non Nullable<T> types (string, byte[])
474                 //
475                 // this.fieldName = SetValidVaue( value, nullability );
476
477                 if (primitiveType.ClrEquivalentType.IsClass)
478                 {
479                     // ref types have an extra boolean parameter to tell if the property is allowed to 
480                     // be null or not
481                     parameters.Add(new CodePrimitiveExpression(Item.Nullable));
482                 }
483
484                 // now create and add the built statement
485                 statements.Add(
486                     new CodeAssignStatement(
487                             FieldRef,
488                             new CodeMethodInvokeExpression(
489                                 CreateEdmStructuralObjectRef(TypeReference),
490                                 Utils.SetValidValueMethodName,
491                                 parameters.ToArray())));
492             }
493             else
494             {
495                 // this.fieldName = value;
496                 statements.Add(
497                     new CodeAssignStatement(
498                         FieldRef, valueRef));
499
500             }
501         }
502
503         private CodeExpression GetEnumValue<T>(T value)
504         {
505             Type type = typeof(T);
506             return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(TypeReference.ForType(type)), Enum.GetName(type, value));
507         }
508
509         /// <summary>
510         /// Emit the property set statments to properly set a ComplexType.
511         /// </summary>
512         /// <param name="statements">The collection of statements that the set statements should be added to.</param>
513         private void EmitComplexTypePropertySetStatements(CodeStatementCollection statements, CollectionKind collectionKind)
514         {
515             CodePropertySetValueReferenceExpression valueRef = new CodePropertySetValueReferenceExpression();
516             //Since collections are not supported by
517             //the stack, we don't need to do anything special 
518             //like doing an Attach or Detatch like the way we do for complex types.
519             if (collectionKind == CollectionKind.None)
520             {
521
522                 // this.fieldName = SetValidValue( this.fieldName, value, _pifieldName);
523                 statements.Add(
524                     new CodeAssignStatement(
525                         FieldRef,
526                         new CodeMethodInvokeExpression(
527                                 ThisRef,
528                                 Utils.SetValidValueMethodName,
529                                 FieldRef,
530                                 valueRef,
531                                 new CodePrimitiveExpression(PropertyName))));
532
533                 // this._complexPropertyInitialized = true;
534                 statements.Add(
535                     new CodeAssignStatement(
536                         ComplexPropertyInitializedFieldRef,
537                         new CodePrimitiveExpression(true)));
538             }
539             else
540             {
541                 // this.fieldName = value;
542                 statements.Add(
543                     new CodeAssignStatement(
544                         FieldRef, valueRef));
545
546             }
547
548         }
549
550         /// <summary>
551         /// See if a property names will hide a base class member name
552         /// </summary>
553         private bool HidesBaseClassProperty
554         {
555             get
556             {
557                 StructuralType parentBaseClass = Item.DeclaringType.BaseType as StructuralType;
558                 if (parentBaseClass != null && parentBaseClass.Members.Contains(PropertyName))
559                     return true;
560
561                 return false;
562             }
563         }
564
565         private CodeTypeReference GetType(EdmProperty property, bool getElementType)
566         {
567             PropertyTypeReferences types = default(PropertyTypeReferences);
568             EdmType propertyType = property.TypeUsage.EdmType;
569
570             // Initialize types
571             if (MetadataUtil.IsPrimitiveType(propertyType))
572             {
573                 types = new PropertyTypeReferences(TypeReference, (PrimitiveType)propertyType);
574             }
575             else if (MetadataUtil.IsComplexType(propertyType))
576             {
577                 types = new PropertyTypeReferences(TypeReference, (ComplexType)propertyType, Generator);
578             }
579             else if (Helper.IsCollectionType(propertyType))
580             {
581                 TypeUsage typeUsage = ((CollectionType)propertyType).TypeUsage;
582                 if (MetadataUtil.IsPrimitiveType(typeUsage.EdmType))
583                 {
584                     types = new PropertyTypeReferences(TypeReference, (PrimitiveType)typeUsage.EdmType, GetCollectionKind(property.TypeUsage));
585                 }
586                 else
587                 {
588                     Debug.Assert(MetadataUtil.IsComplexType(typeUsage.EdmType));
589                     types = new PropertyTypeReferences(TypeReference, (ComplexType)typeUsage.EdmType, GetCollectionKind(property.TypeUsage), Generator);
590                 }
591             }
592             else
593             {
594                 // shouldn't be able to get here....
595                 Debug.Fail("Unexpected Property.Type type: " + propertyType.GetType());
596             }
597
598             // Set types, or retrieve existing types if they have been set in the interim
599             // Don't cache Collection types since CollectionKind is really a facet and
600             //it is not part of the key we are using for the dictionary used to cache.
601             if (!Helper.IsCollectionType(propertyType))
602             {
603                 Debug.Assert(types.NonNullable != null && types.Nullable != null, "did you forget to set the types variable?");
604             }
605
606             if (property.Nullable)
607             {
608                 return types.Nullable;
609             }
610             else
611             {
612                 return types.NonNullable;
613             }
614         }
615
616
617         private static CollectionKind GetCollectionKind(TypeUsage usage)
618         {
619             Facet collectionFacet;
620             if (usage.Facets.TryGetValue(EdmConstants.CollectionKind, false, out collectionFacet))
621             {
622                 return (CollectionKind)collectionFacet.Value;
623             }
624
625             return CollectionKind.None;
626         }
627
628         private string OnChangingPartialMethodName(string propertyName) { return "On" + propertyName + "Changing"; }
629         private string OnChangedPartialMethodName(string propertyName) { return "On" + propertyName + "Changed"; }
630
631         #endregion
632
633         #region Private Properties
634
635         private CodeFieldReferenceExpression FieldRef
636         {
637             get
638             {
639                 if (_fieldRef == null)
640                     _fieldRef = new CodeFieldReferenceExpression(ThisRef, FieldName);
641
642                 return _fieldRef;
643             }
644         }
645
646         private CodeFieldReferenceExpression ComplexPropertyInitializedFieldRef
647         {
648             get
649             {
650                 if (_complexPropertyInitializedFieldRef == null)
651                     _complexPropertyInitializedFieldRef = new CodeFieldReferenceExpression(ThisRef, ComplexPropertyInitializedFieldName);
652
653                 return _complexPropertyInitializedFieldRef;
654             }
655         }
656
657         private string FieldName
658         {
659             get
660             {
661                 return Utils.FieldNameFromPropName(PropertyName);
662             }
663         }
664
665         private string ComplexPropertyInitializedFieldName
666         {
667             get
668             {
669                 return Utils.ComplexPropertyInitializedNameFromPropName(PropertyName);
670             }
671         }
672
673         internal bool IsKeyProperty
674         {
675             get
676             {
677                 EntityType entity = Item.DeclaringType as EntityType;
678                 if (entity != null)
679                 {
680                     return entity.KeyMembers.Contains(Item.Name);
681                 }
682                 return false;
683             }
684         }
685
686         internal static bool HasDefault(EdmProperty property)
687         {
688             return property.DefaultValue != null;
689         }
690
691         private CodeExpression GetDefaultValueExpression(EdmProperty property)
692         {
693             PrimitiveTypeKind type;
694             object value = property.DefaultValue;
695             if (value != null
696                  && Utils.TryGetPrimitiveTypeKind(property.TypeUsage.EdmType, out type))
697             {
698                 if (!property.Nullable && value.Equals(TypeSystem.GetDefaultValue(value.GetType())))
699                 {
700                     return null;
701                 }
702
703                 switch (type)
704                 {
705                     case PrimitiveTypeKind.Boolean:
706                     case PrimitiveTypeKind.Byte:
707                     case PrimitiveTypeKind.Int16:
708                     case PrimitiveTypeKind.Int32:
709                     case PrimitiveTypeKind.Int64:
710                     case PrimitiveTypeKind.Decimal:
711                     case PrimitiveTypeKind.Single:
712                     case PrimitiveTypeKind.Double:
713                     case PrimitiveTypeKind.String:
714                         {
715                             return new CodePrimitiveExpression(value);
716                         }
717                     case PrimitiveTypeKind.Guid:
718                         {
719                             return GetCodeExpressionFromGuid(value);
720                         }
721                     case PrimitiveTypeKind.DateTime:
722                         {
723                             return GetCodeExpressionFromDateTimeDefaultValue(value, property);
724                         }
725                     case PrimitiveTypeKind.DateTimeOffset:
726                         {
727                             return GetCodeExpressionFromDateTimeOffsetDefaultValue(value, property);
728                         }
729                     case PrimitiveTypeKind.Time:
730                         {
731                             return GetCodeExpressionFromTimeSpanDefaultValue(value, property);
732                         }
733                     case PrimitiveTypeKind.Binary:
734                         {
735                             return GetCodeExpressionFromBinary(value);
736                         }
737                     default:
738                         Debug.Fail("Unsupported property type:" + type.ToString());
739                         break;
740                 }
741                 return null;
742             }
743             return null;
744         }
745
746         private CodeExpression GetCodeExpressionFromBinary(object value)
747         {
748             byte[] data = (byte[])value;
749             CodeExpression[] bytes = new CodeExpression[data.Length];
750
751             for (int iByte = 0; iByte < data.Length; ++iByte)
752             {
753                 bytes[iByte] = new CodePrimitiveExpression(data[iByte]);
754             }
755
756             return new CodeArrayCreateExpression(TypeReference.ByteArray, bytes);
757         }
758
759         private CodeExpression GetCodeExpressionFromGuid(object value)
760         {
761             Guid guid = (Guid)value;
762             return new CodeObjectCreateExpression(TypeReference.Guid,
763                 new CodePrimitiveExpression(guid.ToString("D", CultureInfo.InvariantCulture)));
764         }
765
766         private CodeExpression GetCodeExpressionFromDateTimeDefaultValue(object value, EdmProperty property)
767         {
768             DateTime utc = (DateTime)value;
769             DateTime dateTime = DateTime.SpecifyKind(utc, DateTimeKind.Unspecified);
770
771             return new CodeObjectCreateExpression(TypeReference.DateTime, new CodePrimitiveExpression(dateTime.Ticks), GetEnumValue(DateTimeKind.Unspecified));
772         }
773
774         private CodeExpression GetCodeExpressionFromDateTimeOffsetDefaultValue(object value, EdmProperty property)
775         {
776             DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
777
778             return new CodeObjectCreateExpression(TypeReference.DateTimeOffset, new CodePrimitiveExpression(dateTimeOffset.Ticks),
779                 new CodeObjectCreateExpression(TypeReference.TimeSpan, new CodePrimitiveExpression(dateTimeOffset.Offset.Ticks)));
780         }
781
782         private CodeExpression GetCodeExpressionFromTimeSpanDefaultValue(object value, EdmProperty property)
783         {
784             TimeSpan timeSpan = (TimeSpan)value;
785             return new CodeObjectCreateExpression(TypeReference.TimeSpan, new CodePrimitiveExpression(timeSpan.Ticks));
786         }
787
788         public bool IsVirtualProperty
789         {
790             get
791             {
792                 return false;
793             }
794         }
795
796         private struct PropertyTypeReferences
797         {
798             CodeTypeReference _nonNullable;
799             CodeTypeReference _nullable;
800             public PropertyTypeReferences(TypeReference typeReference, PrimitiveType primitiveType)
801                 : this(typeReference, primitiveType, CollectionKind.None)
802             {
803             }
804
805             public PropertyTypeReferences(TypeReference typeReference, PrimitiveType primitiveType, CollectionKind collectionKind)
806             {
807                 Type type = primitiveType.ClrEquivalentType;
808                 if (collectionKind == CollectionKind.None)
809                 {
810                     _nonNullable = typeReference.ForType(type);
811                     if (type.IsValueType)
812                     {
813                         _nullable = typeReference.NullableForType(type);
814                     }
815                     else
816                     {
817                         _nullable = typeReference.ForType(type);
818                     }
819                 }
820                 else
821                 {
822                     CodeTypeReference primitiveTypeRef = typeReference.ForType(type);
823                     CodeTypeReference collectionType = GetCollectionTypeReference(typeReference, primitiveTypeRef, collectionKind);
824                     _nonNullable = collectionType;
825                     _nullable = collectionType;
826                 }
827             }
828
829             public PropertyTypeReferences(TypeReference typeReference, ComplexType complexType, CollectionKind collectionKind, ClientApiGenerator generator)
830             {
831                 CodeTypeReference baseType = generator.GetLeastPossibleQualifiedTypeReference(complexType);
832                 baseType = GetCollectionTypeReference(typeReference, baseType, collectionKind);
833                 _nonNullable = baseType;
834                 _nullable = baseType;
835             }
836
837             private static CodeTypeReference GetCollectionTypeReference(TypeReference typeReference, CodeTypeReference baseType, CollectionKind collectionKind)
838             {
839                 if (collectionKind == CollectionKind.Bag)
840                 {
841                     baseType = GetCollectionTypeReferenceForBagSemantics(typeReference, baseType);
842                 }
843                 else if (collectionKind == CollectionKind.List)
844                 {
845                     baseType = GetCollectionTypeReferenceForListSemantics(typeReference, baseType);
846                 }
847                 else
848                 {
849                     Debug.Assert(collectionKind == CollectionKind.None, "Was another CollectionKind value added");
850                     // nothing more to do for .None
851                 }
852                 return baseType;
853             }
854
855             public PropertyTypeReferences(TypeReference typeReference, ComplexType complexType, ClientApiGenerator generator)
856                 : this(typeReference, complexType, CollectionKind.None, generator)
857             {
858             }
859
860             private static CodeTypeReference GetCollectionTypeReferenceForBagSemantics(TypeReference typeReference, CodeTypeReference baseType)
861             {
862                 CodeTypeReference typeRef = typeReference.ForType(typeof(System.Collections.Generic.ICollection<>), baseType);
863                 return typeRef;
864             }
865
866             private static CodeTypeReference GetCollectionTypeReferenceForListSemantics(TypeReference typeReference, CodeTypeReference baseType)
867             {
868                 CodeTypeReference typeRef = typeReference.ForType(typeof(System.Collections.Generic.IList<>), baseType);
869                 return typeRef;
870             }
871
872             public CodeTypeReference NonNullable
873             {
874                 get { return _nonNullable; }
875             }
876             public CodeTypeReference Nullable
877             {
878                 get { return _nullable; }
879             }
880         }
881         #endregion
882
883         // properties from ClassPropertyEmitter
884
885         public string PropertyFQName
886         {
887             get
888             {
889                 return Item.DeclaringType.FullName + "." + Item.Name;
890             }
891         }
892
893         public string PropertyName
894         {
895             get
896             {
897                 return EntityPropertyName;
898             }
899         }
900
901         private string PropertyClassName
902         {
903             get
904             {
905                 return Item.DeclaringType.Name;
906             }
907         }
908
909         private CodeMemberProperty EmitPropertyDeclaration(MemberAttributes scope, CodeTypeReference propertyType, bool isVirtual,
910             bool hidesBaseProperty)
911         {
912             Debug.Assert(GetAccessibilityRank(scope) >= 0, "scope should only be an accessibility attribute");
913
914             CodeMemberProperty memberProperty = new CodeMemberProperty();
915             memberProperty.Name = PropertyName;
916             CommentEmitter.EmitSummaryComments(Item, memberProperty.Comments);
917
918             memberProperty.Attributes = scope;
919
920             if (!isVirtual)
921             {
922                 memberProperty.Attributes |= MemberAttributes.Final;
923             }
924
925             if (hidesBaseProperty || AncestorClassDefinesName(memberProperty.Name))
926             {
927                 memberProperty.Attributes |= MemberAttributes.New;
928             }
929
930             memberProperty.Type = propertyType;
931
932             return memberProperty;
933         }
934
935         private void DisallowReturnTypeChange(CodeTypeReference baseType, CodeTypeReference newType)
936         {
937             if (Helper.IsCollectionType(Item.TypeUsage.EdmType) && GetCollectionKind(Item.TypeUsage) != CollectionKind.None)
938             {
939                 if (newType == null)
940                 {
941                     throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnTypeToNull(Item.Name, Item.DeclaringType.Name));
942                 }
943
944                 // you can change the return type of collection properties
945                 // we don't even need to check
946                 return;
947             }
948
949             if (!(baseType == null && newType == null) &&
950                 (
951                     (baseType != null && !baseType.Equals(newType)) ||
952                     (newType != null && !newType.Equals(baseType))
953                 )
954                )
955             {
956                 throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnType(Item.Name, Item.DeclaringType.Name));
957             }
958         }
959     }
960 }