Example #1
0
        /// <summary>
        /// Constructor which creates an empty root.
        /// </summary>
        /// <param name="epmTargetTree">Target xml tree</param>
        internal EpmSourceTree(EpmTargetTree epmTargetTree)
        {
            DebugUtils.CheckNoExternalCallers();

            this.root = new EpmSourcePathSegment();
            this.epmTargetTree = epmTargetTree;
        }
 private bool ShouldWritePropertyInContent(IEdmStructuredType owningType, ProjectedPropertiesAnnotation projectedProperties, string propertyName, object propertyValue, EpmSourcePathSegment epmSourcePathSegment)
 {
     bool flag = !projectedProperties.ShouldSkipProperty(propertyName);
     if ((((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && (owningType != null)) && owningType.IsODataComplexTypeKind())
     {
         IEdmComplexType complexType = (IEdmComplexType) owningType;
         CachedPrimitiveKeepInContentAnnotation annotation = base.Model.EpmCachedKeepPrimitiveInContent(complexType);
         if ((annotation != null) && annotation.IsKeptInContent(propertyName))
         {
             return flag;
         }
     }
     if ((propertyValue == null) && (epmSourcePathSegment != null))
     {
         return true;
     }
     EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment);
     if (entityPropertyMapping == null)
     {
         return flag;
     }
     string str = propertyValue as string;
     if ((str != null) && (str.Length == 0))
     {
         switch (entityPropertyMapping.TargetSyndicationItem)
         {
             case SyndicationItemProperty.AuthorEmail:
             case SyndicationItemProperty.AuthorUri:
             case SyndicationItemProperty.ContributorEmail:
             case SyndicationItemProperty.ContributorUri:
                 return true;
         }
     }
     return (entityPropertyMapping.KeepInContent && flag);
 }
Example #3
0
        /// <summary>
        /// Determines if the property with the specified value should be written into content or not.
        /// </summary>
        /// <param name="owningType">The owning type of the property to be checked.</param>
        /// <param name="projectedProperties">The set of projected properties for the <paramref name="owningType"/></param>
        /// <param name="propertyName">The name of the property to be checked.</param>
        /// <param name="propertyValue">The property value to write.</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment for the property being written.</param>
        /// <returns>true if the property should be written into content, or false otherwise</returns>
        private bool ShouldWritePropertyInContent(
            IEdmStructuredType owningType,
            ProjectedPropertiesAnnotation projectedProperties,
            string propertyName,
            object propertyValue,
            EpmSourcePathSegment epmSourcePathSegment)
        {
            // check whether the property is projected; if no EPM is specified for the property the projection decides
            bool propertyProjected = !projectedProperties.ShouldSkipProperty(propertyName);

            bool useV1ProviderBehavior = this.MessageWriterSettings.WriterBehavior == null ? false : this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior;

            if (useV1ProviderBehavior && owningType != null && owningType.IsODataComplexTypeKind())
            {
                IEdmComplexType owningComplexType = (IEdmComplexType)owningType;
                CachedPrimitiveKeepInContentAnnotation keepInContentAnnotation = this.Model.EpmCachedKeepPrimitiveInContent(owningComplexType);
                if (keepInContentAnnotation != null && keepInContentAnnotation.IsKeptInContent(propertyName))
                {
                    return(propertyProjected);
                }
            }

            // We sometimes write properties into content even if asked not to.
            // If the property value is null and the property (or one of its descendant properties) is mapped,
            // we always write into content, even if the property was not projected.
            if (propertyValue == null && epmSourcePathSegment != null)
            {
                return(true);
            }

            EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment);

            if (entityPropertyMapping == null)
            {
                return(propertyProjected);
            }

            string stringPropertyValue = propertyValue as string;

            if (stringPropertyValue != null && stringPropertyValue.Length == 0)
            {
                // If the property value is an empty string and we should be writing it into an ATOM element which does not allow empty string
                // we write it into content as well, also even if the property was not projected.
                switch (entityPropertyMapping.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorEmail:
                case SyndicationItemProperty.ContributorUri:
                    return(true);

                default:
                    break;
                }
            }

            return(entityPropertyMapping.KeepInContent && propertyProjected);
        }
Example #4
0
        /// <summary>
        /// Gets the <see cref="EntityPropertyMappingAttribute"/> for the specified <paramref name="propertyName"/>
        /// from the <paramref name="epmParentSourcePathSegment"/>.
        /// </summary>
        /// <param name="epmParentSourcePathSegment">The EPM source path segment for the parent of the property being written.</param>
        /// <param name="propertyName">The name of the property to get the <see cref="EntityPropertyMappingAttribute"/> for.</param>
        /// <returns>The <see cref="EntityPropertyMappingAttribute"/> for the specified <paramref name="propertyName"/> or null if none exists.</returns>
        internal static EntityPropertyMappingAttribute GetEntityPropertyMapping(EpmSourcePathSegment epmParentSourcePathSegment, string propertyName)
        {
            DebugUtils.CheckNoExternalCallers();
            ExceptionUtils.CheckArgumentStringNotNullOrEmpty(propertyName, "propertyName");

            EpmSourcePathSegment epmSourcePathSegment = GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);

            return(GetEntityPropertyMapping(epmSourcePathSegment));
        }
Example #5
0
        /// <summary>
        /// Cache all the properties and collection item enumerations needed for EPM processing.
        /// </summary>
        /// <param name="propertyValueCache">The property value cache to cache the EPM related properties in.</param>
        /// <param name="sourceTree">The source tree describing all properties taking part in entity property mappings.</param>
        internal static void CacheEpmProperties(EntryPropertiesValueCache propertyValueCache, EpmSourceTree sourceTree)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(propertyValueCache != null, "propertyValueCache != null");
            Debug.Assert(sourceTree != null, "sourceTree != null");

            EpmSourcePathSegment rootSegment = sourceTree.Root;

            Debug.Assert(rootSegment.PropertyName == null, "Root segment should have 'null' property name.");
            CacheEpmSourcePathSegments(propertyValueCache, rootSegment.SubProperties, propertyValueCache.EntryProperties);
        }
Example #6
0
        /// <summary>
        /// Returns an <see cref="EpmSourcePathSegment"/> for a given property provided the parent <see cref="EpmSourcePathSegment"/>.
        /// </summary>
        /// <param name="epmParentSourcePathSegment">The parent <see cref="EpmSourcePathSegment"/> to get the property segment from.</param>
        /// <param name="propertyName">The name of the property to get the <see cref="EpmSourcePathSegment"/> for.</param>
        /// <returns>An <see cref="EpmSourcePathSegment"/> for a given property provided the parent <see cref="EpmSourcePathSegment"/>.</returns>
        internal static EpmSourcePathSegment GetPropertySourcePathSegment(EpmSourcePathSegment epmParentSourcePathSegment, string propertyName)
        {
            DebugUtils.CheckNoExternalCallers();

            EpmSourcePathSegment epmSourcePathSegment = null;

            if (epmParentSourcePathSegment != null)
            {
                epmSourcePathSegment = epmParentSourcePathSegment.SubProperties.FirstOrDefault(subProperty => subProperty.PropertyName == propertyName);
            }

            return(epmSourcePathSegment);
        }
Example #7
0
        /// <summary>
        /// Writes a property with a complex value in ATOM format.
        /// </summary>
        /// <param name="complexValue">The complex value to write.</param>
        /// <param name="propertyName">The name of the property being written.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforeValueAction">Action called before the complex value is written, if it's actually written.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="propertyTypeReference">The type information for the property being written.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property we're writing. (can be null)</param>
        /// <param name="complexValueProjectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        private bool WriteComplexValueProperty(
            ODataComplexValue complexValue,
            string propertyName,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforeValueAction,
            EpmValueCache epmValueCache,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation complexValueProjectedProperties)
        {
            // Complex properties are written recursively.
            DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();

            if (isTopLevel)
            {
                // Top-level property must always write the property element
                Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null");
                this.WritePropertyStart(beforeValueAction, propertyName, isWritingCollection, /*isTopLevel*/ true);
                this.AssertRecursionDepthIsZero();
                this.WriteComplexValue(
                    complexValue,
                    propertyTypeReference,
                    isOpenPropertyType,
                    isWritingCollection,
                    null /* beforeValueAction */,
                    null /* afterValueAction */,
                    complexValuePropertyNamesChecker,
                    null /* collectionValidator */,
                    epmValueCache,
                    epmSourcePathSegment,
                    null /* projectedProperties */);
                this.AssertRecursionDepthIsZero();
                this.WritePropertyEnd();
                return(true);
            }

            return(this.WriteComplexValue(
                       complexValue,
                       propertyTypeReference,
                       isOpenPropertyType,
                       isWritingCollection,
                       () => this.WritePropertyStart(beforeValueAction, propertyName, isWritingCollection, /*isTopLevel*/ false),
                       this.WritePropertyEnd,
                       complexValuePropertyNamesChecker,
                       null /* collectionValidator */,
                       epmValueCache,
                       epmSourcePathSegment,
                       complexValueProjectedProperties));
        }
Example #8
0
        internal static EntityPropertyMappingAttribute GetEntityPropertyMapping(EpmSourcePathSegment epmSourcePathSegment)
        {
            if (epmSourcePathSegment == null)
            {
                return(null);
            }
            EntityPropertyMappingInfo epmInfo = epmSourcePathSegment.EpmInfo;

            if (epmInfo == null)
            {
                return(null);
            }
            return(epmInfo.Attribute);
        }
Example #9
0
        internal static EpmSourcePathSegment GetPropertySourcePathSegment(EpmSourcePathSegment epmParentSourcePathSegment, string propertyName)
        {
            Func <EpmSourcePathSegment, bool> predicate = null;
            EpmSourcePathSegment segment = null;

            if (epmParentSourcePathSegment == null)
            {
                return(segment);
            }
            if (predicate == null)
            {
                predicate = subProperty => subProperty.PropertyName == propertyName;
            }
            return(epmParentSourcePathSegment.SubProperties.FirstOrDefault <EpmSourcePathSegment>(predicate));
        }
Example #10
0
        /// <summary>
        /// Write the given collection of properties.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="cachedProperties">Collection of cached properties for the entry.</param>
        /// <param name="isWritingCollection">true if we are writing a top level collection instead of an entry.</param>
        /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param>
        /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property which sub-properites we're writing. (can be null)</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        internal bool WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable <ODataProperty> cachedProperties,
            bool isWritingCollection,
            Action beforePropertiesAction,
            Action afterPropertiesAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            if (cachedProperties == null)
            {
                return(false);
            }

            bool propertyWritten = false;

            foreach (ODataProperty property in cachedProperties)
            {
                propertyWritten |= this.WriteProperty(
                    property,
                    owningType,
                    /*isTopLevel*/ false,
                    isWritingCollection,
                    propertyWritten ? null : beforePropertiesAction,
                    epmValueCache,
                    epmSourcePathSegment,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }

            if (afterPropertiesAction != null && propertyWritten)
            {
                afterPropertiesAction();
            }

            return(propertyWritten);
        }
Example #11
0
        /// <summary>
        /// Gets the <see cref="EntityPropertyMappingAttribute"/> for the specified <paramref name="epmSourcePathSegment"/>.
        /// </summary>
        /// <param name="epmSourcePathSegment">The EPM source path segment to get the <see cref="EntityPropertyMappingAttribute"/> from.</param>
        /// <returns>The <see cref="EntityPropertyMappingAttribute"/> for the specified <paramref name="epmSourcePathSegment"/> or null if none exists.</returns>
        internal static EntityPropertyMappingAttribute GetEntityPropertyMapping(EpmSourcePathSegment epmSourcePathSegment)
        {
            DebugUtils.CheckNoExternalCallers();

            if (epmSourcePathSegment == null)
            {
                return(null);
            }

            EntityPropertyMappingInfo epmInfo = epmSourcePathSegment.EpmInfo;

            if (epmInfo == null)
            {
                return(null);
            }

            EntityPropertyMappingAttribute epmAttribute = epmInfo.Attribute;

            Debug.Assert(epmAttribute != null, "Attribute should always be initialized for EpmInfo.");
            return(epmAttribute);
        }
Example #12
0
        /// <summary>Validates the specified segment and all its subsegments.</summary>
        /// <param name="pathSegment">The path segment to validate.</param>
        /// <param name="resourceType">The resource type of the property represented by this segment (null for open properties).</param>
        /// <param name="declaredPropertiesLookup">The dictionary to lookup and add declaredProperties associated with resourceTypes.</param>
        private static void Validate(EpmSourcePathSegment pathSegment, ClientTypeOrResourceType_Alias resourceType, Dictionary <ResourceType, IEnumerable <ResourceProperty> > declaredPropertiesLookup)
#endif
        {
            Debug.Assert(pathSegment != null, "pathSegment != null");
            foreach (EpmSourcePathSegment subSegment in pathSegment.SubProperties)
            {
#if ASTORIA_CLIENT
                ClientTypeOrResourceType_Alias subSegmentResourceType = GetPropertyType(resourceType, subSegment.PropertyName);

                // sometimes the previous call returns null,  WHY do we even bother
                // to continue on after we can't find a resourceType?
                Validate(subSegment, subSegmentResourceType);
#else
                ClientTypeOrResourceType_Alias subSegmentResourceType = GetPropertyType(resourceType, subSegment.PropertyName, declaredPropertiesLookup);

                // sometimes the previous call returns null,  WHY do we even bother
                // to continue on after we can't find a resourceType?
                Validate(subSegment, subSegmentResourceType, declaredPropertiesLookup);
#endif
            }
        }
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param>
        /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The type owning the property (or null if no metadata is available).</param>
        /// <param name="version">The protocol version used for writing.</param>
        /// <param name="isTopLevel">True if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param>
        internal static void WriteProperty(
            XmlWriter writer,
            DataServiceMetadataProviderWrapper metadata,
            ODataProperty property,
            ResourceType owningType,
            ODataVersion version, 
            bool isTopLevel,
            bool isWritingCollection,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmParentSourcePathSegment)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(writer != null, "writer != null");

            ValidationUtils.ValidateProperty(property);
            ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(property.Name, owningType);

            EpmSourcePathSegment epmSourcePathSegment = null;
            if (epmParentSourcePathSegment != null)
            {
                epmSourcePathSegment = epmParentSourcePathSegment.SubProperties.Where(subProperty => subProperty.PropertyName == property.Name).FirstOrDefault();
            }

            object value = property.Value;

            // TODO: If we implement validation or type conversions the value needs to be converted here
            //       since the next method call needs to know if the value is a string or not in some cases.

            // If EPM tells us to skip this property in content, then we're done here.
            if (!ShouldWritePropertyInContent(value, epmSourcePathSegment, version))
            {
                return;
            }

            // <d:propertyname>
            writer.WriteStartElement(
                isWritingCollection ? string.Empty : AtomConstants.ODataNamespacePrefix,
                property.Name,
                AtomConstants.ODataNamespace);

            if (isTopLevel)
            {
                WriteDefaultNamespaceAttributes(writer, DefaultNamespaceFlags.OData | DefaultNamespaceFlags.ODataMetadata);
            }

            // Null property value.
            if (value == null)
            {
                // verify that MultiValue properties are not null
                if (resourceProperty != null && resourceProperty.Kind == ResourcePropertyKind.MultiValue)
                {
                    throw new ODataException(Strings.ODataWriter_MultiValuePropertiesMustNotHaveNullValue(resourceProperty.Name));
                }

                ODataAtomWriterUtils.WriteNullAttribute(writer);
            }
            else
            {
                ODataComplexValue complexValue = value as ODataComplexValue;
                ResourceType resourcePropertyType = resourceProperty == null ? null : resourceProperty.ResourceType;
                bool isOpenPropertyType = owningType != null && owningType.IsOpenType && resourceProperty == null;

                // Complex properties are written recursively.
                if (complexValue != null)
                {
                    WriteComplexValue(writer, metadata, complexValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment);
                }
                else
                {
                    ODataMultiValue multiValue = value as ODataMultiValue;
                    if (multiValue != null)
                    {
                        ODataVersionChecker.CheckMultiValueProperties(version, property.Name);
                        WriteMultiValue(writer, metadata, multiValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment);
                    }
                    else
                    {
                        WritePrimitiveValue(writer, value, resourcePropertyType);
                    }
                }
            }

            // </d:propertyname>
            writer.WriteEndElement();
        }
Example #14
0
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();

            List<EpmSourcePathSegment> pathToCurrentSegment = new List<EpmSourcePathSegment>();
            EpmSourcePathSegment currentSourceSegment = this.Root;
            EpmSourcePathSegment foundSourceSegment = null;
            ResourceType currentType = epmInfo.ActualPropertyType;
            EpmSourcePathSegment multiValuePropertySegment = null;

            Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.SourcePath), "Invalid source path");
            string[] propertyPath = epmInfo.Attribute.SourcePath.Split('/');

            if (epmInfo.CriteriaValue != null)
            {
                ValidateConditionalMapping(epmInfo);
            }

            Debug.Assert(propertyPath.Length > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < propertyPath.Length; sourcePropertyIndex++)
            {
                string propertyName = propertyPath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new ODataException(Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, epmInfo.Attribute.SourcePath));
                }

                bool isMultiValueProperty;
                currentType = GetPropertyType(currentType, propertyName, out isMultiValueProperty);

                foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundSourceSegment != null)
                {
                    currentSourceSegment = foundSourceSegment;
                }
                else
                {
                    EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName);
                    currentSourceSegment.SubProperties.Add(newSourceSegment);
                    currentSourceSegment = newSourceSegment;
                }

                pathToCurrentSegment.Add(currentSourceSegment);

                if (isMultiValueProperty)
                {
                    Debug.Assert(
                        currentSourceSegment.EpmInfo == null || currentSourceSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                        "MultiValue property must have EpmInfo marked as MultiValue or none at all.");
                    Debug.Assert(
                        currentSourceSegment.EpmInfo != null || foundSourceSegment == null,
                        "The only way to get a propety without info attached yet on a MultiValue property is when we just created it.");

                    if (multiValuePropertySegment != null)
                    {
                        // Nested MultiValue - not allowed to be mapped
                        throw new ODataException(Strings.EpmSourceTree_NestedMultiValue(
                            multiValuePropertySegment.EpmInfo.Attribute.SourcePath,
                            multiValuePropertySegment.EpmInfo.DefiningType.Name,
                            epmInfo.Attribute.SourcePath));
                    }

                    multiValuePropertySegment = currentSourceSegment;

                    // MultiValue properties can only be mapped to a top-level element, so we can blindly use the first part
                    //   of the target path as the target path for the MultiValue property.
                    Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.TargetPath), "Target path should have been checked by the EpmAttribute constructor.");
                    string multiValuePropertyTargetPath = epmInfo.Attribute.TargetPath.Split('/')[0];

                    if (currentSourceSegment.EpmInfo == null || !currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                    {
                        if (currentSourceSegment.EpmInfo != null)
                        {
                            Debug.Assert(!currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo), "Just verifying that the ifs are correct.");
                            Debug.Assert(foundSourceSegment != null, "Can't have existing node with EpmInfo on it here and not found it before.");

                            // If the MultiValue property we're trying to add is from a different type than the one we already have
                            //   just overwrite the epm info. This means that the derived type defines a different mapping for the property than the base type.
                            //   We also need to walk all the children of the base type mapping here and remove them from the target tree
                            //   since we're overriding the entire MultiValue property mapping (not just one item property)
                            // Note that for MultiValue properties, removing the MultiValue property node itself will remove all the MultiValue item properties as well
                            //   as they have to be children of the MultiValue property node in the target tree.
                            this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);

                            // We also need to remove all children of the MultiValue property node from the source tree
                            //   as the derived type is overriding it completely. If the derived type doesn't override all of the properties
                            //   we should fail as if it did't map all of them.
                            currentSourceSegment.SubProperties.Clear();
                        }

                        // This is the first time we've seen this MultiValue property mapped
                        //   (on this type, we might have seen it on the base type, but that has been removed)

                        // The source path is the path we have so far for the property
                        string multiValuePropertySourcePath = string.Join("/", propertyPath, 0, sourcePropertyIndex + 1);

                        if (!epmInfo.IsSyndicationMapping)
                        {
                            // Custom EPM for MultiValue is not supported yet
                            // Note: This has already been implemented, but then removed from the code. To see what it takes to implement this
                            //   please see the change which adds this comment into the sources.
                            throw new ODataException(Strings.EpmSourceTree_MultiValueNotAllowedInCustomMapping(
                                multiValuePropertySourcePath,
                                epmInfo.DefiningType.Name));
                        }

                        // Create a new EPM attribute to represent the MultiValue property mapping
                        //   note that this attribute is basically implicitly declared whenever the user declares EPM attribute
                        //   for a property from some MultiValue property. (the declaration happens right here)
                        EntityPropertyMappingAttribute multiValueEpmAttribute = new EntityPropertyMappingAttribute(
                                multiValuePropertySourcePath,
                                multiValuePropertyTargetPath,
                                epmInfo.Attribute.TargetNamespacePrefix,
                                epmInfo.Attribute.TargetNamespaceUri,
                                epmInfo.Attribute.KeepInContent);

                        // Create a special EpmInfo from the above special attribute which represents just the MultiValue property itself
                        EntityPropertyMappingInfo multiValueEpmInfo = new EntityPropertyMappingInfo(
                            multiValueEpmAttribute, 
                            epmInfo.DefiningType, 
                            epmInfo.ActualPropertyType);
                        multiValueEpmInfo.MultiValueStatus = EntityPropertyMappingMultiValueStatus.MultiValueProperty;
                        multiValueEpmInfo.MultiValueItemType = currentType;

                        // We need to mark the info as syndication/custom mapping explicitely since the attribute we create (From which it's infered) is always custom mapping
                        Debug.Assert(epmInfo.IsSyndicationMapping, "Only syndication mapping is allowed for MultiValue properties.");
                        multiValueEpmInfo.SetMultiValuePropertySyndicationMapping();

                        multiValueEpmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());

                        multiValueEpmInfo.Criteria = epmInfo.Criteria;
                        multiValueEpmInfo.CriteriaValue = epmInfo.CriteriaValue;

                        // Now associate the current source tree segment with the new info object (or override the one from base)
                        currentSourceSegment.EpmInfo = multiValueEpmInfo;

                        // And add the new info to the target tree
                        this.epmTargetTree.Add(multiValueEpmInfo);

                        // And continue with the walk of the source path.
                        // This means that the EPM attribute specified as the input to this method is still to be added
                        // It might be added to the source tree if the path is longer (property on an item in the MultiValue property of complex types)
                        //   or it might not be added to the source tree if this segment is the last (MultiValue property of primitive types).
                        // In any case it will be added to the target tree (so even if the MultiValue property itself is mapped to the top-level element only
                        //   the items in the MultiValue property can be mapped to child element/attribute of that top-level element).
                    }
                    else
                    {
                        Debug.Assert(currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo), "The condition in the surrounding if is broken.");

                        // We have already found a MultiValue property mapped from this source node.
                        // If it's on the same defining type we need to make sure that it's the same MultiValue property being mapped
                        // First verify that the mapping for the other property has the same top-level element for the MultiValue property
                        //   since we only allow properties from one MultiValue property to be mapped to the same top-level element
                        if (multiValuePropertyTargetPath != currentSourceSegment.EpmInfo.Attribute.TargetPath ||
                            epmInfo.Attribute.TargetNamespacePrefix != currentSourceSegment.EpmInfo.Attribute.TargetNamespacePrefix ||
                            epmInfo.Attribute.TargetNamespaceUri != currentSourceSegment.EpmInfo.Attribute.TargetNamespaceUri ||
                            epmInfo.Criteria != currentSourceSegment.EpmInfo.Criteria ||
                            String.Compare(epmInfo.Attribute.CriteriaValue, currentSourceSegment.EpmInfo.CriteriaValue, StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            throw new ODataException(Strings.EpmSourceTree_PropertiesFromSameMultiValueMappedToDifferentTopLevelElements(currentSourceSegment.EpmInfo.Attribute.SourcePath, currentSourceSegment.EpmInfo.DefiningType.Name));
                        }

                        // Second verify that the mappings for both properties have the same KeepInContent value
                        if (epmInfo.Attribute.KeepInContent != currentSourceSegment.EpmInfo.Attribute.KeepInContent)
                        {
                            throw new ODataException(Strings.EpmSourceTree_PropertiesFromSameMultiValueMappedWithDifferentKeepInContent(currentSourceSegment.EpmInfo.Attribute.SourcePath, currentSourceSegment.EpmInfo.DefiningType.Name));
                        }
                    }
                }
            }

            // The last segment is the one being mapped from by the user specified attribute.
            // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly.
            // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type
            //   and we will make this check later during serialization again when we actually have the value of the property.
            if (currentType != null)
            {
                if (currentType.ResourceTypeKind != ResourceTypeKind.Primitive)
                {
                    throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }

                SyndicationItemProperty targetSyndicationItem = epmInfo.Attribute.TargetSyndicationItem;
                if (targetSyndicationItem == SyndicationItemProperty.LinkRel || targetSyndicationItem == SyndicationItemProperty.CategoryScheme)
                {
                    if (PrimitiveStringResourceType != currentType)
                    {
                        throw new InvalidOperationException(Strings.EpmSourceTree_NonStringPropertyMappedToConditionAttribute(
                            currentSourceSegment.PropertyName,
                            epmInfo.DefiningType.FullName,
                            targetSyndicationItem.ToString()));
                    }
                }
            }

            if (multiValuePropertySegment == currentSourceSegment)
            {
                // If the MultiValue property is the last segment it means that the MultiValue property itself is being mapped (and it must be a MultiValue of primitive types).

                // If we found the MultiValue property already in the tree, here we actually want the item of the MultiValue property (as the MultiValue one was processed above already)
                // If we have the item value already in the tree use it as the foundProperty so that we correctly check the duplicate mappings below
                if (foundSourceSegment != null)
                {
                    Debug.Assert(foundSourceSegment == currentSourceSegment, "If we found an existing segment it must be the current one.");
                    foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.IsMultiValueItemValue);
                }

                if (foundSourceSegment == null)
                {
                    // This is a bit of a special case. In the source tree we will create a special node to represent the item value (we need that to be able to tell
                    //   if it was not mapped twice).
                    // In the target tree, we will also create a special node which will hold the information specific
                    //   to serialization of the item value (for example the exact syndication mapping target and so on).
                    //   The creation of the special node is done in the target tree Add method.
                    EpmSourcePathSegment newSegment = EpmSourcePathSegment.CreateMultiValueItemValueSegment();
                    currentSourceSegment.SubProperties.Add(newSegment);
                    currentSourceSegment = newSegment;
                }
                else
                {
                    currentSourceSegment = foundSourceSegment;
                }
            }

            // Note that once we're here the EpmInfo we have is never the MultiValue property itself, it's always either a non-MultiValue property
            //   or MultiValue item property.
            Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo.");

            // Two EpmAttributes with same PropertyName in the same ResourceType, this could be a result of inheritance
            if (foundSourceSegment != null)
            {
                Debug.Assert(Object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment");
                Debug.Assert(
                    foundSourceSegment.EpmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                    "We should never get here with a MultiValue property itself, we should have a node represent its item or property on the item instead.");

                // Check for duplicates on the same entity type
                Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                {
                    throw new ODataException(Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }

                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);
            }

            epmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());
            currentSourceSegment.EpmInfo = epmInfo;

            if (multiValuePropertySegment != null)
            {
                Debug.Assert(multiValuePropertySegment.EpmInfo != null, "All MultiValue property segments must have EpmInfo assigned.");

                // We are mapping a MultiValue property - so mark the info as a MultiValue item property (since the MultiValue property itself was added above)
                epmInfo.MultiValueStatus = EntityPropertyMappingMultiValueStatus.MultiValueItemProperty;

                // Set the item type on each of the item properties, so that the segmented path know from which type to start
                epmInfo.MultiValueItemType = multiValuePropertySegment.EpmInfo.MultiValueItemType;

                // And trim its property value path to start from the MultiValue item. This path is basically a list of properties to traverse
                //   when access the value of the property on the specified resource. For non-MultiValue and MultiValue properties themselves 
                //   this path starts with the entity instance. For MultiValue item properties this path starts with the MultiValue item instance.
                //   Note that if it's a MultiValue of primitive types, the path is going to be empty meaning that the value is the item instance itself.
                epmInfo.TrimMultiValueItemPropertyPath(multiValuePropertySegment.EpmInfo);

#if DEBUG
                // Check that if the MultiValue item is of primitive type, we can only ever add a single child source segment which points directly to the MultiValue property itself
                // If we would allow this here, we would fail later, but with a much weirder error message
                Debug.Assert(
                    multiValuePropertySegment.EpmInfo.MultiValueItemType.ResourceTypeKind != ResourceTypeKind.Primitive || epmInfo.PropertyValuePath.Length == 0,
                    "We shoud have failed to map a subproperty of a primitive MultiValue item.");
#endif
            }

            this.epmTargetTree.Add(epmInfo);
        }
Example #15
0
        /// <summary>
        /// Validates the specified segment which is a subsegment of a MultiValue property or the MultiValue property segment itself.
        /// </summary>
        /// <param name="multiValuePropertyInfo">Info about the MultiValue property being processed. Used for exception messages only.</param>
        /// <param name="multiValueSegment">The segment belonging to a MultiValue property to validate.</param>
        /// <param name="resourceType">The resource type of the property represented by this segment (item type for the MultiValue property itself).</param>
        private static void ValidateMultiValueSegment(
            EntityPropertyMappingInfo multiValuePropertyInfo, 
            EpmSourcePathSegment multiValueSegment, 
            ResourceType resourceType)
        {
            Debug.Assert(
                multiValuePropertyInfo != null && multiValuePropertyInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                "multiValuePropertyName must be non-null and must be a MultiValue info.");
            Debug.Assert(multiValueSegment != null, "multiValueSegment != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(
                multiValueSegment.EpmInfo == null ||
                multiValueSegment.EpmInfo == multiValuePropertyInfo ||
                multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty,
                "The specified segment does not belong to a MultiValue property subtree.");

            if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
            {
                Debug.Assert(
                    multiValueSegment.EpmInfo == null || multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                    "EPM source segment representing a complex property of a MultiValue property must not have an EpmInfo for the property itself.");

                // Verify that all properties of the complex type are mapped (have respective source segments)
                foreach (ResourceProperty property in resourceType.Properties)
                {
                    string propertyName = property.Name;
                    string resourceTypeName = resourceType.Name;
                    EpmSourcePathSegment subSegment = multiValueSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                    if (subSegment == null)
                    {
                        throw new ODataException(Strings.EpmSourceTree_NotAllMultiValueItemPropertiesMapped(
                            multiValuePropertyInfo.Attribute.SourcePath,
                            multiValuePropertyInfo.DefiningType.Name,
                            propertyName,
                            resourceTypeName));
                    }
                    else
                    {
                        ResourceType propertyType = property.ResourceType;

                        // Recursive call to verify the sub segment
                        // Note that we don't need to check for enless loops and recursion depth because we are effectively walking the EPM source tree
                        //   which itself can't have loops in it, and can't be infinite either. So if the metadata for a MultiValue property has loops in it
                        //   we would eventually fail to find a matching segment in the source tree and throw.
                        ValidateMultiValueSegment(multiValuePropertyInfo, subSegment, propertyType);
                    }
                }
            }
            else
            {
                Debug.Assert(multiValueSegment.EpmInfo != null, "Primitive value must have EpmInfo.");

                if (multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty)
                {
                    // MultiValue of primitive types
                    Debug.Assert(multiValueSegment.SubProperties.Count == 1, "Exactly one subproperty should be on a node representing a MultiValue property of primitive types.");
                    Debug.Assert(
                        multiValueSegment.SubProperties[0].IsMultiValueItemValue && multiValueSegment.SubProperties[0].EpmInfo != null &&
                        multiValueSegment.SubProperties[0].EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty,
                        "The only subproperty of a collectin of primitive types should be the special item's value node.");
                    EpmSourcePathSegment leafSegment = multiValueSegment.SubProperties[0];
                    if (leafSegment.EpmInfo.IsAtomLinkMapping)
                    {
                        if (leafSegment.EpmInfo.CriteriaValue == null)
                        {
                            throw new ODataException(Strings.EpmSourceTree_MultiValueOfPrimitiveMappedToLinkWithoutCriteria(
                                multiValuePropertyInfo.Attribute.SourcePath, 
                                multiValuePropertyInfo.DefiningType.Name, 
                                leafSegment.EpmInfo.Attribute.TargetPath));
                        }
                        else if (leafSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.LinkHref)
                        {
                            throw new ODataException(Strings.EpmTargetTree_ConditionalMappingLinkHrefIsRequired(
                                multiValuePropertyInfo.Attribute.SourcePath, 
                                multiValuePropertyInfo.DefiningType.Name, 
                                leafSegment.EpmInfo.Attribute.TargetPath));
                        }
                    }
                    else if (leafSegment.EpmInfo.IsAtomCategoryMapping)
                    {
                        if (leafSegment.EpmInfo.CriteriaValue == null && leafSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CategoryTerm)
                        {
                            throw new ODataException(Strings.EpmTargetTree_ConditionalMappingCategoryTermIsRequired(
                                multiValuePropertyInfo.Attribute.SourcePath, 
                                multiValuePropertyInfo.DefiningType.Name, 
                                leafSegment.EpmInfo.Attribute.TargetPath));
                        }
                    }
                }
                else
                {
                    // MultiValue of complex types, we're on a leaf primitive property.
                    Debug.Assert(
                        multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty,
                        "Only multiValue property or multiValue item property nodes should get here.");
                    Debug.Assert(multiValueSegment.SubProperties.Count == 0, "Primtive item property value should have no sub properties.");
                    Debug.Assert(!multiValueSegment.IsMultiValueItemValue, "Special MultiValue item value must not be a child of a complex property.");

                    EntityPropertyMappingInfo multiValueSegmentEpmInfo = multiValueSegment.EpmInfo;
                    if (multiValueSegmentEpmInfo.IsAtomLinkMapping != multiValueSegmentEpmInfo.IsAtomLinkMapping ||
                        multiValueSegmentEpmInfo.IsAtomCategoryMapping != multiValueSegmentEpmInfo.IsAtomCategoryMapping ||
                        String.Compare(multiValueSegmentEpmInfo.CriteriaValue, multiValueSegmentEpmInfo.CriteriaValue, StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        throw new ODataException(Strings.EpmSourceTree_MultiValueOfComplexTypesDifferentConditionalMapping(
                            multiValuePropertyInfo.Attribute.SourcePath, 
                            multiValuePropertyInfo.DefiningType.Name,
                            multiValuePropertyInfo.Attribute.TargetPath));
                    }
                }
            }
        }
 internal void SetPropertyValuePath(EpmSourcePathSegment[] path)
 {
     this.propertyValuePath = path;
 }
Example #17
0
        private bool WriteProperty(ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
        {
            Action beforeValueAction = null;
            Action afterValueAction  = null;

            WriterValidationUtils.ValidatePropertyNotNull(property);
            object propertyValue = property.Value;
            string propertyName  = property.Name;
            EpmSourcePathSegment          propertySourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);
            ODataComplexValue             complexValue = propertyValue as ODataComplexValue;
            ProjectedPropertiesAnnotation emptyProjectedPropertiesMarker = null;

            if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, propertyValue, propertySourcePathSegment))
            {
                if ((propertySourcePathSegment == null) || (complexValue == null))
                {
                    return(false);
                }
                emptyProjectedPropertiesMarker = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker;
            }
            WriterValidationUtils.ValidateProperty(property);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);

            if (propertyValue is ODataStreamReferenceValue)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }
            if (((edmProperty != null) && edmProperty.Type.IsSpatial()) || ((edmProperty == null) && (propertyValue is ISpatial)))
            {
                ODataVersionChecker.CheckSpatialValue(base.Version);
            }
            if (propertyValue == null)
            {
                this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return(true);
            }
            bool isOpenPropertyType = ((owningType != null) && owningType.IsOpen) && (edmProperty == null);

            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, propertyValue);
            }
            IEdmTypeReference metadataTypeReference = (edmProperty == null) ? null : edmProperty.Type;

            if (complexValue != null)
            {
                DuplicatePropertyNamesChecker checker = base.CreateDuplicatePropertyNamesChecker();
                if (isTopLevel)
                {
                    this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                    this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, null, null, checker, null, epmValueCache, propertySourcePathSegment, null);
                    this.WritePropertyEnd();
                    return(true);
                }
                if (beforeValueAction == null)
                {
                    beforeValueAction = delegate {
                        this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                    };
                }
                if (afterValueAction == null)
                {
                    afterValueAction = delegate {
                        this.WritePropertyEnd();
                    };
                }
                return(this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, beforeValueAction, afterValueAction, checker, null, epmValueCache, propertySourcePathSegment, emptyProjectedPropertiesMarker));
            }
            ODataCollectionValue collectionValue = propertyValue as ODataCollectionValue;

            if (collectionValue != null)
            {
                ODataVersionChecker.CheckCollectionValueProperties(base.Version, propertyName);
                this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                this.WriteCollectionValue(collectionValue, metadataTypeReference, isOpenPropertyType, isWritingCollection);
                this.WritePropertyEnd();
                return(true);
            }
            this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
            this.WritePrimitiveValue(propertyValue, null, metadataTypeReference);
            this.WritePropertyEnd();
            return(true);
        }
Example #18
0
        internal bool WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties)
        {
            Action action2  = null;
            string typeName = complexValue.TypeName;

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
            }
            this.IncreaseRecursionDepth();
            IEdmComplexTypeReference reference = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();

            if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0))
            {
                typeName = null;
            }
            SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>();

            if (annotation != null)
            {
                typeName = annotation.TypeName;
            }
            Action beforePropertiesAction = beforeValueAction;

            if (typeName != null)
            {
                if (beforeValueAction != null)
                {
                    if (action2 == null)
                    {
                        action2 = delegate {
                            beforeValueAction();
                            this.WritePropertyTypeAttribute(typeName);
                        };
                    }
                    beforePropertiesAction = action2;
                }
                else
                {
                    this.WritePropertyTypeAttribute(typeName);
                }
            }
            if (((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker))
            {
                IEdmComplexType definition = (IEdmComplexType)reference.Definition;
                if (base.Model.EpmCachedKeepPrimitiveInContent(definition) == null)
                {
                    List <string> keptInContentPropertyNames = null;
                    foreach (IEdmProperty property in from p in definition.Properties()
                             where p.Type.IsODataPrimitiveTypeKind()
                             select p)
                    {
                        EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, property.Name);
                        if ((entityPropertyMapping != null) && entityPropertyMapping.KeepInContent)
                        {
                            if (keptInContentPropertyNames == null)
                            {
                                keptInContentPropertyNames = new List <string>();
                            }
                            keptInContentPropertyNames.Add(property.Name);
                        }
                    }
                    base.Model.SetAnnotationValue <CachedPrimitiveKeepInContentAnnotation>(definition, new CachedPrimitiveKeepInContentAnnotation(keptInContentPropertyNames));
                }
            }
            bool flag = this.WriteProperties((reference == null) ? null : reference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforePropertiesAction, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties);

            this.DecreaseRecursionDepth();
            return(flag);
        }
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            string sourceName = epmInfo.Attribute.SourcePath;
            EpmSourcePathSegment currentProperty = this.Root;
            IList<EpmSourcePathSegment> activeSubProperties = currentProperty.SubProperties;
            EpmSourcePathSegment foundProperty = null;

            Debug.Assert(!string.IsNullOrEmpty(sourceName), "Must have been validated during EntityPropertyMappingAttribute construction");
            foreach (string propertyName in sourceName.Split('/'))
            {
                if (propertyName.Length == 0)
                {
                    throw new InvalidOperationException(Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, sourceName));
                }

                foundProperty = activeSubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundProperty != null)
                {
                    currentProperty = foundProperty;
                }
                else
                {
                    currentProperty = new EpmSourcePathSegment(propertyName);
                    activeSubProperties.Add(currentProperty);
                }

                activeSubProperties = currentProperty.SubProperties;
            }

            if (foundProperty != null)
            {
                Debug.Assert(object.ReferenceEquals(foundProperty, currentProperty), "currentProperty variable should have been updated already to foundProperty");

                if (foundProperty.EpmInfo.DefiningType.Name == epmInfo.DefiningType.Name)
                {
                    throw new InvalidOperationException(Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }

                this.epmTargetTree.Remove(foundProperty.EpmInfo);
            }

            currentProperty.EpmInfo = epmInfo;
            this.epmTargetTree.Add(epmInfo);
        }
Example #20
0
 /// <summary>Validates the specified segment and all its subsegments.</summary>
 /// <param name="pathSegment">The path segment to validate.</param>
 /// <param name="resourceType">The resource type of the property represented by this segment (null for open properties).</param>
 private static void Validate(EpmSourcePathSegment pathSegment, ClientTypeOrResourceType_Alias resourceType)
Example #21
0
        private void WriteEntryContent(ODataEntry entry, IEdmEntityType entryType, EntryPropertiesValueCache propertiesValueCache, EpmSourcePathSegment rootSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties)
        {
            ODataStreamReferenceValue mediaResource = entry.MediaResource;

            if (mediaResource == null)
            {
                this.atomOutputContext.XmlWriter.WriteStartElement("", "content", "http://www.w3.org/2005/Atom");
                this.atomOutputContext.XmlWriter.WriteAttributeString("type", "application/xml");
                this.atomEntryAndFeedSerializer.WriteProperties(entryType, propertiesValueCache.EntryProperties, false, new Action(this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart), new Action(this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd), base.DuplicatePropertyNamesChecker, propertiesValueCache, rootSourcePathSegment, projectedProperties);
                this.atomOutputContext.XmlWriter.WriteEndElement();
            }
            else
            {
                WriterValidationUtils.ValidateStreamReferenceValue(mediaResource, true);
                this.atomEntryAndFeedSerializer.WriteEntryMediaEditLink(mediaResource);
                if (mediaResource.ReadLink != null)
                {
                    this.atomOutputContext.XmlWriter.WriteStartElement("", "content", "http://www.w3.org/2005/Atom");
                    this.atomOutputContext.XmlWriter.WriteAttributeString("type", mediaResource.ContentType);
                    this.atomOutputContext.XmlWriter.WriteAttributeString("src", this.atomEntryAndFeedSerializer.UriToUrlAttributeValue(mediaResource.ReadLink));
                    this.atomOutputContext.XmlWriter.WriteEndElement();
                }
                this.atomEntryAndFeedSerializer.WriteProperties(entryType, propertiesValueCache.EntryProperties, false, new Action(this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart), new Action(this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd), base.DuplicatePropertyNamesChecker, propertiesValueCache, rootSourcePathSegment, projectedProperties);
            }
        }
 internal bool WriteProperties(IEdmStructuredType owningType, IEnumerable<ODataProperty> cachedProperties, bool isWritingCollection, Action beforePropertiesAction, Action afterPropertiesAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties)
 {
     if (cachedProperties == null)
     {
         return false;
     }
     bool flag = false;
     foreach (ODataProperty property in cachedProperties)
     {
         flag |= this.WriteProperty(property, owningType, false, isWritingCollection, flag ? null : beforePropertiesAction, epmValueCache, epmSourcePathSegment, duplicatePropertyNamesChecker, projectedProperties);
     }
     if ((afterPropertiesAction != null) && flag)
     {
         afterPropertiesAction();
     }
     return flag;
 }
 private bool WriteProperty(ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
 {
     Action beforeValueAction = null;
     Action afterValueAction = null;
     WriterValidationUtils.ValidatePropertyNotNull(property);
     object propertyValue = property.Value;
     string propertyName = property.Name;
     EpmSourcePathSegment propertySourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);
     ODataComplexValue complexValue = propertyValue as ODataComplexValue;
     ProjectedPropertiesAnnotation emptyProjectedPropertiesMarker = null;
     if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, propertyValue, propertySourcePathSegment))
     {
         if ((propertySourcePathSegment == null) || (complexValue == null))
         {
             return false;
         }
         emptyProjectedPropertiesMarker = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker;
     }
     WriterValidationUtils.ValidateProperty(property);
     duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
     IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);
     if (propertyValue is ODataStreamReferenceValue)
     {
         throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
     }
     if (((edmProperty != null) && edmProperty.Type.IsSpatial()) || ((edmProperty == null) && (propertyValue is ISpatial)))
     {
         ODataVersionChecker.CheckSpatialValue(base.Version);
     }
     if (propertyValue == null)
     {
         this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
         return true;
     }
     bool isOpenPropertyType = ((owningType != null) && owningType.IsOpen) && (edmProperty == null);
     if (isOpenPropertyType)
     {
         ValidationUtils.ValidateOpenPropertyValue(propertyName, propertyValue);
     }
     IEdmTypeReference metadataTypeReference = (edmProperty == null) ? null : edmProperty.Type;
     if (complexValue != null)
     {
         DuplicatePropertyNamesChecker checker = base.CreateDuplicatePropertyNamesChecker();
         if (isTopLevel)
         {
             this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
             this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, null, null, checker, null, epmValueCache, propertySourcePathSegment, null);
             this.WritePropertyEnd();
             return true;
         }
         if (beforeValueAction == null)
         {
             beforeValueAction = delegate {
                 this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
             };
         }
         if (afterValueAction == null)
         {
             afterValueAction = delegate {
                 this.WritePropertyEnd();
             };
         }
         return this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, beforeValueAction, afterValueAction, checker, null, epmValueCache, propertySourcePathSegment, emptyProjectedPropertiesMarker);
     }
     ODataCollectionValue collectionValue = propertyValue as ODataCollectionValue;
     if (collectionValue != null)
     {
         ODataVersionChecker.CheckCollectionValueProperties(base.Version, propertyName);
         this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
         this.WriteCollectionValue(collectionValue, metadataTypeReference, isOpenPropertyType, isWritingCollection);
         this.WritePropertyEnd();
         return true;
     }
     this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
     this.WritePrimitiveValue(propertyValue, null, metadataTypeReference);
     this.WritePropertyEnd();
     return true;
 }
Example #24
0
        internal static void CacheEpmProperties(EntryPropertiesValueCache propertyValueCache, EpmSourceTree sourceTree)
        {
            EpmSourcePathSegment root = sourceTree.Root;

            CacheEpmSourcePathSegments(propertyValueCache, root.SubProperties, propertyValueCache.EntryProperties);
        }
Example #25
0
        /// <summary>
        /// Adds a path to the source and target tree which is obtained by looking at the EntityPropertyMappingAttribute in the <paramref name="epmInfo"/>
        /// </summary>
        /// <param name="epmInfo">EnitityPropertyMappingInfo holding the source path</param>
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();

            List<EpmSourcePathSegment> pathToCurrentSegment = new List<EpmSourcePathSegment>();
            EpmSourcePathSegment currentSourceSegment = this.Root;
            EpmSourcePathSegment foundSourceSegment = null;
            IEdmType currentType = epmInfo.ActualPropertyType;

            Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.SourcePath), "Invalid source path");
            string[] propertyPath = epmInfo.Attribute.SourcePath.Split('/');

            int propertyPathLength = propertyPath.Length;
            Debug.Assert(propertyPathLength > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < propertyPathLength; sourcePropertyIndex++)
            {
                string propertyName = propertyPath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new ODataException(o.Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath));
                }

                IEdmTypeReference nextPropertyTypeReference = GetPropertyType(currentType, propertyName);
                IEdmType nextPropertyType = nextPropertyTypeReference == null ? null : nextPropertyTypeReference.Definition;

                // If we don't find a property type this is an open property; check whether this is the last segment in the path
                // since otherwise this would not be an open primitive property and only open primitive properties are allowed.
                if (nextPropertyType == null && sourcePropertyIndex < propertyPathLength - 1)
                {
                    throw new ODataException(o.Strings.EpmSourceTree_OpenComplexPropertyCannotBeMapped(propertyName, currentType.ODataFullName()));
                }

                currentType = nextPropertyType;
                foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundSourceSegment != null)
                {
                    currentSourceSegment = foundSourceSegment;
                }
                else
                {
                    EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName);
                    currentSourceSegment.SubProperties.Add(newSourceSegment);
                    currentSourceSegment = newSourceSegment;
                }

                pathToCurrentSegment.Add(currentSourceSegment);
            }

            // The last segment is the one being mapped from by the user specified attribute.
            // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly.
            // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type
            // and we will make this check later during serialization again when we actually have the value of the property.
            if (currentType != null)
            {
                if (!currentType.IsODataPrimitiveTypeKind())
                {
                    throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }
            }

            Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo.");

            // Two EpmAttributes with same PropertyName in the same type, this could be a result of inheritance
            if (foundSourceSegment != null)
            {
                Debug.Assert(object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment");

                // Check for duplicates on the same entity type
                Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                {
                    throw new ODataException(o.Strings.EpmSourceTree_DuplicateEpmAttributesWithSameSourceName(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath));
                }

                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);
            }

            epmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());
            currentSourceSegment.EpmInfo = epmInfo;

            this.epmTargetTree.Add(epmInfo);
        }
        /// <summary>
        /// Write the items in a MultiValue in ATOM format.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param>
        /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param>
        /// <param name="multiValue">The MultiValue to write.</param>
        /// <param name="resourcePropertyType">The resource type of the multi value (or null if not metadata is available).</param>
        /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param>
        /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param>
        /// <param name="version">The protocol version used for writing.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the multivalue property we're writing. (can be null)</param>
        private static void WriteMultiValue(
            XmlWriter writer,
            DataServiceMetadataProviderWrapper metadata,
            ODataMultiValue multiValue, 
            ResourceType resourcePropertyType,
            bool isOpenPropertyType,
            bool isWritingCollection,
            ODataVersion version,
            EpmValueCache epmValueCache, 
            EpmSourcePathSegment epmSourcePathSegment)
        {
            Debug.Assert(multiValue != null, "multiValue != null");

            string typeName = multiValue.TypeName;

            // resolve the type name to the resource type; if no type name is specified we will use the 
            // type inferred from metadata
            MultiValueResourceType multiValueType = (MultiValueResourceType)MetadataUtils.ResolveTypeName(metadata, resourcePropertyType, ref typeName, ResourceTypeKind.MultiValue, isOpenPropertyType);

            if (typeName != null)
            {
                WritePropertyTypeAttribute(writer, typeName);
            }

            ResourceType expectedItemType = multiValueType == null ? null : multiValueType.ItemType;

            IEnumerable items = EpmValueCache.GetMultiValueItems(epmValueCache, epmSourcePathSegment, multiValue, true);
            if (items != null)
            {
                foreach (object itemValue in items)
                {
                    object item;
                    EpmMultiValueItemCache epmItemCache = itemValue as EpmMultiValueItemCache;
                    if (epmItemCache != null)
                    {
                        item = epmItemCache.ItemValue;
                    }
                    else
                    {
                        item = itemValue;
                    }

                    ValidationUtils.ValidateMultiValueItem(item);

                    writer.WriteStartElement(AtomConstants.ODataNamespacePrefix, AtomConstants.ODataMultiValueItemElementName, AtomConstants.ODataNamespace);
                    ODataComplexValue complexValue = item as ODataComplexValue;
                    if (complexValue != null)
                    {
                        WriteComplexValue(writer, metadata, complexValue, expectedItemType, false, isWritingCollection, version, epmItemCache, epmSourcePathSegment);
                    }
                    else
                    {
                        ODataMultiValue multiValueItem = item as ODataMultiValue;
                        if (multiValueItem != null)
                        {
                            throw new ODataException(Strings.ODataWriter_NestedMultiValuesAreNotSupported);
                        }
                        else
                        {
                            AtomValueUtils.WritePrimitiveValue(writer, item, expectedItemType);
                        }
                    }

                    writer.WriteEndElement();
                }
            }
        }
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The type owning the property (or null if no metadata is available).</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a collection instead of an entry.</param>
        /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if the property was actually written, false otherwise.</returns>
        private bool WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforePropertyAction,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmParentSourcePathSegment,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);

            object value = property.Value;
            string propertyName = property.Name;
            EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);

            //// TODO: If we implement type conversions the value needs to be converted here
            ////       since the next method call needs to know if the value is a string or not in some cases.

            ODataComplexValue complexValue = value as ODataComplexValue;
            ProjectedPropertiesAnnotation complexValueProjectedProperties = null;
            if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment))
            {
                // If ShouldWritePropertyInContent returns false for a comlex value we have to continue
                // writing the property but set the projectedProperties to an empty array. The reason for this
                // is that we might find EPM on a nested property that has a null value and thus must be written 
                // in content (in which case the parent property also has to be written).
                // This only applies if we have EPM information for the property.
                if (epmSourcePathSegment != null && complexValue != null)
                {
                    Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property.");
                    complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker;
                }
                else
                {
                    return false;
                }
            }

            WriterValidationUtils.ValidateProperty(property);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);

            if (value is ODataStreamReferenceValue)
            {
                throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }

            // If the property is of Geography or Geometry type or the value is of Geography or Geometry type
            // make sure to check that the version is 3.0 or above.
            if ((edmProperty != null && edmProperty.Type.IsSpatial()) ||
                (edmProperty == null && value is System.Spatial.ISpatial))
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            // Null property value.
            if (value == null)
            {
                this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return true;
            }

            bool isOpenPropertyType = owningType != null && owningType.IsOpen && edmProperty == null;
            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value);
            }

            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;
            if (complexValue != null)
            {
                // Complex properties are written recursively.
                DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                if (isTopLevel)
                {
                    // Top-level property must always write the property element
                    Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null");
                    this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                    this.AssertRecursionDepthIsZero();
                    this.WriteComplexValue(
                        complexValue,
                        propertyTypeReference,
                        isOpenPropertyType,
                        isWritingCollection,
                        null  /* beforeValueAction */,
                        null  /* afterValueAction */,
                        complexValuePropertyNamesChecker,
                        null /* collectionValidator */,
                        epmValueCache,
                        epmSourcePathSegment,
                        null  /* projectedProperties */);
                    this.AssertRecursionDepthIsZero();
                    this.WritePropertyEnd();
                    return true;
                }

                return this.WriteComplexValue(
                    complexValue,
                    propertyTypeReference,
                    isOpenPropertyType,
                    isWritingCollection,
                    () => this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel),
                    () => this.WritePropertyEnd(),
                    complexValuePropertyNamesChecker,
                    null /* collectionValidator */,
                    epmValueCache,
                    epmSourcePathSegment,
                    complexValueProjectedProperties);
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;
            if (collectionValue != null)
            {
                ODataVersionChecker.CheckCollectionValueProperties(this.Version, propertyName);

                this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                this.WriteCollectionValue(
                    collectionValue,
                    propertyTypeReference,
                    isOpenPropertyType,
                    isWritingCollection);
                this.WritePropertyEnd();
                return true;
            }

            this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
            this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference);
            this.WritePropertyEnd();
            return true;
        }
Example #28
0
 /// <summary>Default constructor creates a null root</summary>
 /// <param name="epmTargetTree">Target xml tree</param>
 internal EpmSourceTree(EpmTargetTree epmTargetTree)
 {
     this.root          = new EpmSourcePathSegment();
     this.epmTargetTree = epmTargetTree;
 }
Example #29
0
        /// <summary>
        /// Validates the specified segment and all its subsegments.
        /// </summary>
        /// <param name="pathSegment">The path segment to validate.</param>
        /// <param name="type">The type of the property represented by this segment (null for open properties).</param>
        private static void Validate(EpmSourcePathSegment pathSegment, IEdmType type)
        {
            Debug.Assert(pathSegment != null, "pathSegment != null");

            foreach (EpmSourcePathSegment subSegment in pathSegment.SubProperties)
            {
                IEdmTypeReference subSegmentTypeReference = GetPropertyType(type, subSegment.PropertyName);
                IEdmType subSegmentType = subSegmentTypeReference == null ? null : subSegmentTypeReference.Definition;

                Validate(subSegment, subSegmentType);
            }
        }
Example #30
0
        internal void Add(EntityPropertyMappingInfo epmInfo, IEnumerable <ResourceProperty> declaredProperties)
        {
            Dictionary <ResourceType, IEnumerable <ResourceProperty> > declaredPropertiesLookup = new Dictionary <ResourceType, IEnumerable <ResourceProperty> >(EqualityComparer <ResourceType> .Default);

            declaredPropertiesLookup.Add(epmInfo.ActualPropertyType, declaredProperties);
#endif
            EpmSourcePathSegment           currentSourceSegment = this.Root;
            EpmSourcePathSegment           foundSourceSegment   = null;
            ClientTypeOrResourceType_Alias currentType          = epmInfo.ActualPropertyType;

            Debug.Assert(epmInfo.PropertyValuePath != null && epmInfo.PropertyValuePath.Length > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < epmInfo.PropertyValuePath.Length; sourcePropertyIndex++)
            {
                string propertyName = epmInfo.PropertyValuePath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new InvalidOperationException(c.Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, epmInfo.Attribute.SourcePath));
                }

#if ASTORIA_CLIENT
                currentType = GetPropertyType(currentType, propertyName);
#else
                currentType = GetPropertyType(currentType, propertyName, declaredPropertiesLookup);
#endif

                foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundSourceSegment != null)
                {
                    currentSourceSegment = foundSourceSegment;
                }
                else
                {
                    EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName);
                    currentSourceSegment.SubProperties.Add(newSourceSegment);
                    currentSourceSegment = newSourceSegment;
                }
            }

            // The last segment is the one being mapped from by the user specified attribute.
            // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly.
            // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type
            //   and we will make this check later during serialization again when we actually have the value of the property.
            if (currentType != null)
            {
#if ASTORIA_CLIENT
                if (!PrimitiveType.IsKnownNullableType(currentType.ElementType))
#else
                if (currentType.ResourceTypeKind != ResourceTypeKind.Primitive)
#endif
                {
                    throw new InvalidOperationException(c.Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }
            }

            // Note that once we're here the EpmInfo we have is never the collection property itself, it's always either a non-collection property
            //   or collection item property.
            Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo.");

            // Two EpmAttributes with same PropertyName in the same ResourceType, this could be a result of inheritance
            if (foundSourceSegment != null)
            {
                Debug.Assert(Object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment");

                // Check for duplicates on the same entity type
                Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                {
                    throw new InvalidOperationException(c.Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }

                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);
            }

            currentSourceSegment.EpmInfo = epmInfo;

            this.epmTargetTree.Add(epmInfo);
        }
Example #31
0
        internal bool WriteProperties(IEdmStructuredType owningType, IEnumerable <ODataProperty> cachedProperties, bool isWritingCollection, Action beforePropertiesAction, Action afterPropertiesAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties)
        {
            if (cachedProperties == null)
            {
                return(false);
            }
            bool flag = false;

            foreach (ODataProperty property in cachedProperties)
            {
                flag |= this.WriteProperty(property, owningType, false, isWritingCollection, flag ? null : beforePropertiesAction, epmValueCache, epmSourcePathSegment, duplicatePropertyNamesChecker, projectedProperties);
            }
            if ((afterPropertiesAction != null) && flag)
            {
                afterPropertiesAction();
            }
            return(flag);
        }
Example #32
0
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The type owning the property (or null if no metadata is available).</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a collection instead of an entry.</param>
        /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if the property was actually written, false otherwise.</returns>
        private bool WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforePropertyAction,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmParentSourcePathSegment,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);

            object value        = property.Value;
            string propertyName = property.Name;
            EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);

            //// TODO: If we implement type conversions the value needs to be converted here
            ////       since the next method call needs to know if the value is a string or not in some cases.

            ODataComplexValue             complexValue = value as ODataComplexValue;
            ProjectedPropertiesAnnotation complexValueProjectedProperties = null;

            if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment))
            {
                // If ShouldWritePropertyInContent returns false for a comlex value we have to continue
                // writing the property but set the projectedProperties to an empty array. The reason for this
                // is that we might find EPM on a nested property that has a null value and thus must be written
                // in content (in which case the parent property also has to be written).
                // This only applies if we have EPM information for the property.
                if (epmSourcePathSegment != null && complexValue != null)
                {
                    Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property.");
                    complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker;
                }
                else
                {
                    return(false);
                }
            }

            WriterValidationUtils.ValidateProperty(property);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);

            if (value is ODataStreamReferenceValue)
            {
                throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }

            // If the property is of Geography or Geometry type or the value is of Geography or Geometry type
            // make sure to check that the version is 3.0 or above.
            if ((edmProperty != null && edmProperty.Type.IsSpatial()) ||
                (edmProperty == null && value is System.Spatial.ISpatial))
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            // Null property value.
            if (value == null)
            {
                this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return(true);
            }

            bool isOpenPropertyType = owningType != null && owningType.IsOpen && edmProperty == null;

            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value);
            }

            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            if (complexValue != null)
            {
                // Complex properties are written recursively.
                DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                if (isTopLevel)
                {
                    // Top-level property must always write the property element
                    Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null");
                    this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                    this.AssertRecursionDepthIsZero();
                    this.WriteComplexValue(
                        complexValue,
                        propertyTypeReference,
                        isOpenPropertyType,
                        isWritingCollection,
                        null /* beforeValueAction */,
                        null /* afterValueAction */,
                        complexValuePropertyNamesChecker,
                        null /* collectionValidator */,
                        epmValueCache,
                        epmSourcePathSegment,
                        null /* projectedProperties */);
                    this.AssertRecursionDepthIsZero();
                    this.WritePropertyEnd();
                    return(true);
                }

                return(this.WriteComplexValue(
                           complexValue,
                           propertyTypeReference,
                           isOpenPropertyType,
                           isWritingCollection,
                           () => this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel),
                           () => this.WritePropertyEnd(),
                           complexValuePropertyNamesChecker,
                           null /* collectionValidator */,
                           epmValueCache,
                           epmSourcePathSegment,
                           complexValueProjectedProperties));
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                ODataVersionChecker.CheckCollectionValueProperties(this.Version, propertyName);

                this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                this.WriteCollectionValue(
                    collectionValue,
                    propertyTypeReference,
                    isOpenPropertyType,
                    isWritingCollection);
                this.WritePropertyEnd();
                return(true);
            }

            this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
            this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference);
            this.WritePropertyEnd();
            return(true);
        }
Example #33
0
        private bool ShouldWritePropertyInContent(IEdmStructuredType owningType, ProjectedPropertiesAnnotation projectedProperties, string propertyName, object propertyValue, EpmSourcePathSegment epmSourcePathSegment)
        {
            bool flag = !projectedProperties.ShouldSkipProperty(propertyName);

            if ((((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && (owningType != null)) && owningType.IsODataComplexTypeKind())
            {
                IEdmComplexType complexType = (IEdmComplexType)owningType;
                CachedPrimitiveKeepInContentAnnotation annotation = base.Model.EpmCachedKeepPrimitiveInContent(complexType);
                if ((annotation != null) && annotation.IsKeptInContent(propertyName))
                {
                    return(flag);
                }
            }
            if ((propertyValue == null) && (epmSourcePathSegment != null))
            {
                return(true);
            }
            EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment);

            if (entityPropertyMapping == null)
            {
                return(flag);
            }
            string str = propertyValue as string;

            if ((str != null) && (str.Length == 0))
            {
                switch (entityPropertyMapping.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorEmail:
                case SyndicationItemProperty.ContributorUri:
                    return(true);
                }
            }
            return(entityPropertyMapping.KeepInContent && flag);
        }
		/// <summary>
		/// Sets path to the source property.
		/// </summary>
		/// <param name="path">The path as an array of source path segments.</param>
		internal void SetPropertyValuePath(EpmSourcePathSegment[] path)
		{
			DebugUtils.CheckNoExternalCallers();
			Debug.Assert(path != null, "path != null");
			Debug.Assert(
				this.multiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty || path.Length > 0,
				"The path must contain at least one segment unless it's a multivalue item path.");
			Debug.Assert(this.propertyValuePath == null, "The property value path was already initialized.");

			this.propertyValuePath = path;
		}
Example #35
0
        /// <summary>
        /// Validates the specified segment and all its subsegments.
        /// </summary>
        /// <param name="pathSegment">The path segment to validate.</param>
        /// <param name="resourceType">The resource type of the property represented by this segment (null for open properties).</param>
        private static void Validate(EpmSourcePathSegment pathSegment, ResourceType resourceType)
        {
            Debug.Assert(pathSegment != null, "pathSegment != null");

            foreach (EpmSourcePathSegment subSegment in pathSegment.SubProperties)
            {
                bool isMultiValueProperty;
                ResourceType subSegmentResourceType = GetPropertyType(resourceType, subSegment.PropertyName, out isMultiValueProperty);

                if (isMultiValueProperty)
                {
                    ValidateMultiValueSegment(subSegment.EpmInfo, subSegment, subSegmentResourceType);
                }
                else
                {
                    Validate(subSegment, subSegmentResourceType);
                }
            }
        }
 internal bool WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties)
 {
     Action action2 = null;
     string typeName = complexValue.TypeName;
     if (collectionValidator != null)
     {
         collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
     }
     this.IncreaseRecursionDepth();
     IEdmComplexTypeReference reference = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();
     if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0))
     {
         typeName = null;
     }
     SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>();
     if (annotation != null)
     {
         typeName = annotation.TypeName;
     }
     Action beforePropertiesAction = beforeValueAction;
     if (typeName != null)
     {
         if (beforeValueAction != null)
         {
             if (action2 == null)
             {
                 action2 = delegate {
                     beforeValueAction();
                     this.WritePropertyTypeAttribute(typeName);
                 };
             }
             beforePropertiesAction = action2;
         }
         else
         {
             this.WritePropertyTypeAttribute(typeName);
         }
     }
     if (((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker))
     {
         IEdmComplexType definition = (IEdmComplexType) reference.Definition;
         if (base.Model.EpmCachedKeepPrimitiveInContent(definition) == null)
         {
             List<string> keptInContentPropertyNames = null;
             foreach (IEdmProperty property in from p in definition.Properties()
                 where p.Type.IsODataPrimitiveTypeKind()
                 select p)
             {
                 EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, property.Name);
                 if ((entityPropertyMapping != null) && entityPropertyMapping.KeepInContent)
                 {
                     if (keptInContentPropertyNames == null)
                     {
                         keptInContentPropertyNames = new List<string>();
                     }
                     keptInContentPropertyNames.Add(property.Name);
                 }
             }
             base.Model.SetAnnotationValue<CachedPrimitiveKeepInContentAnnotation>(definition, new CachedPrimitiveKeepInContentAnnotation(keptInContentPropertyNames));
         }
     }
     bool flag = this.WriteProperties((reference == null) ? null : reference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforePropertiesAction, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties);
     this.DecreaseRecursionDepth();
     return flag;
 }
        /// <summary>
        /// Determines if the property with the specified value should be written into content or not.
        /// </summary>
        /// <param name="owningType">The owning type of the property to be checked.</param>
        /// <param name="projectedProperties">The set of projected properties for the <paramref name="owningType"/></param>
        /// <param name="propertyName">The name of the property to be checked.</param>
        /// <param name="propertyValue">The property value to write.</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment for the property being written.</param>
        /// <returns>true if the property should be written into content, or false otherwise</returns>
        private bool ShouldWritePropertyInContent(
            IEdmStructuredType owningType,
            ProjectedPropertiesAnnotation projectedProperties,
            string propertyName,
            object propertyValue,
            EpmSourcePathSegment epmSourcePathSegment)
        {
            // check whether the property is projected; if no EPM is specified for the property the projection decides 
            bool propertyProjected = !projectedProperties.ShouldSkipProperty(propertyName);

            bool useV1ProviderBehavior = this.MessageWriterSettings.WriterBehavior == null ? false : this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior;
            if (useV1ProviderBehavior && owningType != null && owningType.IsODataComplexTypeKind())
            {
                IEdmComplexType owningComplexType = (IEdmComplexType)owningType;
                CachedPrimitiveKeepInContentAnnotation keepInContentAnnotation = this.Model.EpmCachedKeepPrimitiveInContent(owningComplexType);
                if (keepInContentAnnotation != null && keepInContentAnnotation.IsKeptInContent(propertyName))
                {
                    return propertyProjected;
                }
            }

            // We sometimes write properties into content even if asked not to.
            // If the property value is null and the property (or one of its descendant properties) is mapped, 
            // we always write into content, even if the property was not projected.
            if (propertyValue == null && epmSourcePathSegment != null)
            {
                return true;
            }

            EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment);
            if (entityPropertyMapping == null)
            {
                return propertyProjected;
            }

            string stringPropertyValue = propertyValue as string;
            if (stringPropertyValue != null && stringPropertyValue.Length == 0)
            {
                // If the property value is an empty string and we should be writing it into an ATOM element which does not allow empty string
                // we write it into content as well, also even if the property was not projected.
                switch (entityPropertyMapping.TargetSyndicationItem)
                {
                    case SyndicationItemProperty.AuthorEmail:
                    case SyndicationItemProperty.AuthorUri:
                    case SyndicationItemProperty.ContributorEmail:
                    case SyndicationItemProperty.ContributorUri:
                        return true;

                    default:
                        break;
                }
            }

            return entityPropertyMapping.KeepInContent && propertyProjected;
        }
Example #38
0
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The owning type for the <paramref name="property"/> or null if no metadata is available.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if the property was actually written, false otherwise.</returns>
        private bool WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforePropertyAction,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmParentSourcePathSegment,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);

            object value        = property.Value;
            string propertyName = property.Name;
            EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);

            //// TODO: If we implement type conversions the value needs to be converted here
            ////       since the next method call needs to know if the value is a string or not in some cases.

            ODataComplexValue             complexValue = value as ODataComplexValue;
            ProjectedPropertiesAnnotation complexValueProjectedProperties = null;

            if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment))
            {
                // If ShouldWritePropertyInContent returns false for a comlex value we have to continue
                // writing the property but set the projectedProperties to an empty array. The reason for this
                // is that we might find EPM on a nested property that has a null value and thus must be written
                // in content (in which case the parent property also has to be written).
                // This only applies if we have EPM information for the property.
                if (epmSourcePathSegment != null && complexValue != null)
                {
                    Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property.");
                    complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance;
                }
                else
                {
                    return(false);
                }
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty      edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            if (value is ODataStreamReferenceValue)
            {
                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }

            // If the property is of Geography or Geometry type or the value is of Geography or Geometry type
            // make sure to check that the version is 3.0 or above.
            if ((propertyTypeReference != null && propertyTypeReference.IsSpatial()) ||
                (propertyTypeReference == null && value is System.Spatial.ISpatial))
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            // Null property value.
            if (value == null)
            {
                this.WriteNullPropertyValue(propertyTypeReference, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return(true);
            }

            bool isOpenPropertyType = owningType != null && owningType.IsOpen && propertyTypeReference == null;

            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            }

            if (complexValue != null)
            {
                return(this.WriteComplexValueProperty(
                           complexValue,
                           propertyName,
                           isTopLevel,
                           isWritingCollection,
                           beforePropertyAction,
                           epmValueCache,
                           propertyTypeReference,
                           isOpenPropertyType,
                           epmSourcePathSegment,
                           complexValueProjectedProperties));
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                this.WriteCollectionValueProperty(
                    collectionValue,
                    propertyName,
                    isTopLevel,
                    isWritingCollection,
                    beforePropertyAction,
                    propertyTypeReference,
                    isOpenPropertyType);

                return(true);
            }

            // If the value isn't one of the value types tested for already, it must be a non-null primitive.
            this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = property.ODataValue.GetAnnotation <SerializationTypeNameAnnotation>();

            this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation);
            this.WritePropertyEnd();
            return(true);
        }
        /// <summary>
        /// Write the given collection of properties.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param>
        /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param>
        /// <param name="owningType">The <see cref="ResourceType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="cachedProperties">Collection of cached properties for the entry.</param>
        /// <param name="version">The protocol version used for writing.</param>
        /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property which sub-properites we're writing. (can be null)</param>
        internal static void WriteProperties(
            XmlWriter writer,
            DataServiceMetadataProviderWrapper metadata,
            ResourceType owningType,
            IEnumerable<ODataProperty> cachedProperties, 
            ODataVersion version, 
            bool isWritingCollection,
            EpmValueCache epmValueCache, 
            EpmSourcePathSegment epmSourcePathSegment)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(writer != null, "writer != null");

            if (cachedProperties == null)
            {
                return;
            }

            foreach (ODataProperty property in cachedProperties)
            {
                WriteProperty(writer, metadata, property, owningType, version, false, isWritingCollection, epmValueCache, epmSourcePathSegment);
            }
        }
        /// <summary>
        /// Write the given collection of properties.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="cachedProperties">Collection of cached properties for the entry.</param>
        /// <param name="isWritingCollection">true if we are writing a collection instead of an entry.</param>
        /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param>
        /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property which sub-properites we're writing. (can be null)</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        internal bool WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable<ODataProperty> cachedProperties,
            bool isWritingCollection,
            Action beforePropertiesAction,
            Action afterPropertiesAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            if (cachedProperties == null)
            {
                return false;
            }

            bool propertyWritten = false;
            foreach (ODataProperty property in cachedProperties)
            {
                propertyWritten |= this.WriteProperty(
                    property,
                    owningType,
                    false,
                    isWritingCollection,
                    propertyWritten ? null : beforePropertiesAction,
                    epmValueCache,
                    epmSourcePathSegment,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }

            if (afterPropertiesAction != null && propertyWritten)
            {
                afterPropertiesAction();
            }

            return propertyWritten;
        }
        /// <summary>
        /// Writes out the value of a complex property.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param>
        /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param>
        /// <param name="complexValue">The complex value to write.</param>
        /// <param name="metadataType">The metadata type for the complex value.</param>
        /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param>
        /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param>
        /// <param name="version">The protocol version used for writing.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property we're writing. (can be null)</param>
        internal static void WriteComplexValue(
            XmlWriter writer,
            DataServiceMetadataProviderWrapper metadata,
            ODataComplexValue complexValue,
            ResourceType metadataType,
            bool isOpenPropertyType,
            bool isWritingCollection,
            ODataVersion version,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            string typeName = complexValue.TypeName;

            // resolve the type name to the resource type; if no type name is specified we will use the 
            // type inferred from metadata
            ResourceType complexValueType = MetadataUtils.ResolveTypeName(metadata, metadataType, ref typeName, ResourceTypeKind.ComplexType, isOpenPropertyType);

            if (typeName != null)
            {
                WritePropertyTypeAttribute(writer, typeName);
            }

            WriteProperties(
                writer,
                metadata,
                complexValueType,
                EpmValueCache.GetComplexValueProperties(epmValueCache, epmSourcePathSegment, complexValue, true),
                version,
                isWritingCollection,
                epmValueCache,
                epmSourcePathSegment);
        }
 internal EpmSourceTree(EpmTargetTree epmTargetTree)
 {
     this.root = new EpmSourcePathSegment(string.Empty);
     this.epmTargetTree = epmTargetTree;
 }
        /// <summary>
        /// Determines if the property with the specified value should be written into content or not.
        /// </summary>
        /// <param name="propertyValue">The property value to write.</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment for the property being written.</param>
        /// <param name="version">The version of the protocol being used for the response.</param>
        /// <returns>true if the property should be written into content, or false otherwise</returns>
        private static bool ShouldWritePropertyInContent(object propertyValue, EpmSourcePathSegment epmSourcePathSegment, ODataVersion version)
        {
            if (epmSourcePathSegment == null)
            {
                return true;
            }

            EntityPropertyMappingInfo epmInfo = epmSourcePathSegment.EpmInfo;
            if (epmInfo == null)
            {
                return true;
            }

            EntityPropertyMappingAttribute epmAttribute = epmInfo.Attribute;
            Debug.Assert(epmAttribute != null, "Attribute should always be initialized for EpmInfo.");
            if (version <= ODataVersion.V2)
            {
                // In V2 and lower we sometimes write properties into content even if asked not to.
                // If the property value is null, we always write into content
                if (propertyValue == null)
                {
                    return true;
                }

                string stringPropertyValue = propertyValue as string;
                if (stringPropertyValue != null && stringPropertyValue.Length == 0)
                {
                    // If the property value is an empty string and we should be writing it into an ATOM element which does not allow empty string
                    // we write it into content as well.
                    switch (epmAttribute.TargetSyndicationItem)
                    {
                        case SyndicationItemProperty.AuthorEmail:
                        case SyndicationItemProperty.AuthorUri:
                        case SyndicationItemProperty.ContributorEmail:
                        case SyndicationItemProperty.ContributorUri:
                            return true;

                        default:
                            break;
                    }
                }
            }

            return epmAttribute.KeepInContent;
        }
        /// <summary>Writes the value of a complex object.</summary>
        /// <param name="element">Element to write.</param>
        /// <param name="propertyName">name of the property whose value needs to be written</param>
        /// <param name="expectedType">expected type of the property</param>
        /// <param name="relativeUri">relative uri for the complex type element</param>
        /// <param name="content">Content to write to.</param>
        /// <param name="currentSourceRoot">Epm source sub-tree corresponding to <paramref name="element"/></param>
        private void WriteComplexObjectValue(object element, string propertyName, ResourceType expectedType, string relativeUri, DictionaryContent content, EpmSourcePathSegment currentSourceRoot)
        {
            Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)");
            Debug.Assert(expectedType != null, "expectedType != null");
            Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)");
            Debug.Assert(expectedType.ResourceTypeKind == ResourceTypeKind.ComplexType, "Must be complex type");
            Debug.Assert(content != null, "content != null");

            // Non-value complex types may form a cycle.
            // PERF: we can keep a single element around and save the HashSet initialization
            // until we find a second complex type - this saves the allocation on trees
            // with shallow (single-level) complex types.
            Debug.Assert(!expectedType.IsMediaLinkEntry, "!expectedType.IsMediaLinkEntry");
            DictionaryContent valueProperties = new DictionaryContent(expectedType.Properties.Count);
            Debug.Assert(!expectedType.InstanceType.IsValueType, "!expectedType.Type.IsValueType -- checked in the resource type constructor.");

            if (element == null)
            {
                content.AddNull(expectedType.FullName, propertyName);
            }
            else
            {
                if (this.AddToComplexTypeCollection(element))
                {
                    ResourceType resourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);
                    this.WriteObjectProperties(null, element, resourceType, null, relativeUri, null, valueProperties, currentSourceRoot);
                    if (!valueProperties.IsEmpty)
                    {
                        content.Add(propertyName, resourceType.FullName, valueProperties);
                    }

                    this.RemoveFromComplexTypeCollection(element);
                }
                else
                {
                    throw new InvalidOperationException(Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName));
                }
            }
        }
        internal bool WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection,
            Action beforeValueAction,
            Action afterValueAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            string typeName = complexValue.TypeName;

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
            }

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the 
            // type inferred from metadata
            IEdmComplexTypeReference complexTypeReference =
                WriterValidationUtils.ResolveTypeNameForWriting(this.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();

            // If the type is the same as the one specified by the parent collection, omit the type name, since it's not needed.
            if (typeName != null && collectionValidator != null)
            {
                string expectedItemTypeName = collectionValidator.ItemTypeNameFromCollection;
                if (string.CompareOrdinal(expectedItemTypeName, typeName) == 0)
                {
                    typeName = null;
                }
            }

            SerializationTypeNameAnnotation serializationTypeNameAnnotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>();
            if (serializationTypeNameAnnotation != null)
            {
                typeName = serializationTypeNameAnnotation.TypeName;
            }

            Action beforeValueCallbackWithTypeName = beforeValueAction;
            if (typeName != null)
            {
                // The beforeValueAction (if specified) will write the actual property element start.
                // So if we are to write the type attribute, we must postpone that after the start element was written.
                // And so we chain the existing action with our type attribute writing and use that
                // as the before action instead.
                if (beforeValueAction != null)
                {
                    beforeValueCallbackWithTypeName = () =>
                    {
                        beforeValueAction();
                        this.WritePropertyTypeAttribute(typeName);
                    };
                }
                else
                {
                    this.WritePropertyTypeAttribute(typeName);
                }
            }

            // NOTE: see the comments on ODataWriterBehavior.UseV1ProviderBehavior for more information
            // NOTE: We have to check for ProjectedPropertiesAnnotation.Empty here to avoid filling the cache for
            //       complex values we are writing only to ensure we don't have nested EPM-mapped null values 
            //       that will end up in the content eventually.
            if (this.MessageWriterSettings.WriterBehavior != null &&
                this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior &&
                !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker))
            {
                IEdmComplexType complexType = (IEdmComplexType)complexTypeReference.Definition;
                CachedPrimitiveKeepInContentAnnotation keepInContentCache = this.Model.EpmCachedKeepPrimitiveInContent(complexType);
                if (keepInContentCache == null)
                {
                    // we are about to write the first value of the given type; compute the keep-in-content information for the primitive properties of this type.
                    List<string> keepInContentPrimitiveProperties = null;

                    // initialize the cache with all primitive properties
                    foreach (IEdmProperty edmProperty in complexType.Properties().Where(p => p.Type.IsODataPrimitiveTypeKind()))
                    {
                        // figure out the keep-in-content value
                        EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, edmProperty.Name);
                        if (entityPropertyMapping != null && entityPropertyMapping.KeepInContent)
                        {
                            if (keepInContentPrimitiveProperties == null)
                            {
                                keepInContentPrimitiveProperties = new List<string>();
                            }

                            keepInContentPrimitiveProperties.Add(edmProperty.Name);
                        }
                    }

                    this.Model.SetAnnotationValue<CachedPrimitiveKeepInContentAnnotation>(complexType, new CachedPrimitiveKeepInContentAnnotation(keepInContentPrimitiveProperties));
                }
            }

            bool propertyWritten = this.WriteProperties(
                complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(),
                EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true),
                isWritingCollection,
                beforeValueCallbackWithTypeName,
                afterValueAction,
                duplicatePropertyNamesChecker,
                epmValueCache,
                epmSourcePathSegment,
                projectedProperties);

            this.DecreaseRecursionDepth();
            return propertyWritten;
        }
        /// <summary>Writes all the properties of the specified resource or complex object.</summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="customObject">Resource or complex object with properties to write out.</param>
        /// <param name="resourceType">resourceType containing metadata about the current custom object</param>
        /// <param name="absoluteUri">absolute uri for the given resource</param>
        /// <param name="relativeUri">relative uri for the given resource</param>
        /// <param name="item">Item in which to place links / expansions.</param>
        /// <param name="content">Content in which to place values.</param>
        /// <param name="currentSourceRoot">Epm source sub-tree corresponding to <paramref name="customObject"/></param>
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri absoluteUri, string relativeUri, SyndicationItem item, DictionaryContent content, EpmSourcePathSegment currentSourceRoot)
        {
            Debug.Assert(customObject != null, "customObject != null");
            Debug.Assert(resourceType != null, "resourceType != null");

            Debug.Assert(!String.IsNullOrEmpty(relativeUri), "!String.IsNullOrEmpty(relativeUri)");
            
            if (absoluteUri == null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            {
                // entity type should have an URI, complex type should not have an URI
                // If the static type of the object is "Object", we will mistreat an entity type as complex type and hit this situation
                throw new DataServiceException(500, Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(resourceType.Name));
            }

            this.RecurseEnter();
            try
            {
                List<ResourcePropertyInfo> navProperties = null;
                IEnumerable<ProjectionNode> projectionNodes = null;
                if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                {
                    Debug.Assert(this.CurrentContainer != null, "this.CurrentContainer != null");
                    if (this.Provider.IsEntityTypeDisallowedForSet(this.CurrentContainer, resourceType))
                    {
                        throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name));
                    }

                    navProperties = new List<ResourcePropertyInfo>(resourceType.Properties.Count);

                    projectionNodes = this.GetProjections();
                }

                if (projectionNodes == null)
                {
                    var action = resourceType.DictionarySerializerDelegate;
                    if (action == null && this.Provider.IsV1Provider)
                    {
                        Module module = typeof(SyndicationSerializer).Module;
                        Type customObjectType = customObject.GetType();
                        Type[] parameterTypes = new Type[] { typeof(object), typeof(DictionaryContent) };
                        DynamicMethod method = new DynamicMethod("content_populator", typeof(void), parameterTypes, module, false /* skipVisibility */);
                        ILGenerator generator = method.GetILGenerator();
                        MethodInfo methodWritePrimitiveValue = typeof(SyndicationSerializer).GetMethod("WritePrimitiveValue", BindingFlags.Static | BindingFlags.NonPublic);

                        // Downcast the argument.
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Castclass, customObjectType);

                        foreach (ResourceProperty property in resourceType.Properties.Where(p => p.TypeKind == ResourceTypeKind.Primitive))
                        {
                            if (SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, property.Name))
                            {
                                continue;
                            }

                            // WritePrimitiveValue(propertyValue, property.Name, property.ResourceType, content);
                            generator.Emit(OpCodes.Dup);
                            generator.Emit(OpCodes.Call, resourceType.GetPropertyInfo(property).GetGetMethod());
                            if (property.Type.IsValueType)
                            {
                                generator.Emit(OpCodes.Box, property.Type);
                            }

                            generator.Emit(OpCodes.Ldstr, property.Name);
                            generator.Emit(OpCodes.Ldstr, property.ResourceType.FullName);
                            generator.Emit(OpCodes.Ldarg_1);
                            generator.Emit(OpCodes.Call, methodWritePrimitiveValue);
                        }

                        generator.Emit(OpCodes.Pop);
                        generator.Emit(OpCodes.Ret);
                        action = (Action<object, DictionaryContent>)method.CreateDelegate(typeof(Action<object, DictionaryContent>), null);
                        resourceType.DictionarySerializerDelegate = action;
                    }

                    if (action != null)
                    {
                        action(customObject, content);
                    }
                    else
                    {
                        foreach (ResourceProperty property in resourceType.Properties.Where(p => p.TypeKind == ResourceTypeKind.Primitive))
                        {
                            object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, null);
                            if (SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, property.Name))
                            {
                                continue;
                            }

                            WritePrimitiveValue(propertyValue, property.Name, property.ResourceType.FullName, content);
                        }
                    }

                    foreach (ResourceProperty property in this.Provider.GetResourceProperties(this.CurrentContainer, resourceType))
                    {
                        string propertyName = property.Name;
                        if (property.TypeKind == ResourceTypeKind.EntityType)
                        {
                            Debug.Assert(navProperties != null, "navProperties list must be assigned for entity types");

                            object propertyValue =
                                (this.ShouldExpandSegment(property.Name)) ? GetExpandedProperty(this.Provider, expanded, customObject, property) : null;
                            navProperties.Add(new ResourcePropertyInfo(property, propertyValue));
                        }
                        else
                        {
                            if (property.TypeKind == ResourceTypeKind.ComplexType)
                            {
                                object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, null);
                                bool needPop = this.PushSegmentForProperty(property);
                                this.WriteComplexObjectValue(
                                        propertyValue,
                                        propertyName,
                                        property.ResourceType,
                                        relativeUri + "/" + property.Name,
                                        content,
                                        SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, property.Name));
                                this.PopSegmentName(needPop);
                            }
                        }
                    }

                    if (resourceType.IsOpenType)
                    {
                        IEnumerable<KeyValuePair<string, object>> properties = this.Provider.GetOpenPropertyValues(customObject);
                        foreach (KeyValuePair<string, object> property in properties)
                        {
                            string propertyName = property.Key;

                            if (String.IsNullOrEmpty(propertyName))
                            {
                                throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyName(resourceType.FullName));
                            }

                            Type valueType;
                            ResourceType propertyResourceType;

                            object value = property.Value;

                            if (value == null || value == DBNull.Value)
                            {
                                valueType = typeof(string);
                                propertyResourceType = ResourceType.PrimitiveStringResourceType;
                            }
                            else
                            {
                                valueType = value.GetType();
                                propertyResourceType = WebUtil.GetResourceType(this.Provider, value);
                            }

                            // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). So ignore it.
                            if (propertyResourceType == null)
                            {
                                throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyType(propertyName));
                            }

                            if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
                            {
                                if (value != null && SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, propertyName))
                                {
                                    continue;
                                }

                                WritePrimitiveValue(value, propertyName, propertyResourceType.FullName, content);
                            }
                            else
                            {
                                if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                                {
                                    Debug.Assert(propertyResourceType.InstanceType == valueType, "propertyResourceType.Type == valueType");
                                    this.WriteComplexObjectValue(
                                            value,
                                            propertyName,
                                            propertyResourceType,
                                            relativeUri + "/" + propertyName,
                                            content,
                                            SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, propertyName));
                                }
                                else
                                {
                                    Debug.Assert(
                                        propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType,
                                        "propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType -- otherwise should have been processed as primitve or complex type.");

                                    // Open navigation properties are not supported on OpenTypes
                                    throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName));
                                }
                            }
                        }
                    }
                }
                else
                {
                    foreach (ProjectionNode projectionNode in projectionNodes)
                    {
                        string propertyName = projectionNode.PropertyName;
                        ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);

                        // First solve the normal entity type property - turn it into a nav. property record
                        if (property != null && property.TypeKind == ResourceTypeKind.EntityType)
                        {
                            Debug.Assert(navProperties != null, "navProperties list must be assigned for entity types");

                            // By calling the GetResourceProperties we will use the cached list of properties
                            //   for the given type and set. But we have to search through it.
                            // We could use the GetContainer (since that's what the GetResourceProperties does) and check
                            //   if it returns null, but result of that is only partially cached so it might be expensive
                            //   to evaluate for each item in the feed.
                            if (this.Provider.GetResourceProperties(this.CurrentContainer, resourceType).Contains(property))
                            {
                                object expandedPropertyValue =
                                    (this.ShouldExpandSegment(propertyName)) ? GetExpandedProperty(this.Provider, expanded, customObject, property) : null;
                                navProperties.Add(new ResourcePropertyInfo(property, expandedPropertyValue));
                            }

                            continue;
                        }

                        // Now get the property value
                        object propertyValue = WebUtil.GetPropertyValue(this.Provider, customObject, resourceType, property, property == null ? propertyName : null);

                        // Determine the type of the property
                        ResourceType propertyResourceType;
                        if (property != null)
                        {
                            propertyResourceType = property.ResourceType;
                        }
                        else
                        {
                            if (propertyValue == null || propertyValue == DBNull.Value)
                            {
                                propertyResourceType = ResourceType.PrimitiveStringResourceType;
                            }
                            else
                            {
                                propertyResourceType = WebUtil.GetResourceType(this.Provider, propertyValue);

                                // A null ResourceType indicates a ----ed type (eg, IntPtr or DateTimeOffset). So ignore it.
                                if (propertyResourceType == null)
                                {
                                    throw new DataServiceException(500, Strings.Syndication_InvalidOpenPropertyType(propertyName));
                                }
                            }
                        }

                        // And write out the value (depending on the type of the property)
                        if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
                        {
                            if (propertyValue == DBNull.Value)
                            {
                                propertyValue = null;
                            }

                            if (propertyValue != null && SyndicationSerializer.EpmNeedToSkip(currentSourceRoot, propertyName))
                            {
                                continue;
                            }

                            WritePrimitiveValue(propertyValue, propertyName, propertyResourceType.FullName, content);
                        }
                        else if (propertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                        {
                            bool needPop = false;
                            if (property != null)
                            {
                                needPop = this.PushSegmentForProperty(property);
                            }

                            this.WriteComplexObjectValue(
                                    propertyValue,
                                    propertyName,
                                    propertyResourceType,
                                    relativeUri + "/" + propertyName,
                                    content,
                                    SyndicationSerializer.EpmGetComplexPropertySegment(currentSourceRoot, propertyName));
                            if (property != null)
                            {
                                this.PopSegmentName(needPop);
                            }
                        }
                        else
                        {
                            Debug.Assert(
                                propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType,
                                "propertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType -- otherwise should have been processed as primitve or complex type.");

                            // Open navigation properties are not supported on OpenTypes
                            throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName));
                        }
                    }
                }

                if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                {
                    for (int i = 0; i < navProperties.Count; i++)
                    {
                        ResourcePropertyInfo propertyInfo = navProperties[i];
                        ResourceProperty navProperty = propertyInfo.Property;

                        Debug.Assert(
                            navProperty.IsOfKind(ResourcePropertyKind.ResourceReference) ||
                            navProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference),
                            "this must be nav property");

                        // Generate a link - see http://tools.ietf.org/html/rfc4287#section-4.2.7
                        string linkType = navProperty.IsOfKind(ResourcePropertyKind.ResourceReference) ? XmlConstants.AtomEntryElementName : XmlConstants.AtomFeedElementName;
                        linkType = String.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", XmlConstants.MimeApplicationAtom, XmlConstants.AtomTypeAttributeName, linkType);
                        string segmentIdentifier = navProperty.Name;

                        if (!this.ShouldExpandSegment(navProperty.Name))
                        {
                            WriteDeferredContentElement(
                                XmlConstants.DataWebRelatedNamespace + navProperty.Name,
                                navProperty.Name,
                                relativeUri + "/" + segmentIdentifier,
                                linkType,
                                item);
                        }
                        else
                        {
                            object propertyValue = propertyInfo.Value;
                            IExpandedResult expandedResultPropertyValue = propertyValue as IExpandedResult;
                            object expandedPropertyValue =
                                expandedResultPropertyValue != null ?
                                GetExpandedElement(expandedResultPropertyValue) :
                                propertyValue;
                            string propertyRelativeUri = relativeUri + "/" + segmentIdentifier;
                            Uri propertyAbsoluteUri = RequestUriProcessor.AppendUnescapedSegment(absoluteUri, segmentIdentifier);

                            SyndicationLink link = new SyndicationLink();
                            link.RelationshipType = XmlConstants.DataWebRelatedNamespace + navProperty.Name;
                            link.Title = navProperty.Name;
                            link.Uri = new Uri(propertyRelativeUri, UriKind.RelativeOrAbsolute);
                            link.MediaType = linkType;
                            item.Links.Add(link);

                            bool needPop = this.PushSegmentForProperty(navProperty);

                            // if this.CurrentContainer is null, the target set of the navigation property is hidden.
                            if (this.CurrentContainer != null)
                            {
                                if (navProperty.IsOfKind(ResourcePropertyKind.ResourceSetReference))
                                {
                                    IEnumerable enumerable;
                                    bool collection = WebUtil.IsElementIEnumerable(expandedPropertyValue, out enumerable);
                                    Debug.Assert(collection, "metadata loading must have ensured that navigation set properties must implement IEnumerable");

                                    SyndicationFeed feed = new SyndicationFeed();
                                    InlineAtomFeed inlineFeedExtension = new InlineAtomFeed(feed, this.factory);
                                    link.ElementExtensions.Add(inlineFeedExtension);
                                    IEnumerator enumerator = enumerable.GetEnumerator();
                                    try
                                    {
                                        bool hasMoved = enumerator.MoveNext();
                                        this.WriteFeedElements(
                                            propertyValue as IExpandedResult, 
                                            enumerator, 
                                            navProperty.ResourceType, 
                                            navProperty.Name, 
                                            propertyAbsoluteUri, 
                                            propertyRelativeUri, 
                                            hasMoved, 
                                            feed,
                                            true);
                                    }
                                    catch
                                    {
                                        WebUtil.Dispose(enumerator);
                                        throw;
                                    }
                                }
                                else
                                {
                                    SyndicationItem inlineItem = new SyndicationItem();
                                    this.WriteEntryElement(propertyValue as IExpandedResult, expandedPropertyValue, navProperty.ResourceType, propertyAbsoluteUri, propertyRelativeUri, inlineItem);
                                    InlineAtomItem inlineItemExtension = new InlineAtomItem(inlineItem, this.factory);
                                    link.ElementExtensions.Add(inlineItemExtension);
                                }
                            }

                            this.PopSegmentName(needPop);
                        }
                    }
                }
            }
            finally
            {
                // The matching call to RecurseLeave is in a try/finally block not because it's necessary in the 
                // presence of an exception (progress will halt anyway), but because it's easier to maintain in the 
                // code in the presence of multiple exit points (returns).
                this.RecurseLeave();
            }
        }
		/// <summary>
		/// Reads a property value starting on a complex value.
		/// </summary>
		/// <param name="complexValue">The complex value to start with.</param>
		/// <param name="complexPropertySegment">The EPM source path segment which points to the <paramref name="complexValue"/>.</param>
		/// <param name="epmValueCache">The EPM value cache to use.</param>
		/// <param name="sourceSegmentIndex">The index in the property value path to start with.</param>
		/// <param name="resourceType">The resource type of the complex value.</param>
		/// <param name="metadata">The metadata provider to use.</param>
		/// <param name="nullOnParentProperty">true if the value of the property is null because one of its parent properties was null, in this case
		/// the return value of the method is always null. false if the value of the property is the actual property value which may or may not be null.</param>
		/// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
		private object ReadComplexPropertyValue(
			ODataComplexValue complexValue, 
			EpmSourcePathSegment complexPropertySegment, 
			EpmValueCache epmValueCache, 
			int sourceSegmentIndex, 
			ResourceType resourceType,
			DataServiceMetadataProviderWrapper metadata,
			out bool nullOnParentProperty)
		{
			Debug.Assert(this.propertyValuePath != null, "The propertyValuePath should have been initialized by now.");
			Debug.Assert(this.propertyValuePath.Length > sourceSegmentIndex, "The propertyValuePath must be at least as long as the source segment index.");
			Debug.Assert(epmValueCache != null, "epmValueCache != null");
			Debug.Assert(sourceSegmentIndex >= 0, "sourceSegmentIndex >= 0");
				Debug.Assert(resourceType != null, "resourceType != null");

			if (complexValue == null)
			{
				nullOnParentProperty = true;
				return null;
			}

			return this.ReadPropertyValue(
				EpmValueCache.GetComplexValueProperties(epmValueCache, complexPropertySegment, complexValue, false),
				sourceSegmentIndex,
				resourceType,
				metadata,
				epmValueCache,
				out nullOnParentProperty);
		}
Example #48
0
        /// <summary>
        /// Reads a property value starting with the specified index to the property value path.
        /// </summary>
        /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param>
        /// <param name="cachedProperties">The enumeration of properties to search for the first property in the property value path.</param>
        /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param>
        /// <param name="structuredTypeReference">The type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param>
        /// <param name="epmValueCache">The EPM value cache to use.</param>
        /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
        private object ReadPropertyValue(
            EntityPropertyMappingInfo epmInfo,
            IEnumerable <ODataProperty> cachedProperties,
            int sourceSegmentIndex,
            IEdmStructuredTypeReference structuredTypeReference,
            EpmValueCache epmValueCache)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now.");
            Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index.");
            Debug.Assert(structuredTypeReference != null, "structuredTypeReference != null");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");

            EpmSourcePathSegment sourceSegment = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string propertyName = sourceSegment.PropertyName;
            bool   lastSegment  = epmInfo.PropertyValuePath.Length == sourceSegmentIndex + 1;

            IEdmStructuredType structuredType = structuredTypeReference.StructuredDefinition();
            IEdmProperty       edmProperty    = WriterValidationUtils.ValidatePropertyDefined(propertyName, structuredType);

            if (edmProperty != null)
            {
                // If this is the last part of the path, then it has to be a primitive or atomic collection type otherwise should be a complex type
                if (lastSegment)
                {
                    if (!edmProperty.Type.IsODataPrimitiveTypeKind() && !edmProperty.Type.IsNonEntityODataCollectionTypeKind())
                    {
                        throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else
                {
                    if (edmProperty.Type.TypeKind() != EdmTypeKind.Complex)
                    {
                        throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                    }
                }
            }
            else
            {
                Debug.Assert(
                    structuredType.IsOpen,
                    "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method.");
            }

            ODataProperty property = cachedProperties == null ? null : cachedProperties.FirstOrDefault(p => p.Name == propertyName);

            if (property == null)
            {
                throw new ODataException(o.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }

            object            propertyValue        = property.Value;
            ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue;

            if (lastSegment)
            {
                if (propertyValue == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(edmProperty, this.WriterBehavior, this.atomOutputContext.Model);
                }
                else
                {
                    // If this property is the last one it has to be either a primitive or collection
                    if (propertyComplexValue != null)
                    {
                        throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                    else
                    {
                        ODataCollectionValue propertyCollectionValue = propertyValue as ODataCollectionValue;
                        if (propertyCollectionValue != null)
                        {
                            // Validate the type name for the collection
                            string typeName = propertyCollectionValue.TypeName;
                            WriterValidationUtils.ResolveTypeNameForWriting(
                                this.atomOutputContext.Model,
                                edmProperty == null ? null : edmProperty.Type,
                                ref typeName,
                                EdmTypeKind.Collection,
                                edmProperty == null);
                        }
                        else
                        {
                            if (propertyValue is ODataStreamReferenceValue)
                            {
                                // Stream properties should not come here, if it were an ODataEntry property it would have been
                                // filtered in ReadEntryPropertyValue() by "epmValueCache.EntryProperties" call.
                                throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                            }
                            else if (propertyValue is ISpatial)
                            {
                                throw new ODataException(o.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                            }
                            else if (edmProperty != null)
                            {
                                ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, edmProperty.Type);
                            }
                        }
                    }
                }

                return(propertyValue);
            }

            // Otherwise it's in the middle and thus it must be a complex value
            if (propertyComplexValue == null)
            {
                if (propertyValue != null)
                {
                    // It's not a complex value - fail.
                    throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                else
                {
                    // The value of the property is null, which can be a null complex value
                    // Note that we must not attempt to resolve the type as if the type name was null here, because
                    //  1) We don't need the type for anything anyway (the value is null, this is the end)
                    //  2) If the property is open, trying to resolve a null type name would throw
                    //     but we don't have a null type name, we have a null entire value.
                    return(null);
                }
            }

            string localTypeName = propertyComplexValue.TypeName;
            IEdmComplexTypeReference complexValueType = WriterValidationUtils.ResolveTypeNameForWriting(
                this.atomOutputContext.Model,
                edmProperty == null ? null : edmProperty.Type,
                ref localTypeName,
                EdmTypeKind.Complex,
                edmProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(
                       epmInfo,
                       propertyComplexValue,
                       epmValueCache,
                       sourceSegmentIndex + 1,
                       complexValueType));
        }
Example #49
0
        internal bool WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection,
            Action beforeValueAction,
            Action afterValueAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            string typeName = complexValue.TypeName;

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
            }

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType).AsComplexOrNull();

            string collectionItemTypeName;

            typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName);
            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            Action beforeValueCallbackWithTypeName = beforeValueAction;

            if (typeName != null)
            {
                // The beforeValueAction (if specified) will write the actual property element start.
                // So if we are to write the type attribute, we must postpone that after the start element was written.
                // And so we chain the existing action with our type attribute writing and use that
                // as the before action instead.
                if (beforeValueAction != null)
                {
                    beforeValueCallbackWithTypeName = () =>
                    {
                        beforeValueAction();
                        this.WritePropertyTypeAttribute(typeName);
                    };
                }
                else
                {
                    this.WritePropertyTypeAttribute(typeName);
                }
            }

            // NOTE: see the comments on ODataWriterBehavior.UseV1ProviderBehavior for more information
            // NOTE: We have to check for ProjectedPropertiesAnnotation.Empty here to avoid filling the cache for
            //       complex values we are writing only to ensure we don't have nested EPM-mapped null values
            //       that will end up in the content eventually.
            if (this.MessageWriterSettings.WriterBehavior != null &&
                this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior &&
                !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance))
            {
                IEdmComplexType complexType = (IEdmComplexType)complexTypeReference.Definition;
                CachedPrimitiveKeepInContentAnnotation keepInContentCache = this.Model.EpmCachedKeepPrimitiveInContent(complexType);
                if (keepInContentCache == null)
                {
                    // we are about to write the first value of the given type; compute the keep-in-content information for the primitive properties of this type.
                    List <string> keepInContentPrimitiveProperties = null;

                    // initialize the cache with all primitive properties
                    foreach (IEdmProperty edmProperty in complexType.Properties().Where(p => p.Type.IsODataPrimitiveTypeKind()))
                    {
                        // figure out the keep-in-content value
                        EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, edmProperty.Name);
                        if (entityPropertyMapping != null && entityPropertyMapping.KeepInContent)
                        {
                            if (keepInContentPrimitiveProperties == null)
                            {
                                keepInContentPrimitiveProperties = new List <string>();
                            }

                            keepInContentPrimitiveProperties.Add(edmProperty.Name);
                        }
                    }

                    this.Model.SetAnnotationValue <CachedPrimitiveKeepInContentAnnotation>(complexType, new CachedPrimitiveKeepInContentAnnotation(keepInContentPrimitiveProperties));
                }
            }

            bool propertyWritten = this.WriteProperties(
                complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(),
                EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true),
                isWritingCollection,
                beforeValueCallbackWithTypeName,
                afterValueAction,
                duplicatePropertyNamesChecker,
                epmValueCache,
                epmSourcePathSegment,
                projectedProperties);

            this.DecreaseRecursionDepth();
            return(propertyWritten);
        }
        /// <summary>
        /// Sets path to the source property.
        /// </summary>
        /// <param name="path">The path as an array of source path segments.</param>
        internal void SetPropertyValuePath(EpmSourcePathSegment[] path)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(path != null, "path != null");
            Debug.Assert(path.Length > 0, "The path must contain at least one segment.");
            Debug.Assert(this.propertyValuePath == null, "The property value path was already initialized.");

            this.propertyValuePath = path;
        }
        /// <summary>
        /// Checks if a particular property value should be skipped from the content section due to 
        /// EntityProperty mappings for friendly feeds
        /// </summary>
        /// <param name="currentSourceRoot">Current root segment in the source tree for a resource type</param>
        /// <param name="propertyName">Name of the property being checked for</param>
        /// <returns>true if skipping of property value is needed, false otherwise</returns>
        private static bool EpmNeedToSkip(EpmSourcePathSegment currentSourceRoot, String propertyName)
        {
            if (currentSourceRoot != null)
            {
                EpmSourcePathSegment epmProperty = currentSourceRoot.SubProperties.Find(subProp => subProp.PropertyName == propertyName);
                if (epmProperty != null)
                {
                    Debug.Assert(epmProperty.SubProperties.Count == 0, "Complex type added as leaf node in EPM tree.");
                    Debug.Assert(epmProperty.EpmInfo != null, "Found a non-leaf property for which EpmInfo is not set.");
                    Debug.Assert(epmProperty.EpmInfo.Attribute != null, "Attribute should always be initialized for EpmInfo.");
                    if (epmProperty.EpmInfo.Attribute.KeepInContent == false)
                    {
                        return true;
                    }
                }
            }

            return false;
        }
 /// <summary>
 /// Obtains the child EPM segment corresponding to the given <paramref name="propertyName"/>
 /// </summary>
 /// <param name="currentSourceRoot">Current root segment</param>
 /// <param name="propertyName">Name of property</param>
 /// <returns>Child segment or null if there is not segment corresponding to the given <paramref name="propertyName"/></returns>
 private static EpmSourcePathSegment EpmGetComplexPropertySegment(EpmSourcePathSegment currentSourceRoot, String propertyName)
 {
     if (currentSourceRoot != null)
     {
         return currentSourceRoot.SubProperties.Find(subProp => subProp.PropertyName == propertyName);
     }
     else
     {
         return null;
     }
 }
Example #53
0
        /// <summary>
        /// Write the content of the given entry.
        /// </summary>
        /// <param name="entry">The entry for which to write properties.</param>
        /// <param name="entryType">The <see cref="IEdmEntityType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="propertiesValueCache">The cache of properties.</param>
        /// <param name="rootSourcePathSegment">The root of the EPM source tree, if there's an EPM applied.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        private void WriteEntryContent(
            ODataEntry entry,
            IEdmEntityType entryType,
            EntryPropertiesValueCache propertiesValueCache,
            EpmSourcePathSegment rootSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(propertiesValueCache != null, "propertiesValueCache != null");

            ODataStreamReferenceValue mediaResource = entry.MediaResource;

            if (mediaResource == null)
            {
                // <content type="application/xml">
                this.atomOutputContext.XmlWriter.WriteStartElement(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomContentElementName,
                    AtomConstants.AtomNamespace);

                this.atomOutputContext.XmlWriter.WriteAttributeString(
                    AtomConstants.AtomTypeAttributeName,
                    MimeConstants.MimeApplicationXml);

                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
                this.atomEntryAndFeedSerializer.WriteProperties(
                    entryType,
                    propertiesValueCache.EntryProperties,
                    false /* isWritingCollection */,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd,
                    this.DuplicatePropertyNamesChecker,
                    propertiesValueCache,
                    rootSourcePathSegment,
                    projectedProperties);
                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();

                // </content>
                this.atomOutputContext.XmlWriter.WriteEndElement();
            }
            else
            {
                WriterValidationUtils.ValidateStreamReferenceValue(mediaResource, true);

                this.atomEntryAndFeedSerializer.WriteEntryMediaEditLink(mediaResource);

                if (mediaResource.ReadLink != null)
                {
                    // <content type="type" src="src">
                    this.atomOutputContext.XmlWriter.WriteStartElement(
                        AtomConstants.AtomNamespacePrefix,
                        AtomConstants.AtomContentElementName,
                        AtomConstants.AtomNamespace);

                    Debug.Assert(!string.IsNullOrEmpty(mediaResource.ContentType), "The default stream content type should have been validated by now. If we have a read link we must have a non-empty content type as well.");
                    this.atomOutputContext.XmlWriter.WriteAttributeString(
                        AtomConstants.AtomTypeAttributeName,
                        mediaResource.ContentType);

                    this.atomOutputContext.XmlWriter.WriteAttributeString(
                        AtomConstants.MediaLinkEntryContentSourceAttributeName,
                        this.atomEntryAndFeedSerializer.UriToUrlAttributeValue(mediaResource.ReadLink));

                    // </content>
                    this.atomOutputContext.XmlWriter.WriteEndElement();
                }

                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
                this.atomEntryAndFeedSerializer.WriteProperties(
                    entryType,
                    propertiesValueCache.EntryProperties,
                    false /* isWritingCollection */,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd,
                    this.DuplicatePropertyNamesChecker,
                    propertiesValueCache,
                    rootSourcePathSegment,
                    projectedProperties);
                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
            }
        }
Example #54
0
        private object ReadPropertyValue(EntityPropertyMappingInfo epmInfo, IEnumerable <ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache)
        {
            EpmSourcePathSegment segment            = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string             propertyName         = segment.PropertyName;
            bool               flag                 = epmInfo.PropertyValuePath.Length == (sourceSegmentIndex + 1);
            IEdmStructuredType owningStructuredType = structuredTypeReference.StructuredDefinition();
            IEdmProperty       expectedProperty     = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningStructuredType);

            if (expectedProperty != null)
            {
                if (flag)
                {
                    if (!expectedProperty.Type.IsODataPrimitiveTypeKind() && !expectedProperty.Type.IsNonEntityODataCollectionTypeKind())
                    {
                        throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else if (expectedProperty.Type.TypeKind() != EdmTypeKind.Complex)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
            }
            ODataProperty property2 = (cachedProperties == null) ? null : cachedProperties.FirstOrDefault <ODataProperty>(p => (p.Name == propertyName));

            if (property2 == null)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }
            object            obj2         = property2.Value;
            ODataComplexValue complexValue = obj2 as ODataComplexValue;

            if (flag)
            {
                if (obj2 == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(expectedProperty, this.WriterBehavior, this.atomOutputContext.Model);
                    return(obj2);
                }
                if (complexValue != null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                }
                ODataCollectionValue value3 = obj2 as ODataCollectionValue;
                if (value3 != null)
                {
                    string str = value3.TypeName;
                    WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref str, EdmTypeKind.Collection, expectedProperty == null);
                    return(obj2);
                }
                if (obj2 is ODataStreamReferenceValue)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }
                if (obj2 is ISpatial)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                }
                if (expectedProperty != null)
                {
                    ValidationUtils.ValidateIsExpectedPrimitiveType(obj2, expectedProperty.Type);
                }
                return(obj2);
            }
            if (complexValue == null)
            {
                if (obj2 != null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                return(null);
            }
            string typeName = complexValue.TypeName;
            IEdmComplexTypeReference complexType = WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref typeName, EdmTypeKind.Complex, expectedProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(epmInfo, complexValue, epmValueCache, sourceSegmentIndex + 1, complexType));
        }