File: System\Data\EntityModel\Emitters\PropertyEmitter.cs
Project: ndp\fx\src\DataEntityDesign\Design\System.Data.Entity.Design.csproj (System.Data.Entity.Design)
//---------------------------------------------------------------------
// <copyright file="PropertyEmitter.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.CodeDom;
using System.Collections.Generic;
using System.Data.Entity.Design;
using System.Data.Entity.Design.Common;
using System.Data.Entity.Design.SsdlGenerator;
using System.Data.Metadata.Edm;
using System.Data.Objects.ELinq;
using System.Diagnostics;
using System.Globalization;
 
namespace System.Data.EntityModel.Emitters
{
    internal sealed class PropertyEmitter : PropertyEmitterBase
    {
        private CodeFieldReferenceExpression _fieldRef = null;
        private CodeFieldReferenceExpression _complexPropertyInitializedFieldRef = null;
 
        // statics
        private const string NestedStoreObjectCollection = "InlineObjectCollection";
        private const string DetachFromParentMethodName = "DetachFromParent";
 
        #region Public Methods
 
        public PropertyEmitter(ClientApiGenerator generator, EdmProperty property, bool declaringTypeUsesStandardBaseType)
            : base(generator, property, declaringTypeUsesStandardBaseType)
        {
        }
 
        /// <summary>
        /// Emit the declaration of the property for the class.
        /// </summary>
        /// <returns>The Property declaration pieces of the CodeDom.</returns>
        public CodeMemberProperty EmitPropertyDeclaration(CodeTypeReference propertyReturnType)
        {
            MemberAttributes scope = AccessibilityFromGettersAndSetters(Item);
            CodeMemberProperty memberProperty = EmitPropertyDeclaration(scope, propertyReturnType, IsVirtualProperty, HidesBaseClassProperty);
 
            memberProperty.HasSet = true;
            memberProperty.HasGet = true;
 
            return memberProperty;
        }
 
        /// <summary>
        /// Main method for Emitting property code.
        /// </summary>
        /// <param name="typeDecl">The CodeDom representation of the type that the property is being added to.</param>
        protected override void EmitProperty(CodeTypeDeclaration typeDecl)
        {
            CodeTypeReference typeRef = PropertyType;
 
            // raise the PropertyGenerated event
            //
            PropertyGeneratedEventArgs eventArgs = new PropertyGeneratedEventArgs(Item, FieldName, typeRef);
            this.Generator.RaisePropertyGeneratedEvent(eventArgs);
 
            // the event subscriber cannot change the return type of the property
            //
            DisallowReturnTypeChange(typeRef, eventArgs.ReturnType);
 
            CodeMemberProperty memberProperty = EmitPropertyDeclaration(eventArgs.ReturnType);
            if (memberProperty == null)
            {
                return;
            }
 
            EmitCustomAttributes(memberProperty, eventArgs.AdditionalAttributes);
 
            EmitPropertyGetter(memberProperty, eventArgs.AdditionalGetStatements);
            EmitPropertySetter(memberProperty, eventArgs.AdditionalSetStatements);
            typeDecl.Members.Add(memberProperty);
 
            EmitField(typeDecl, eventArgs.ReturnType);
 
            EmitPropertyOnChangePartialMethods(typeDecl, eventArgs.ReturnType);
        }
 
        /// <summary>
        /// Emit these methods as "abstract" and fix them up later to be "partial".
        /// CodeDOM does not support partial methods
        /// </summary>
        /// <param name="typeDecl"></param>
        private void EmitPropertyOnChangePartialMethods(CodeTypeDeclaration typeDecl, CodeTypeReference returnType)
        {
            CodeMemberMethod onChangingDomMethod = new CodeMemberMethod();
            Generator.AttributeEmitter.EmitGeneratedCodeAttribute(onChangingDomMethod);
            onChangingDomMethod.Name = OnChangingPartialMethodName(PropertyName);
            onChangingDomMethod.ReturnType = new CodeTypeReference(typeof(void));
            onChangingDomMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
            onChangingDomMethod.Parameters.Add(new CodeParameterDeclarationExpression(returnType, "value"));
            typeDecl.Members.Add(onChangingDomMethod);
 
            CodeMemberMethod onChangedDomMethod = new CodeMemberMethod();
            Generator.AttributeEmitter.EmitGeneratedCodeAttribute(onChangedDomMethod);
            onChangedDomMethod.Name = OnChangedPartialMethodName(PropertyName);
            onChangedDomMethod.ReturnType = new CodeTypeReference(typeof(void));
            onChangedDomMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
            typeDecl.Members.Add(onChangedDomMethod);
 
            Generator.FixUps.Add(new FixUp(PropertyClassName + "." + OnChangingPartialMethodName(PropertyName), FixUpType.MarkAbstractMethodAsPartial));
            Generator.FixUps.Add(new FixUp(PropertyClassName + "." + OnChangedPartialMethodName(PropertyName), FixUpType.MarkAbstractMethodAsPartial));
        }
 
        private void EmitField(CodeTypeDeclaration typeDecl, CodeTypeReference fieldType)
        {
            CodeMemberField memberField = new CodeMemberField(fieldType, FieldName);
            Generator.AttributeEmitter.EmitGeneratedCodeAttribute(memberField);
 
            memberField.Attributes = MemberAttributes.Private;
            if (HasDefault(Item))
            {
                memberField.InitExpression = GetDefaultValueExpression(Item);
            }
 
            typeDecl.Members.Add(memberField);
 
            if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
            {
                CodeMemberField complexInitField = new CodeMemberField(TypeReference.ForType(typeof(bool)), ComplexPropertyInitializedFieldName);
                Generator.AttributeEmitter.EmitGeneratedCodeAttribute(complexInitField);
                complexInitField.Attributes = MemberAttributes.Private;
                typeDecl.Members.Add(complexInitField);
            }
        }
 
        /// <summary>
        /// Get a reference to the base class DataObject
        /// </summary>
        public static CodeTypeReferenceExpression CreateEdmStructuralObjectRef(TypeReference typeReference)
        {
            return new CodeTypeReferenceExpression(typeReference.ForType(typeof(System.Data.Objects.DataClasses.StructuralObject)));
        }
 
        #endregion
 
        #region Public Properties
 
        public CodeTypeReference PropertyType
        {
            get
            {
                CodeTypeReference typeRef = GetType(Item, false);
                return typeRef;
            }
        }
 
        public new EdmProperty Item
        {
            get
            {
                return base.Item as EdmProperty;
            }
        }
 
        #endregion
 
        #region Internal Methods
 
        /// <summary>
        /// Name of the associated Entity property for Ref(T) properties
        /// </summary>
        public string EntityPropertyName
        {
            get
            {
                return Item.Name;
            }
        }
 
        #endregion
 
        #region Private Methods
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberProperty"></param>
        /// <param name="additionalAttributes">Additional attributes to emit</param>
        private void EmitCustomAttributes(CodeMemberProperty memberProperty,
                                          List<CodeAttributeDeclaration> additionalAttributes)
        {
            Generator.AttributeEmitter.EmitPropertyAttributes(this, memberProperty, additionalAttributes);
        }
 
        private void EmitPropertyGetter(CodeMemberProperty memberProperty, List<CodeStatement> additionalGetStatements)
        {
            CodeStatementCollection statements = memberProperty.GetStatements;
 
            // we need to insert user-specified code before other/existing code, including
            // the return statement
            if (additionalGetStatements != null && additionalGetStatements.Count > 0)
            {
                try
                {
                    CodeStatementCollection getStatements = new CodeStatementCollection();
                    getStatements.AddRange(additionalGetStatements.ToArray());
                    if (statements != null && statements.Count > 0)
                    {
                        getStatements.AddRange(statements);
                    }
                    statements.Clear();
                    statements.AddRange(getStatements);
                }
                catch (ArgumentNullException e)
                {
                    Generator.AddError(Strings.InvalidGetStatementSuppliedForProperty(Item.Name),
                                       ModelBuilderErrorCode.InvalidGetStatementSuppliedForProperty,
                                       EdmSchemaErrorSeverity.Error,
                                       e);
                }
            }
 
            MemberAttributes access = memberProperty.Attributes & MemberAttributes.AccessMask;
 
            AddGetterSetterFixUp(Generator.FixUps, PropertyFQName, GetGetterAccessibility(Item), access, true);
 
            EmitPropertyGetterBody(statements);
        }
 
        private void EmitPropertyGetterBody(CodeStatementCollection statements)
        {
            // If the SchemaElement.Type isn't a ComplexType it better be PrimitiveType.
            if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
            {
                //Since Complex Collections are not supported by
                //the stack, we don't need to do anything special 
                //like doing an Attach or Detatch like the way we do for complex types.
                if (GetCollectionKind(Item.TypeUsage) == CollectionKind.None)
                {
                    // _field = GetValidValue( _field, FieldPropertyInfo, _fieldInitialized);
                    statements.Add(
                        new CodeAssignStatement(FieldRef,
                            new CodeMethodInvokeExpression(
                                    ThisRef,
                                    Utils.GetValidValueMethodName,
                                    new CodeDirectionExpression(FieldDirection.In, FieldRef),
                                    new CodePrimitiveExpression(PropertyName),
                                    new CodePrimitiveExpression(Item.Nullable),
                                    ComplexPropertyInitializedFieldRef)));
 
                    // this._complexPropertyInitialized = true;
                    statements.Add(
                        new CodeAssignStatement(
                            ComplexPropertyInitializedFieldRef,
                            new CodePrimitiveExpression(true)));
                }
                // return _field;
                statements.Add(new CodeMethodReturnStatement(FieldRef));
            }
            else
            {
                PrimitiveType primitiveType = Item.TypeUsage.EdmType as PrimitiveType;
                if (primitiveType != null && primitiveType.ClrEquivalentType == typeof(byte[]))
                {
                    // return GetValidValue(_field);
                    statements.Add(
                        new CodeMethodReturnStatement(
                            new CodeMethodInvokeExpression(
                                CreateEdmStructuralObjectRef(TypeReference),
                                Utils.GetValidValueMethodName,
                                this.FieldRef)));
                }
                else
                {
                    // for everything else just return the field.
                    statements.Add(new CodeMethodReturnStatement(FieldRef));
                }
            }
        }
 
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberProperty"></param>
        /// <param name="additionalSetStatements">Additional statements to emit</param>
        private void EmitPropertySetter(CodeMemberProperty memberProperty, List<CodeStatement> additionalSetStatements)
        {
            CodeStatementCollection statements = memberProperty.SetStatements;
 
            MemberAttributes access = memberProperty.Attributes & MemberAttributes.AccessMask;
 
            AddGetterSetterFixUp(Generator.FixUps, PropertyFQName, GetSetterAccessibility(Item), access, false);
 
            EmitPropertySetterBody(statements, additionalSetStatements);
        }
 
        /// <summary>
        /// This is a control function to delegate the creation of the 
        /// setter statments to the correct code path
        /// </summary>
        /// <param name="statements">The collection that the setter statements should be added to.</param>
        /// <param name="additionalSetStatements">Additional statements to emit</param>
        private void EmitPropertySetterBody(CodeStatementCollection statements, List<CodeStatement> additionalSetStatements)
        {
            // Invoke the partial method "On[PropertyName]Changing();
            statements.Add(
                new CodeMethodInvokeExpression(
                    ThisRef,
                    OnChangingPartialMethodName(PropertyName), new CodePropertySetValueReferenceExpression()));
 
            // ReportPropertyChanging( _piFieldName );
            statements.Add(
                new CodeMethodInvokeExpression(
                    ThisRef,
                    Utils.ReportPropertyChangingMethodName,
                    new CodePrimitiveExpression(PropertyName)));
 
            // insert additional statements following the PropertyChanging event
            if (additionalSetStatements != null && additionalSetStatements.Count > 0)
            {
                try
                {
                    statements.AddRange(additionalSetStatements.ToArray());
                }
                catch (ArgumentNullException e)
                {
                    Generator.AddError(Strings.InvalidSetStatementSuppliedForProperty(Item.Name),
                                       ModelBuilderErrorCode.InvalidSetStatementSuppliedForProperty,
                                       EdmSchemaErrorSeverity.Error,
                                       e);
                }
            }
 
            if (MetadataUtil.IsPrimitiveType(Item.TypeUsage.EdmType))
            {
                EmitScalarTypePropertySetStatements(statements, CollectionKind.None);
            }
            else if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
            {
                // ComplexTypes have a completely different set pattern:
                EmitComplexTypePropertySetStatements(statements, CollectionKind.None);
            }
            else if (MetadataUtil.IsCollectionType(Item.TypeUsage.EdmType))
            {
                if (MetadataUtil.IsComplexType(((CollectionType)Item.TypeUsage.EdmType).TypeUsage.EdmType))
                {
                    EmitComplexTypePropertySetStatements(statements, GetCollectionKind(Item.TypeUsage));
                }
                else
                {
                    Debug.Assert(MetadataUtil.IsPrimitiveType(((CollectionType)Item.TypeUsage.EdmType).TypeUsage.EdmType),
                        "Collections should be of primitive types or complex types");
                    EmitScalarTypePropertySetStatements(statements, GetCollectionKind(Item.TypeUsage));
                }
 
            }
            else if (MetadataUtil.IsEnumerationType(Item.TypeUsage.EdmType))
            {
                // this.fieldName = value;
                statements.Add(
                    new CodeAssignStatement(
                            FieldRef,
                            new CodePropertySetValueReferenceExpression()));
 
            }
 
            // ReportPropertyChanged( _piFieldName );
            statements.Add(
                new CodeMethodInvokeExpression(
                    ThisRef,
                    Utils.ReportPropertyChangedMethodName,
                    new CodePrimitiveExpression(PropertyName)));
 
            // Invoke the partial method "On[PropertyName]Changed();
            statements.Add(
                new CodeMethodInvokeExpression(
                    ThisRef,
                    OnChangedPartialMethodName(PropertyName)));
        }
 
        /// <summary>
        /// Do the fixups to allow get and set statements in properties
        /// to have different accessibility than the property itself.
        /// </summary>
        /// <param name="accessibility">The accessibility for the getter or setter</param>
        /// <param name="propertyAccessibility">The property's accessibility</param>
        /// <param name="isGetter">True if this is a getter, false if a setter</param>
        internal static void AddGetterSetterFixUp(FixUpCollection fixups, string propertyFqName, MemberAttributes accessibility, MemberAttributes propertyAccessibility, bool isGetter)
        {
            Debug.Assert(GetAccessibilityRank(accessibility) >= 0, "bad accessibility");
 
            // Private
            if (accessibility == MemberAttributes.Private && propertyAccessibility != MemberAttributes.Private)
            {
                if (isGetter)
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsPrivate));
                }
                else
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsPrivate));
                }
            }
 
            // Internal
            if (accessibility == MemberAttributes.Assembly && propertyAccessibility != MemberAttributes.Assembly)
            {
                if (isGetter)
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsInternal));
                }
                else
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsInternal));
                }
            }
 
            // Public
            if (accessibility == MemberAttributes.Public && propertyAccessibility != MemberAttributes.Public)
            {
                if (isGetter)
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsPublic));
                }
                else
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsPublic));
                }
            }
 
            // Protected
            if (accessibility == MemberAttributes.Family && propertyAccessibility != MemberAttributes.Family)
            {
                if (isGetter)
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsProtected));
                }
                else
                {
                    fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsProtected));
                }
            }
 
        }
 
        /// <summary>
        /// Emit the set statements for a property that is a scalar type
        /// </summary>
        /// <param name="statements">The statement collection to add the set statements to.</param>
        private void EmitScalarTypePropertySetStatements(CodeStatementCollection statements,
            CollectionKind collectionKind)
        {
            Debug.Assert(statements != null, "statments can't be null");
            Debug.Assert(((MetadataUtil.IsPrimitiveType(Item.TypeUsage.EdmType)) || (MetadataUtil.IsCollectionType(Item.TypeUsage.EdmType)))
                , "Must be a primitive type or collection type property");
 
            CodePropertySetValueReferenceExpression valueRef = new CodePropertySetValueReferenceExpression();
            //Since collections are not supported by
            //the stack, we don't need to do anything special 
            //like doing an Attach or Detatch like the way we do for complex types.
            if (collectionKind == CollectionKind.None)
            {
 
                PrimitiveType primitiveType = (PrimitiveType)Item.TypeUsage.EdmType;
 
                // basic pattern
                // this.fieldName = SetValidValue( value );
                //
                List<CodeExpression> parameters = new List<CodeExpression>();
                parameters.Add(valueRef);
 
 
                // pattern for non Nullable<T> types (string, byte[])
                //
                // this.fieldName = SetValidVaue( value, nullability );
 
                if (primitiveType.ClrEquivalentType.IsClass)
                {
                    // ref types have an extra boolean parameter to tell if the property is allowed to 
                    // be null or not
                    parameters.Add(new CodePrimitiveExpression(Item.Nullable));
                }
 
                // now create and add the built statement
                statements.Add(
                    new CodeAssignStatement(
                            FieldRef,
                            new CodeMethodInvokeExpression(
                                CreateEdmStructuralObjectRef(TypeReference),
                                Utils.SetValidValueMethodName,
                                parameters.ToArray())));
            }
            else
            {
                // this.fieldName = value;
                statements.Add(
                    new CodeAssignStatement(
                        FieldRef, valueRef));
 
            }
        }
 
        private CodeExpression GetEnumValue<T>(T value)
        {
            Type type = typeof(T);
            return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(TypeReference.ForType(type)), Enum.GetName(type, value));
        }
 
        /// <summary>
        /// Emit the property set statments to properly set a ComplexType.
        /// </summary>
        /// <param name="statements">The collection of statements that the set statements should be added to.</param>
        private void EmitComplexTypePropertySetStatements(CodeStatementCollection statements, CollectionKind collectionKind)
        {
            CodePropertySetValueReferenceExpression valueRef = new CodePropertySetValueReferenceExpression();
            //Since collections are not supported by
            //the stack, we don't need to do anything special 
            //like doing an Attach or Detatch like the way we do for complex types.
            if (collectionKind == CollectionKind.None)
            {
 
                // this.fieldName = SetValidValue( this.fieldName, value, _pifieldName);
                statements.Add(
                    new CodeAssignStatement(
                        FieldRef,
                        new CodeMethodInvokeExpression(
                                ThisRef,
                                Utils.SetValidValueMethodName,
                                FieldRef,
                                valueRef,
                                new CodePrimitiveExpression(PropertyName))));
 
                // this._complexPropertyInitialized = true;
                statements.Add(
                    new CodeAssignStatement(
                        ComplexPropertyInitializedFieldRef,
                        new CodePrimitiveExpression(true)));
            }
            else
            {
                // this.fieldName = value;
                statements.Add(
                    new CodeAssignStatement(
                        FieldRef, valueRef));
 
            }
 
        }
 
        /// <summary>
        /// See if a property names will hide a base class member name
        /// </summary>
        private bool HidesBaseClassProperty
        {
            get
            {
                StructuralType parentBaseClass = Item.DeclaringType.BaseType as StructuralType;
                if (parentBaseClass != null && parentBaseClass.Members.Contains(PropertyName))
                    return true;
 
                return false;
            }
        }
 
        private CodeTypeReference GetType(EdmProperty property, bool getElementType)
        {
            PropertyTypeReferences types = default(PropertyTypeReferences);
            EdmType propertyType = property.TypeUsage.EdmType;
 
            // Initialize types
            if (MetadataUtil.IsPrimitiveType(propertyType))
            {
                types = new PropertyTypeReferences(TypeReference, (PrimitiveType)propertyType);
            }
            else if (MetadataUtil.IsComplexType(propertyType))
            {
                types = new PropertyTypeReferences(TypeReference, (ComplexType)propertyType, Generator);
            }
            else if (Helper.IsCollectionType(propertyType))
            {
                TypeUsage typeUsage = ((CollectionType)propertyType).TypeUsage;
                if (MetadataUtil.IsPrimitiveType(typeUsage.EdmType))
                {
                    types = new PropertyTypeReferences(TypeReference, (PrimitiveType)typeUsage.EdmType, GetCollectionKind(property.TypeUsage));
                }
                else
                {
                    Debug.Assert(MetadataUtil.IsComplexType(typeUsage.EdmType));
                    types = new PropertyTypeReferences(TypeReference, (ComplexType)typeUsage.EdmType, GetCollectionKind(property.TypeUsage), Generator);
                }
            }
            else
            {
                // shouldn't be able to get here....
                Debug.Fail("Unexpected Property.Type type: " + propertyType.GetType());
            }
 
            // Set types, or retrieve existing types if they have been set in the interim
            // Don't cache Collection types since CollectionKind is really a facet and
            //it is not part of the key we are using for the dictionary used to cache.
            if (!Helper.IsCollectionType(propertyType))
            {
                Debug.Assert(types.NonNullable != null && types.Nullable != null, "did you forget to set the types variable?");
            }
 
            if (property.Nullable)
            {
                return types.Nullable;
            }
            else
            {
                return types.NonNullable;
            }
        }
 
 
        private static CollectionKind GetCollectionKind(TypeUsage usage)
        {
            Facet collectionFacet;
            if (usage.Facets.TryGetValue(EdmConstants.CollectionKind, false, out collectionFacet))
            {
                return (CollectionKind)collectionFacet.Value;
            }
 
            return CollectionKind.None;
        }
 
        private string OnChangingPartialMethodName(string propertyName) { return "On" + propertyName + "Changing"; }
        private string OnChangedPartialMethodName(string propertyName) { return "On" + propertyName + "Changed"; }
 
        #endregion
 
        #region Private Properties
 
        private CodeFieldReferenceExpression FieldRef
        {
            get
            {
                if (_fieldRef == null)
                    _fieldRef = new CodeFieldReferenceExpression(ThisRef, FieldName);
 
                return _fieldRef;
            }
        }
 
        private CodeFieldReferenceExpression ComplexPropertyInitializedFieldRef
        {
            get
            {
                if (_complexPropertyInitializedFieldRef == null)
                    _complexPropertyInitializedFieldRef = new CodeFieldReferenceExpression(ThisRef, ComplexPropertyInitializedFieldName);
 
                return _complexPropertyInitializedFieldRef;
            }
        }
 
        private string FieldName
        {
            get
            {
                return Utils.FieldNameFromPropName(PropertyName);
            }
        }
 
        private string ComplexPropertyInitializedFieldName
        {
            get
            {
                return Utils.ComplexPropertyInitializedNameFromPropName(PropertyName);
            }
        }
 
        internal bool IsKeyProperty
        {
            get
            {
                EntityType entity = Item.DeclaringType as EntityType;
                if (entity != null)
                {
                    return entity.KeyMembers.Contains(Item.Name);
                }
                return false;
            }
        }
 
        internal static bool HasDefault(EdmProperty property)
        {
            return property.DefaultValue != null;
        }
 
        private CodeExpression GetDefaultValueExpression(EdmProperty property)
        {
            PrimitiveTypeKind type;
            object value = property.DefaultValue;
            if (value != null
                 && Utils.TryGetPrimitiveTypeKind(property.TypeUsage.EdmType, out type))
            {
                if (!property.Nullable && value.Equals(TypeSystem.GetDefaultValue(value.GetType())))
                {
                    return null;
                }
 
                switch (type)
                {
                    case PrimitiveTypeKind.Boolean:
                    case PrimitiveTypeKind.Byte:
                    case PrimitiveTypeKind.Int16:
                    case PrimitiveTypeKind.Int32:
                    case PrimitiveTypeKind.Int64:
                    case PrimitiveTypeKind.Decimal:
                    case PrimitiveTypeKind.Single:
                    case PrimitiveTypeKind.Double:
                    case PrimitiveTypeKind.String:
                        {
                            return new CodePrimitiveExpression(value);
                        }
                    case PrimitiveTypeKind.Guid:
                        {
                            return GetCodeExpressionFromGuid(value);
                        }
                    case PrimitiveTypeKind.DateTime:
                        {
                            return GetCodeExpressionFromDateTimeDefaultValue(value, property);
                        }
                    case PrimitiveTypeKind.DateTimeOffset:
                        {
                            return GetCodeExpressionFromDateTimeOffsetDefaultValue(value, property);
                        }
                    case PrimitiveTypeKind.Time:
                        {
                            return GetCodeExpressionFromTimeSpanDefaultValue(value, property);
                        }
                    case PrimitiveTypeKind.Binary:
                        {
                            return GetCodeExpressionFromBinary(value);
                        }
                    default:
                        Debug.Fail("Unsupported property type:" + type.ToString());
                        break;
                }
                return null;
            }
            return null;
        }
 
        private CodeExpression GetCodeExpressionFromBinary(object value)
        {
            byte[] data = (byte[])value;
            CodeExpression[] bytes = new CodeExpression[data.Length];
 
            for (int iByte = 0; iByte < data.Length; ++iByte)
            {
                bytes[iByte] = new CodePrimitiveExpression(data[iByte]);
            }
 
            return new CodeArrayCreateExpression(TypeReference.ByteArray, bytes);
        }
 
        private CodeExpression GetCodeExpressionFromGuid(object value)
        {
            Guid guid = (Guid)value;
            return new CodeObjectCreateExpression(TypeReference.Guid,
                new CodePrimitiveExpression(guid.ToString("D", CultureInfo.InvariantCulture)));
        }
 
        private CodeExpression GetCodeExpressionFromDateTimeDefaultValue(object value, EdmProperty property)
        {
            DateTime utc = (DateTime)value;
            DateTime dateTime = DateTime.SpecifyKind(utc, DateTimeKind.Unspecified);
 
            return new CodeObjectCreateExpression(TypeReference.DateTime, new CodePrimitiveExpression(dateTime.Ticks), GetEnumValue(DateTimeKind.Unspecified));
        }
 
        private CodeExpression GetCodeExpressionFromDateTimeOffsetDefaultValue(object value, EdmProperty property)
        {
            DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
 
            return new CodeObjectCreateExpression(TypeReference.DateTimeOffset, new CodePrimitiveExpression(dateTimeOffset.Ticks),
                new CodeObjectCreateExpression(TypeReference.TimeSpan, new CodePrimitiveExpression(dateTimeOffset.Offset.Ticks)));
        }
 
        private CodeExpression GetCodeExpressionFromTimeSpanDefaultValue(object value, EdmProperty property)
        {
            TimeSpan timeSpan = (TimeSpan)value;
            return new CodeObjectCreateExpression(TypeReference.TimeSpan, new CodePrimitiveExpression(timeSpan.Ticks));
        }
 
        public bool IsVirtualProperty
        {
            get
            {
                return false;
            }
        }
 
        private struct PropertyTypeReferences
        {
            CodeTypeReference _nonNullable;
            CodeTypeReference _nullable;
            public PropertyTypeReferences(TypeReference typeReference, PrimitiveType primitiveType)
                : this(typeReference, primitiveType, CollectionKind.None)
            {
            }
 
            public PropertyTypeReferences(TypeReference typeReference, PrimitiveType primitiveType, CollectionKind collectionKind)
            {
                Type type = primitiveType.ClrEquivalentType;
                if (collectionKind == CollectionKind.None)
                {
                    _nonNullable = typeReference.ForType(type);
                    if (type.IsValueType)
                    {
                        _nullable = typeReference.NullableForType(type);
                    }
                    else
                    {
                        _nullable = typeReference.ForType(type);
                    }
                }
                else
                {
                    CodeTypeReference primitiveTypeRef = typeReference.ForType(type);
                    CodeTypeReference collectionType = GetCollectionTypeReference(typeReference, primitiveTypeRef, collectionKind);
                    _nonNullable = collectionType;
                    _nullable = collectionType;
                }
            }
 
            public PropertyTypeReferences(TypeReference typeReference, ComplexType complexType, CollectionKind collectionKind, ClientApiGenerator generator)
            {
                CodeTypeReference baseType = generator.GetLeastPossibleQualifiedTypeReference(complexType);
                baseType = GetCollectionTypeReference(typeReference, baseType, collectionKind);
                _nonNullable = baseType;
                _nullable = baseType;
            }
 
            private static CodeTypeReference GetCollectionTypeReference(TypeReference typeReference, CodeTypeReference baseType, CollectionKind collectionKind)
            {
                if (collectionKind == CollectionKind.Bag)
                {
                    baseType = GetCollectionTypeReferenceForBagSemantics(typeReference, baseType);
                }
                else if (collectionKind == CollectionKind.List)
                {
                    baseType = GetCollectionTypeReferenceForListSemantics(typeReference, baseType);
                }
                else
                {
                    Debug.Assert(collectionKind == CollectionKind.None, "Was another CollectionKind value added");
                    // nothing more to do for .None
                }
                return baseType;
            }
 
            public PropertyTypeReferences(TypeReference typeReference, ComplexType complexType, ClientApiGenerator generator)
                : this(typeReference, complexType, CollectionKind.None, generator)
            {
            }
 
            private static CodeTypeReference GetCollectionTypeReferenceForBagSemantics(TypeReference typeReference, CodeTypeReference baseType)
            {
                CodeTypeReference typeRef = typeReference.ForType(typeof(System.Collections.Generic.ICollection<>), baseType);
                return typeRef;
            }
 
            private static CodeTypeReference GetCollectionTypeReferenceForListSemantics(TypeReference typeReference, CodeTypeReference baseType)
            {
                CodeTypeReference typeRef = typeReference.ForType(typeof(System.Collections.Generic.IList<>), baseType);
                return typeRef;
            }
 
            public CodeTypeReference NonNullable
            {
                get { return _nonNullable; }
            }
            public CodeTypeReference Nullable
            {
                get { return _nullable; }
            }
        }
        #endregion
 
        // properties from ClassPropertyEmitter
 
        public string PropertyFQName
        {
            get
            {
                return Item.DeclaringType.FullName + "." + Item.Name;
            }
        }
 
        public string PropertyName
        {
            get
            {
                return EntityPropertyName;
            }
        }
 
        private string PropertyClassName
        {
            get
            {
                return Item.DeclaringType.Name;
            }
        }
 
        private CodeMemberProperty EmitPropertyDeclaration(MemberAttributes scope, CodeTypeReference propertyType, bool isVirtual,
            bool hidesBaseProperty)
        {
            Debug.Assert(GetAccessibilityRank(scope) >= 0, "scope should only be an accessibility attribute");
 
            CodeMemberProperty memberProperty = new CodeMemberProperty();
            memberProperty.Name = PropertyName;
            CommentEmitter.EmitSummaryComments(Item, memberProperty.Comments);
 
            memberProperty.Attributes = scope;
 
            if (!isVirtual)
            {
                memberProperty.Attributes |= MemberAttributes.Final;
            }
 
            if (hidesBaseProperty || AncestorClassDefinesName(memberProperty.Name))
            {
                memberProperty.Attributes |= MemberAttributes.New;
            }
 
            memberProperty.Type = propertyType;
 
            return memberProperty;
        }
 
        private void DisallowReturnTypeChange(CodeTypeReference baseType, CodeTypeReference newType)
        {
            if (Helper.IsCollectionType(Item.TypeUsage.EdmType) && GetCollectionKind(Item.TypeUsage) != CollectionKind.None)
            {
                if (newType == null)
                {
                    throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnTypeToNull(Item.Name, Item.DeclaringType.Name));
                }
 
                // you can change the return type of collection properties
                // we don't even need to check
                return;
            }
 
            if (!(baseType == null && newType == null) &&
                (
                    (baseType != null && !baseType.Equals(newType)) ||
                    (newType != null && !newType.Equals(baseType))
                )
               )
            {
                throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnType(Item.Name, Item.DeclaringType.Name));
            }
        }
    }
}