/// <summary>
 /// Adds a property to the given type declaration based on the given metadata
 /// </summary>
 /// <param name="memberProperty">The property's metadata</param>
 /// <param name="parentClass">The type declaration</param>
 protected override void DeclareMemberProperty(MemberProperty memberProperty, CodeTypeDeclaration parentClass)
 {
     if (memberProperty.IsStream())
     {
         this.DeclareNamedStreamProperty(memberProperty, parentClass);
     }
     else
     {
         parentClass.AddAutoImplementedProperty(this.GetPropertyType(memberProperty, CodeGenerationTypeUsage.Declaration), memberProperty.Name);
     }
 }
 /// <summary>
 /// Adds a navigation property to the given type declaration based on the given metadata
 /// </summary>
 /// <param name="navigationProperty">The property's metadata</param>
 /// <param name="parentClass">The type declaration</param>
 protected override void DeclareNavigationProperty(NavigationProperty navigationProperty, CodeTypeDeclaration parentClass)
 {
     parentClass.AddAutoImplementedProperty(this.GetPropertyType(navigationProperty, CodeGenerationTypeUsage.Declaration), navigationProperty.Name);
 }
        /// <summary>
        /// Adds scalar and complex properties to the generated type class.
        /// </summary>
        /// <param name="codeClass">The <see cref="CodeTypeDeclaration"/> to which to add the properties.</param>
        /// <param name="type">The <see cref="StructuralType"/> from which to find properties.</param>
        protected virtual void AddProperties(CodeTypeDeclaration codeClass, StructuralType type)
        {
            foreach (var prop in type.Properties)
            {
                CodeTypeReference propertyType = null;
                var genericPropertyTypeAnnotation = prop.Annotations.OfType<GenericPropertyTypeAnnotation>().FirstOrDefault();
                if (genericPropertyTypeAnnotation != null)
                {
                    propertyType = Code.TypeRef(genericPropertyTypeAnnotation.GenericTypeParameterName);
                }
                else
                {
                    propertyType = codeTypeReferenceResolver.Resolve(prop.PropertyType);
                }

                var codeProp = codeClass.AddAutoImplementedProperty(propertyType, prop.Name);

                ApplyPropertyAccessModifier(codeProp, prop.Annotations.OfType<PropertyAccessModifierAnnotation>().SingleOrDefault());

                if (prop.Annotations.Any(a => a is VirtualAnnotation))
                {
                    codeProp.SetVirtual();
                }

                if (prop.Annotations.Any(a => a is CodeAttributeAnnotation))
                {
                    this.AddCodeAttributeAnnotationAsCustomAttribute(codeProp, prop.Annotations.OfType<CodeAttributeAnnotation>());
                }
            }
        }
        /// <summary>
        /// Adds navigation properties to generated type class.
        /// </summary>
        /// <param name="codeClass">The <see cref="CodeTypeDeclaration"/> to which to add the navigation properties.</param>
        /// <param name="type">The <see cref="EntityType"/> from which to find navigation properties.</param>
        /// <param name="defaultConstructor">The <see cref="CodeConstructor"/>, used to initialize navigation properties to their default values.</param>
        /// <remarks>
        /// Because CodeDom only supports property-level attributes, the get access modifier will be used for the entire property
        /// </remarks>
        protected virtual void AddNavigationProperties(CodeTypeDeclaration codeClass, EntityType type, CodeConstructor defaultConstructor)
        {
            foreach (var prop in type.NavigationProperties)
            {
                string propTypeName = prop.ToAssociationEnd.EntityType.FullName;
                bool isVirtual = prop.Annotations.Any(a => a is VirtualAnnotation);

                CodeTypeReference propType;
                if (prop.ToAssociationEnd.Multiplicity == EndMultiplicity.Many)
                {
                    // Instantiate collection in constructor if it's not overrideable
                    if (!isVirtual)
                    {
                        var instantiateType = Code.GenericType("List", propTypeName);
                        defaultConstructor.Statements.Add(Code.This().Property(prop.Name).Assign(Code.New(instantiateType)));
                    }

                    propType = Code.GenericType("ICollection", propTypeName);
                }
                else
                {
                    propType = new CodeTypeReference(propTypeName);
                }

                var codeProp = codeClass.AddAutoImplementedProperty(propType, prop.Name);

                ApplyPropertyAccessModifier(codeProp, prop.Annotations.OfType<PropertyAccessModifierAnnotation>().SingleOrDefault());

                if (prop.Annotations.Any(a => a is CodeAttributeAnnotation))
                {
                    this.AddCodeAttributeAnnotationAsCustomAttribute(codeProp, prop.Annotations.OfType<CodeAttributeAnnotation>());
                }

                if (isVirtual)
                {
                    codeProp.SetVirtual();
                }
            }
        }
        /// <summary>
        /// Generates and adds stream related attributes and elements to the entity type
        /// </summary>
        /// <param name="entityType">The entity type's metadata</param>
        /// <param name="entityTypeClass">The entity types declaration</param>
        protected override void GenerateHasStreamEntityTypeCodeElements(EntityType entityType, CodeTypeDeclaration entityTypeClass)
        {
            ExceptionUtilities.Assert(entityType.HasStream(), "This method should not be called for entity types without stream.");
            
            ClientMediaEntryAnnotation clientMediaEntryAnnotation = entityType.Annotations.OfType<ClientMediaEntryAnnotation>().FirstOrDefault();
            if (clientMediaEntryAnnotation != null)
            {
                // generate MediaEntry and MimeTypeProperty properties and attributes for V1 style stream support
                var attributeArguments1 = new CodeAttributeArgument[]
                {
                    new CodeAttributeArgument(Code.Primitive(clientMediaEntryAnnotation.MediaEntryName)),
                };
                var attributeArguments2 = new CodeAttributeArgument[]
                {
                    new CodeAttributeArgument(Code.Primitive(clientMediaEntryAnnotation.MediaEntryName)),
                    new CodeAttributeArgument(Code.Primitive(clientMediaEntryAnnotation.MimeTypePropertyName))
                };

                entityTypeClass.AddCustomAttribute(Code.TypeRef("MediaEntry"), attributeArguments1);
                entityTypeClass.AddCustomAttribute(Code.TypeRef("MimeTypeProperty"), attributeArguments2);
                entityTypeClass.AddAutoImplementedProperty(Code.TypeRef<byte[]>(), clientMediaEntryAnnotation.MediaEntryName);
                entityTypeClass.AddAutoImplementedProperty(Code.TypeRef<string>(), clientMediaEntryAnnotation.MimeTypePropertyName);
            }
            else
            {
                // No ClientMediaEntryAnnotation is found, generate HasStream atttribute for V2 and up stream support
                entityTypeClass.AddCustomAttribute(Code.TypeRef("HasStream"));
            }
        }
 /// <summary>
 /// Adds a named stream property to the given type declaration based on the given metadata
 /// </summary>
 /// <param name="namedStreamProperty">The named stream property's metadata</param>
 /// <param name="parentClass">The type declaration</param>
 protected override void DeclareNamedStreamProperty(MemberProperty namedStreamProperty, CodeTypeDeclaration parentClass)
 {
     parentClass.AddAutoImplementedProperty(Code.TypeRef("Microsoft.OData.Client.DataServiceStreamLink"), namedStreamProperty.Name);
 }