/// <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));
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        /// <summary>
        /// Returns the items for the specified multivalue.
        /// </summary>
        /// <param name="epmValueCache">The EPM value cache to use (can be null).</param>
        /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param>
        /// <param name="multiValue">The multivalue to get the items for.</param>
        /// <param name="writingContent">If we're writing content of an entry or not.</param>
        /// <returns>The items enumeration for the multivalue.</returns>
        internal static IEnumerable GetMultiValueItems(
            EpmValueCache epmValueCache,
            EpmSourcePathSegment sourcePathSegment,
            ODataMultiValue multiValue,
            bool writingContent)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(multiValue != null, "multiValue != null");
            Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist.");

            if (epmValueCache == null)
            {
                return(multiValue.Items);
            }
            else
            {
                return(epmValueCache.GetMultiValueItems(sourcePathSegment, multiValue, writingContent));
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Returns the properties for the specified complex value.
        /// </summary>
        /// <param name="epmValueCache">The EPM value cache to use (can be null).</param>
        /// <param name="sourcePathSegment">The source path segment for the property which has this complex value.</param>
        /// <param name="complexValue">The complex value to get the properties for.</param>
        /// <param name="writingContent">If we're writing content of an entry or not.</param>
        /// <returns>The properties enumeration for the complex value.</returns>
        internal static IEnumerable <ODataProperty> GetComplexValueProperties(
            EpmValueCache epmValueCache,
            EpmSourcePathSegment sourcePathSegment,
            ODataComplexValue complexValue,
            bool writingContent)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");
            Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist.");

            if (epmValueCache == null)
            {
                return(complexValue.Properties);
            }
            else
            {
                return(epmValueCache.GetComplexValueProperties(sourcePathSegment, complexValue, writingContent));
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Returns the items for the specified multivalue.
        /// </summary>
        /// <param name="epmValueCache">The EPM value cache to use (can be null).</param>
        /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param>
        /// <param name="multiValue">The multivalue to get the items for.</param>
        /// <param name="writingContent">If we're writing content of an entry or not.</param>
        /// <returns>The items enumeration for the multivalue.</returns>
        internal static IEnumerable GetMultiValueItems(
            EpmValueCache epmValueCache,
            EpmSourcePathSegment sourcePathSegment, 
            ODataMultiValue multiValue, 
            bool writingContent)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(multiValue != null, "multiValue != null");
            Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist.");

            if (epmValueCache == null)
            {
                return multiValue.Items;
            }
            else
            {
                return epmValueCache.GetMultiValueItems(sourcePathSegment, multiValue, writingContent);
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Returns the properties for the specified complex value.
        /// </summary>
        /// <param name="epmValueCache">The EPM value cache to use (can be null).</param>
        /// <param name="sourcePathSegment">The source path segment for the property which has this complex value.</param>
        /// <param name="complexValue">The complex value to get the properties for.</param>
        /// <param name="writingContent">If we're writing content of an entry or not.</param>
        /// <returns>The properties enumeration for the complex value.</returns>
        internal static IEnumerable<ODataProperty> GetComplexValueProperties(
            EpmValueCache epmValueCache, 
            EpmSourcePathSegment sourcePathSegment, 
            ODataComplexValue complexValue, 
            bool writingContent)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");
            Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist.");

            if (epmValueCache == null)
            {
                return complexValue.Properties;
            }
            else
            {
                return epmValueCache.GetComplexValueProperties(sourcePathSegment, complexValue, writingContent);
            }
        }
Esempio n. 7
0
        /// <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 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 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);
        }
        /// <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();
        }
        /// <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>
		/// Reads a property value starting with the specified index to the property value path.
		/// </summary>
		/// <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="resourceType">The resource type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param>
		/// <param name="metadata">The metadata provider to use.</param>
		/// <param name="epmValueCache">The EPM value cache 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 ReadPropertyValue(
			IEnumerable<ODataProperty> cachedProperties,
			int sourceSegmentIndex,
			ResourceType resourceType,
			DataServiceMetadataProviderWrapper metadata,
			EpmValueCache epmValueCache,
			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(resourceType != null, "resourceType != null");
			Debug.Assert(epmValueCache != null, "epmValueCache != null");

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

			ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(propertyName, resourceType);
			if (resourceProperty != null)
			{
				// If this is the last part of the path, then it has to be a primitive or multiValue type otherwise should be a complex type
				if (lastSegment)
				{
					if (!resourceProperty.IsOfKind(ResourcePropertyKind.Primitive) && !resourceProperty.IsOfKind(ResourcePropertyKind.MultiValue))
					{
					throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
					}
				}
				else
				{
					if (!resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType))
					{
					throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
					}
				}
			}
			else
			{
				Debug.Assert(
					resourceType.IsOpenType, 
					"Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method.");
			}

			ODataProperty property = null;
			if (cachedProperties != null)
			{
				property = cachedProperties.FirstOrDefault(p => p.Name == propertyName);
			}

			if (property == null)
			{
				throw new ODataException(Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, resourceType.FullName));
			}

			object propertyValue = property.Value;
			ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue;
			if (lastSegment)
			{
				if (propertyValue != null && resourceProperty != null)
				{
					ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, resourceProperty.ResourceType);
				}

				// If this property is the last one it has to be either a primitive or multivalue
				// TODO: Check for named streams here as well
				if (propertyComplexValue != null)
				{
					throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
				}

				nullOnParentProperty = false;
				return propertyValue;
			}
			else
			{
				// Otherwise it's in the middle and thus it must be a complex value
				if (propertyComplexValue == null)
				{
					throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
				}

				string typeName = propertyComplexValue.TypeName;
				ResourceType complexValueType = MetadataUtils.ResolveTypeName(
					metadata,
					resourceProperty == null ? null : resourceProperty.ResourceType,
					ref typeName,
					ResourceTypeKind.ComplexType,
					resourceProperty == null);

				return this.ReadComplexPropertyValue(
					propertyComplexValue,
					sourceSegment,
					epmValueCache,
					sourceSegmentIndex + 1,
					complexValueType,
					metadata,
					out nullOnParentProperty);
			}
		}
		/// <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);
		}
        /// <summary>
        /// Reads a property value starting with the specified index to the property value path.
        /// </summary>
        /// <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="resourceType">The resource type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param>
        /// <param name="metadata">The metadata provider to use.</param>
        /// <param name="epmValueCache">The EPM value cache 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 ReadPropertyValue(
            IEnumerable <ODataProperty> cachedProperties,
            int sourceSegmentIndex,
            ResourceType resourceType,
            DataServiceMetadataProviderWrapper metadata,
            EpmValueCache epmValueCache,
            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(resourceType != null, "resourceType != null");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");

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

            ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(propertyName, resourceType);

            if (resourceProperty != null)
            {
                // If this is the last part of the path, then it has to be a primitive or multiValue type otherwise should be a complex type
                if (lastSegment)
                {
                    if (!resourceProperty.IsOfKind(ResourcePropertyKind.Primitive) && !resourceProperty.IsOfKind(ResourcePropertyKind.MultiValue))
                    {
                        throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else
                {
                    if (!resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType))
                    {
                        throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                    }
                }
            }
            else
            {
                Debug.Assert(
                    resourceType.IsOpenType,
                    "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method.");
            }

            ODataProperty property = null;

            if (cachedProperties != null)
            {
                property = cachedProperties.FirstOrDefault(p => p.Name == propertyName);
            }

            if (property == null)
            {
                throw new ODataException(Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, resourceType.FullName));
            }

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

            if (lastSegment)
            {
                if (propertyValue != null && resourceProperty != null)
                {
                    ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, resourceProperty.ResourceType);
                }

                // If this property is the last one it has to be either a primitive or multivalue
                // TODO: Check for named streams here as well
                if (propertyComplexValue != null)
                {
                    throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                }

                nullOnParentProperty = false;
                return(propertyValue);
            }
            else
            {
                // Otherwise it's in the middle and thus it must be a complex value
                if (propertyComplexValue == null)
                {
                    throw new ODataException(Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }

                string       typeName         = propertyComplexValue.TypeName;
                ResourceType complexValueType = MetadataUtils.ResolveTypeName(
                    metadata,
                    resourceProperty == null ? null : resourceProperty.ResourceType,
                    ref typeName,
                    ResourceTypeKind.ComplexType,
                    resourceProperty == null);

                return(this.ReadComplexPropertyValue(
                           propertyComplexValue,
                           sourceSegment,
                           epmValueCache,
                           sourceSegmentIndex + 1,
                           complexValueType,
                           metadata,
                           out nullOnParentProperty));
            }
        }
Esempio n. 15
0
        /// <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();
                }
            }
        }
Esempio n. 16
0
        /// <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();
        }