Exemple #1
0
        internal void WritePrimitiveValue(object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference)
        {
            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType());

            if (primitiveTypeReference == null)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName));
            }
            string collectionItemTypeName = primitiveTypeReference.FullName();

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(collectionItemTypeName, EdmTypeKind.Primitive);
                if (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, collectionItemTypeName) == 0)
                {
                    collectionItemTypeName = null;
                }
            }
            if (expectedTypeReference != null)
            {
                ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference);
            }
            if ((collectionItemTypeName != null) && (collectionItemTypeName != "Edm.String"))
            {
                this.WritePropertyTypeAttribute(collectionItemTypeName);
            }
            AtomValueUtils.WritePrimitiveValue(base.XmlWriter, value);
        }
        /// <summary>
        /// Writes an Xml element with the specified primitive value as content.
        /// </summary>
        /// <param name="writer">The Xml writer to write to.</param>
        /// <param name="prefix">The prefix for the element's namespace.</param>
        /// <param name="localName">The local name of the element.</param>
        /// <param name="ns">The namespace of the element.</param>
        /// <param name="textConstruct">The <see cref="AtomTextConstruct"/> value to be used as element content.</param>
        internal static void WriteTextConstruct(XmlWriter writer, string prefix, string localName, string ns, AtomTextConstruct textConstruct)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(prefix != null, "prefix != null");
            Debug.Assert(!string.IsNullOrEmpty(localName), "!string.IsNullOrEmpty(localName)");
            Debug.Assert(!string.IsNullOrEmpty(ns), "!string.IsNullOrEmpty(ns)");

            writer.WriteStartElement(prefix, localName, ns);

            if (textConstruct != null)
            {
                AtomTextConstructKind textKind = textConstruct.Kind;

                writer.WriteAttributeString(AtomConstants.AtomTypeAttributeName, AtomValueUtils.ToString(textConstruct.Kind));

                string textValue = textConstruct.Text ?? string.Empty;
                if (textKind == AtomTextConstructKind.Xhtml)
                {
                    writer.WriteRaw(textValue);
                }
                else
                {
                    writer.WriteString(textValue);
                }
            }

            writer.WriteEndElement();
        }
        private ODataEntityReferenceLinks ReadLinksElement()
        {
            ODataEntityReferenceLinks                   links      = new ODataEntityReferenceLinks();
            List <ODataEntityReferenceLink>             sourceList = new List <ODataEntityReferenceLink>();
            DuplicateEntityReferenceLinksElementBitMask none       = DuplicateEntityReferenceLinksElementBitMask.None;

            if (base.XmlReader.IsEmptyElement)
            {
                goto Label_018C;
            }
            base.XmlReader.Read();
Label_002A:
            switch (base.XmlReader.NodeType)
            {
            case XmlNodeType.Element:
                if ((base.XmlReader.NamespaceEquals(base.XmlReader.ODataMetadataNamespace) && base.XmlReader.LocalNameEquals(this.ODataCountElementName)) && (base.Version >= ODataVersion.V2))
                {
                    VerifyEntityReferenceLinksElementNotFound(ref none, DuplicateEntityReferenceLinksElementBitMask.Count, base.XmlReader.ODataMetadataNamespace, "count");
                    long num = (long)AtomValueUtils.ReadPrimitiveValue(base.XmlReader, EdmCoreModel.Instance.GetInt64(false));
                    links.Count = new long?(num);
                    base.XmlReader.Read();
                    goto Label_017A;
                }
                if (base.XmlReader.NamespaceEquals(base.XmlReader.ODataNamespace))
                {
                    if (base.XmlReader.LocalNameEquals(this.ODataUriElementName))
                    {
                        ODataEntityReferenceLink item = this.ReadUriElement();
                        sourceList.Add(item);
                        goto Label_017A;
                    }
                    if (base.XmlReader.LocalNameEquals(this.ODataNextElementName) && (base.Version >= ODataVersion.V2))
                    {
                        VerifyEntityReferenceLinksElementNotFound(ref none, DuplicateEntityReferenceLinksElementBitMask.NextLink, base.XmlReader.ODataNamespace, "next");
                        Uri    xmlBaseUri     = base.XmlReader.XmlBaseUri;
                        string uriFromPayload = base.XmlReader.ReadElementValue();
                        links.NextPageLink = base.ProcessUriFromPayload(uriFromPayload, xmlBaseUri);
                        goto Label_017A;
                    }
                }
                break;

            case XmlNodeType.EndElement:
                goto Label_017A;
            }
            base.XmlReader.Skip();
Label_017A:
            if (base.XmlReader.NodeType != XmlNodeType.EndElement)
            {
                goto Label_002A;
            }
Label_018C:
            base.XmlReader.Read();
            links.Links = new ReadOnlyEnumerable <ODataEntityReferenceLink>(sourceList);
            return(links);
        }
Exemple #4
0
        /// <summary>
        /// Given a property value returns the text value to be used in EPM.
        /// </summary>
        /// <param name="propertyValue">The value of the property.</param>
        /// <returns>The text representation of the value, or the method throws if the text representation was not possible to obtain.</returns>
        internal static string GetPropertyValueAsText(object propertyValue)
        {
            DebugUtils.CheckNoExternalCallers();

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

            return(AtomValueUtils.ConvertPrimitiveToString(propertyValue));
        }
        /// <summary>
        /// Read an enumeration value from the reader.
        /// </summary>
        /// <param name="actualValueTypeReference">The thpe of the value to read.</param>
        /// <returns>An ODataEnumValue with the value read from the payload.</returns>
        private ODataEnumValue ReadEnumValue(IEdmEnumTypeReference actualValueTypeReference)
        {
            Debug.Assert(actualValueTypeReference != null, "actualValueTypeReference != null");
            Debug.Assert(actualValueTypeReference.TypeKind() == EdmTypeKind.Enum, "Only Enum values can be read by this method.");
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);

            ODataEnumValue result = AtomValueUtils.ReadEnumValue(this.XmlReader, actualValueTypeReference);

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(result != null, "The method should never return null since it doesn't handle null values.");
            return(result);
        }
Exemple #6
0
        internal static string GetPropertyValueAsText(object propertyValue)
        {
            string str;

            if (propertyValue == null)
            {
                return(null);
            }
            if (!AtomValueUtils.TryConvertPrimitiveToString(propertyValue, out str))
            {
                throw new ODataException(Microsoft.Data.OData.Strings.AtomValueUtils_CannotConvertValueToAtomPrimitive(propertyValue.GetType().FullName));
            }
            return(str);
        }
Exemple #7
0
        /// <summary>
        /// Given a property value returns the text value to be used in EPM.
        /// </summary>
        /// <param name="propertyValue">The value of the property.</param>
        /// <returns>The text representation of the value, or the method throws if the text representation was not possible to obtain.</returns>
        internal static string GetPropertyValueAsText(object propertyValue)
        {
            DebugUtils.CheckNoExternalCallers();

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

            string textPropertyValue;

            if (!AtomValueUtils.TryConvertPrimitiveToString(propertyValue, out textPropertyValue))
            {
                throw new ODataException(Strings.AtomValueUtils_CannotConvertValueToAtomPrimitive(propertyValue.GetType().FullName));
            }

            return(textPropertyValue);
        }
        /// <summary>
        /// Writes a primitive value.
        /// </summary>
        /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="expectedType">The expected resource type of the primitive value.</param>
        internal static void WritePrimitiveValue(XmlWriter writer, object value, ResourceType expectedType)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(value != null, "value != null");

            string typeName;

            if (!MetadataUtils.TryGetPrimitiveTypeName(value, out typeName))
            {
                throw new ODataException(Strings.ODataWriter_UnsupportedPrimitiveType(value.GetType().FullName));
            }

            if (typeName != EdmConstants.EdmStringTypeName)
            {
                WritePropertyTypeAttribute(writer, typeName);
            }

            AtomValueUtils.WritePrimitiveValue(writer, value, expectedType);
        }
Exemple #9
0
        /// <summary>
        /// Writes a primitive value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the primitive value.</param>
        internal void WritePrimitiveValue(
            object value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(value != null, "value != null");

            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType());

            if (primitiveTypeReference == null)
            {
                throw new ODataException(o.Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName));
            }

            string typeName = primitiveTypeReference.FullName();

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

                if (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0)
                {
                    typeName = null;
                }
            }

            if (expectedTypeReference != null)
            {
                ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference);
            }

            if (typeName != null && typeName != Metadata.EdmConstants.EdmStringTypeName)
            {
                this.WritePropertyTypeAttribute(typeName);
            }

            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value);
        }
Exemple #10
0
        /// <summary>
        /// Writes the value of a primitive instance annotation.
        /// </summary>
        /// <param name="primitiveValue">The primitive value to write.</param>
        /// <param name="expectedTypeReference">The expected type of the annotation from the metadata.</param>
        private void WritePrimitiveInstanceAnnotationValue(ODataPrimitiveValue primitiveValue, IEdmTypeReference expectedTypeReference)
        {
            object clrValue = primitiveValue.Value;
            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(clrValue.GetType());
            string attributeValueNotationName = AtomInstanceAnnotation.LookupAttributeValueNotationNameByEdmTypeKind(primitiveTypeReference.PrimitiveKind());

            // Some primitive values can be specified more concisely via an attribute rather than in the content of the xml element. This is called "attribute value notation".
            // If we're writing a type that supports this, then we always prefer attribute value notation over writing the value in the element content.
            if (attributeValueNotationName != null)
            {
                if (expectedTypeReference != null)
                {
                    ValidationUtils.ValidateIsExpectedPrimitiveType(primitiveValue.Value, primitiveTypeReference, expectedTypeReference);
                }

                this.XmlWriter.WriteAttributeString(attributeValueNotationName, AtomValueUtils.ConvertPrimitiveToString(clrValue));
            }
            else
            {
                this.WritePrimitiveValue(clrValue, /*collectionValidator*/ null, expectedTypeReference, primitiveValue.GetAnnotation <SerializationTypeNameAnnotation>());
            }
        }
        /// <summary>
        /// Writes an enumeration value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the enumeration value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this enumeration value. The annotation value will override whatever type name is being written.</param>
        internal void WriteEnumValue(
            ODataEnumValue value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            Debug.Assert(value != null, "value != null");

            // write type name without validation:
            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, expectedTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);

            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");
            if (typeName != null)
            {
                Debug.Assert(typeName != EdmConstants.EdmStringTypeName, "Enum typeName != EdmConstants.StringTypeName");
                this.WritePropertyTypeAttribute(typeName);
            }

            // write string value without validation:
            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value.Value);
        }
Exemple #12
0
        /// <summary>
        /// Writes a primitive value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the primitive value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this primitive value. The annotation value will override whatever type name is being written.</param>
        internal void WritePrimitiveValue(
            object value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(value != null, "value != null");

            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType());

            if (primitiveTypeReference == null)
            {
                throw new ODataException(ODataErrorStrings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName));
            }

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive);
            }

            if (expectedTypeReference != null)
            {
                ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference);
            }

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, primitiveTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);

            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            if (typeName != null && typeName != EdmConstants.EdmStringTypeName)
            {
                this.WritePropertyTypeAttribute(typeName);
            }

            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value);
        }
        /// <summary>
        /// Reads an annotation's value from the annotation value notation specified on the current element.
        /// </summary>
        /// <param name="expectedTypeReference">The expected type reference of the vocabulary term from the metadata.</param>
        /// <param name="attributeValueNotationTypeReference">The type reference indicated by the name of the attribute used in attribute value notation.
        ///   For example, if the attribute was called "string", this will be a reference to the string type.</param>
        /// <param name="attributeValueNotationAttributeName">The name of the attribute used by attribute avalue notation.</param>
        /// <param name="attributeValueNotationAttributeValue">The value of the attribute used by attribute value notation.</param>
        /// <param name="typeAttributeValue">The value of the "m:type" attribute on the annotation element.</param>
        /// <param name="positionedOnEmptyElement">true if the annotation element is empty, false otherwise.</param>
        /// <param name="model">The edm model instance.</param>
        /// <param name="messageReaderSettings">The message reader settings instance.</param>
        /// <param name="version">The payload version to read.</param>
        /// <returns>The primitive value represented on this element via attribute value notation.</returns>
        private static ODataPrimitiveValue GetValueFromAttributeValueNotation(
            IEdmTypeReference expectedTypeReference,
            IEdmPrimitiveTypeReference attributeValueNotationTypeReference,
            string attributeValueNotationAttributeName,
            string attributeValueNotationAttributeValue,
            string typeAttributeValue,
            bool positionedOnEmptyElement,
            IEdmModel model,
            ODataMessageReaderSettings messageReaderSettings,
            ODataVersion version)
        {
            Debug.Assert(attributeValueNotationTypeReference != null, "attributeValueNotationTypeReference != null");

            if (!positionedOnEmptyElement)
            {
                // If there is content in the body of the element, throw since it's ambiguous whether we should use the value from the attribute or the element content.
                throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_AttributeValueNotationUsedOnNonEmptyElement(attributeValueNotationAttributeName));
            }

            // If both m:type is present and attribute value notation is being used, they must match.
            // For example, if m:type is "Edm.Int32", but the "string" attribute is also present, we should throw.
            if (typeAttributeValue != null && !string.Equals(attributeValueNotationTypeReference.Definition.ODataFullName(), typeAttributeValue, StringComparison.Ordinal))
            {
                throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_AttributeValueNotationUsedWithIncompatibleType(typeAttributeValue, attributeValueNotationAttributeName));
            }

            IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolveAndValidatePrimitiveTargetType(
                expectedTypeReference,
                EdmTypeKind.Primitive,
                attributeValueNotationTypeReference.Definition,
                attributeValueNotationTypeReference.ODataFullName(),
                attributeValueNotationTypeReference.Definition,
                model,
                messageReaderSettings,
                version);

            return(new ODataPrimitiveValue(AtomValueUtils.ConvertStringToPrimitive(attributeValueNotationAttributeValue, targetTypeReference.AsPrimitive())));
        }
 internal void WriteTextConstruct(string prefix, string localName, string ns, AtomTextConstruct textConstruct)
 {
     base.XmlWriter.WriteStartElement(prefix, localName, ns);
     if (textConstruct != null)
     {
         AtomTextConstructKind kind = textConstruct.Kind;
         base.XmlWriter.WriteAttributeString("type", AtomValueUtils.ToString(textConstruct.Kind));
         string text = textConstruct.Text;
         if (text == null)
         {
             text = string.Empty;
         }
         if (kind == AtomTextConstructKind.Xhtml)
         {
             ODataAtomWriterUtils.WriteRaw(base.XmlWriter, text);
         }
         else
         {
             ODataAtomWriterUtils.WriteString(base.XmlWriter, text);
         }
     }
     base.XmlWriter.WriteEndElement();
 }
Exemple #15
0
        private void SetEpmValueForSegment(EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, List <ODataProperty> existingProperties, object propertyValue)
        {
            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            if (!epmInfo.Attribute.KeepInContent)
            {
                IEdmTypeReference type;
                ODataProperty     property = existingProperties.FirstOrDefault <ODataProperty>(p => string.CompareOrdinal(p.Name, propertyName) == 0);
                ODataComplexValue value2   = null;
                if (property != null)
                {
                    value2 = property.Value as ODataComplexValue;
                    if (value2 == null)
                    {
                        return;
                    }
                }
                IEdmProperty property2 = segmentStructuralTypeReference.FindProperty(propertyName);
                if ((property2 == null) && (propertyValuePathIndex != (epmInfo.PropertyValuePath.Length - 1)))
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
                }
                if ((property2 == null) || (this.MessageReaderSettings.DisablePrimitiveTypeConversion && property2.Type.IsODataPrimitiveTypeKind()))
                {
                    type = EdmCoreModel.Instance.GetString(true);
                }
                else
                {
                    type = property2.Type;
                }
                switch (type.TypeKind())
                {
                case EdmTypeKind.Primitive:
                    object obj2;
                    if (type.IsStream())
                    {
                        throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                    }
                    if (propertyValue == null)
                    {
                        ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, type, this.atomInputContext.MessageReaderSettings, true, this.atomInputContext.Version);
                        obj2 = null;
                    }
                    else
                    {
                        obj2 = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, type.AsPrimitive());
                    }
                    this.AddEpmPropertyValue(existingProperties, propertyName, obj2, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    return;

                case EdmTypeKind.Complex:
                {
                    if (value2 == null)
                    {
                        value2 = new ODataComplexValue {
                            TypeName   = type.ODataFullName(),
                            Properties = new ReadOnlyEnumerable <ODataProperty>()
                        };
                        this.AddEpmPropertyValue(existingProperties, propertyName, value2, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    }
                    IEdmComplexTypeReference reference2 = type.AsComplex();
                    this.SetEpmValueForSegment(epmInfo, propertyValuePathIndex + 1, reference2, ReaderUtils.GetPropertiesList(value2.Properties), propertyValue);
                    return;
                }

                case EdmTypeKind.Collection:
                {
                    ODataCollectionValue value4 = new ODataCollectionValue {
                        TypeName = type.ODataFullName(),
                        Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                    };
                    this.AddEpmPropertyValue(existingProperties, propertyName, value4, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    return;
                }
                }
                throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
Exemple #16
0
 private object ReadPrimitiveValue(IEdmPrimitiveTypeReference actualValueTypeReference)
 {
     return(AtomValueUtils.ReadPrimitiveValue(base.XmlReader, actualValueTypeReference));
 }
        /// <summary>
        /// Writes an Xml element with the specified primitive value as content.
        /// </summary>
        /// <param name="prefix">The prefix for the element's namespace.</param>
        /// <param name="localName">The local name of the element.</param>
        /// <param name="ns">The namespace of the element.</param>
        /// <param name="textConstruct">The <see cref="AtomTextConstruct"/> value to be used as element content.</param>
        internal void WriteTextConstruct(string prefix, string localName, string ns, AtomTextConstruct textConstruct)
        {
            Debug.Assert(prefix != null, "prefix != null");
            Debug.Assert(!string.IsNullOrEmpty(localName), "!string.IsNullOrEmpty(localName)");
            Debug.Assert(!string.IsNullOrEmpty(ns), "!string.IsNullOrEmpty(ns)");

            this.XmlWriter.WriteStartElement(prefix, localName, ns);

            if (textConstruct != null)
            {
                AtomTextConstructKind textKind = textConstruct.Kind;

                this.XmlWriter.WriteAttributeString(AtomConstants.AtomTypeAttributeName, AtomValueUtils.ToString(textConstruct.Kind));

                string textValue = textConstruct.Text;
                if (textValue == null)
                {
                    textValue = String.Empty;
                }

                if (textKind == AtomTextConstructKind.Xhtml)
                {
                    ODataAtomWriterUtils.WriteRaw(this.XmlWriter, textValue);
                }
                else
                {
                    ODataAtomWriterUtils.WriteString(this.XmlWriter, textValue);
                }
            }

            this.XmlWriter.WriteEndElement();
        }
Exemple #18
0
        private void SetEpmValueForSegment(
            EntityPropertyMappingInfo epmInfo,
            int propertyValuePathIndex,
            IEdmStructuredTypeReference segmentStructuralTypeReference,
            List <ODataProperty> existingProperties,
            object propertyValue)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(propertyValuePathIndex < epmInfo.PropertyValuePath.Length, "The propertyValuePathIndex is out of bounds.");
            Debug.Assert(existingProperties != null, "existingProperties != null");

            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            // Do not set out-of-content values if the EPM is defined as KeepInContent=true.
            if (epmInfo.Attribute.KeepInContent)
            {
                return;
            }

            // Try to find the property in the existing properties
            // If the property value is atomic from point of view of EPM (non-streaming collection or primitive) then if it already exists
            // it must have been in-content, and thus we leave it as is (note that two EPMs can't map to the same property, we verify that upfront).
            // If the property value is non-atomic, then it is a complex value, we might want to merge the new value comming from EPM with it.
            ODataProperty     existingProperty     = existingProperties.FirstOrDefault(p => string.CompareOrdinal(p.Name, propertyName) == 0);
            ODataComplexValue existingComplexValue = null;

            if (existingProperty != null)
            {
                // In case the property exists and it's a complex value we will try to merge.
                // Note that if the property is supposed to be complex, but it already has a null value, then the null wins.
                // Since in-content null complex value wins over any EPM complex value.
                existingComplexValue = existingProperty.Value as ODataComplexValue;
                if (existingComplexValue == null)
                {
                    return;
                }
            }

            IEdmProperty propertyMetadata = segmentStructuralTypeReference.FindProperty(propertyName);

            Debug.Assert(propertyMetadata != null || segmentStructuralTypeReference.IsOpen(), "We should have verified that if the property is not declared the type must be open.");

            if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1)
            {
                throw new ODataException(o.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
            }

            // Open properties in EPM are by default of type Edm.String - there's no way to specify a typename in EPM
            // consumer is free to do the conversion later on if it needs to.
            // Note that this effectively means that ODataMessageReaderSettings.DisablePrimitiveTypeConversion is as if it's turned on for open EPM properties.
            IEdmTypeReference propertyType;

            if (propertyMetadata == null ||
                (this.MessageReaderSettings.DisablePrimitiveTypeConversion && propertyMetadata.Type.IsODataPrimitiveTypeKind()))
            {
                propertyType = EdmCoreModel.Instance.GetString(/*nullable*/ true);
            }
            else
            {
                propertyType = propertyMetadata.Type;
            }

            // NOTE: WCF DS Server only applies the values when
            // - It's an open property
            // - It's not a key property
            // - It's a key property and it's a POST operation
            // ODataLib here will always set the property though.
            switch (propertyType.TypeKind())
            {
            case EdmTypeKind.Primitive:
            {
                if (propertyType.IsStream())
                {
                    throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                }

                object primitiveValue;
                if (propertyValue == null)
                {
                    ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, propertyType, this.atomInputContext.MessageReaderSettings, /*validateNullValue*/ true, this.atomInputContext.Version);
                    primitiveValue = null;
                }
                else
                {
                    // Convert the value to the desired target type
                    primitiveValue = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, propertyType.AsPrimitive());
                }

                this.AddEpmPropertyValue(existingProperties, propertyName, primitiveValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
            }

            break;

            case EdmTypeKind.Complex:
                // Note: Unlike WCF DS we don't have a preexisting instance to override (since complex values are atomic, so we should not updated them)
                // In our case the complex value either doesn't exist yet on the entry being reported (easy, create it)
                // or it exists, but then it was created during reading of previous normal or EPM properties for this entry. It never exists before
                // we ever get to see the entity. So in our case we will never recreate the complex value, we always start with new one
                // and update it with new properties as they come. (Next time we will start over with a new complex value.)
                Debug.Assert(
                    existingComplexValue == null || (existingProperty != null && existingProperty.Value == existingComplexValue),
                    "If we have existing complex value, we must have an existing property as well.");
                Debug.Assert(
                    epmInfo.PropertyValuePath.Length > propertyValuePathIndex + 1,
                    "Complex value can not be a leaf segment in the source property path. We should have failed constructing the EPM trees for it.");

                if (existingComplexValue == null)
                {
                    Debug.Assert(existingProperty == null, "If we don't have an existing complex value, then we must not have an existing property at all.");

                    // Create a new complex value and set its type name to the type name of the property type (in case of EPM we never have type name from the payload)
                    existingComplexValue = new ODataComplexValue
                    {
                        TypeName   = propertyType.ODataFullName(),
                        Properties = new ReadOnlyEnumerable <ODataProperty>()
                    };

                    this.AddEpmPropertyValue(existingProperties, propertyName, existingComplexValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
                }

                // Get the properties list of the complex value and recursively set the next EPM segment value to it.
                // Note that on inner complex value we don't need to check for duplicate properties
                // because EPM will never add a property which already exists (see the start of this method).
                IEdmComplexTypeReference complexPropertyTypeReference = propertyType.AsComplex();
                Debug.Assert(complexPropertyTypeReference != null, "complexPropertyTypeReference != null");
                this.SetEpmValueForSegment(
                    epmInfo,
                    propertyValuePathIndex + 1,
                    complexPropertyTypeReference,
                    ReaderUtils.GetPropertiesList(existingComplexValue.Properties),
                    propertyValue);

                break;

            case EdmTypeKind.Collection:
                Debug.Assert(propertyType.IsNonEntityODataCollectionTypeKind(), "Collection types in EPM must be atomic.");

                // In this case the property value is the internal list of items.
                // Create a new collection value and set the list as the list of items on it.
                ODataCollectionValue collectionValue = new ODataCollectionValue
                {
                    TypeName = propertyType.ODataFullName(),
                    Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                };

                this.AddEpmPropertyValue(existingProperties, propertyName, collectionValue, segmentStructuralTypeReference.IsODataEntityTypeKind());

                break;

            default:
                throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
        /// <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();
                }
            }
        }
Exemple #20
0
        /// <summary>
        /// Reads all top-level entity reference links and the (optional) inline count and next link elements.
        /// </summary>
        /// <returns>An <see cref="ODataEntityReferenceLinks"/> instance representing the read entity reference links.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The 'd:links' element.
        /// Post-Condtion:  any                 - The node after the 'd:links' end element (or empty 'd:links' element).
        /// </remarks>
        private ODataEntityReferenceLinks ReadLinksElement()
        {
            Debug.Assert(this.XmlReader != null, "this.XmlReader != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.NamespaceURI == this.XmlReader.ODataNamespace, "this.XmlReader.NamespaceURI == this.XmlReader.ODataNamespace");
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.ODataLinksElementName, "this.XmlReader.LocalName == AtomConstants.ODataLinksElementName");

            ODataEntityReferenceLinks                   links               = new ODataEntityReferenceLinks();
            List <ODataEntityReferenceLink>             linkList            = new List <ODataEntityReferenceLink>();
            DuplicateEntityReferenceLinksElementBitMask elementsReadBitmask = DuplicateEntityReferenceLinksElementBitMask.None;

            if (!this.XmlReader.IsEmptyElement)
            {
                // Move to the first child node of the element.
                this.XmlReader.Read();

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                    case XmlNodeType.EndElement:
                        // end of the <links> element
                        continue;

                    case XmlNodeType.Element:
                        // <m:count>
                        if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace) &&
                            this.XmlReader.LocalNameEquals(this.ODataCountElementName) &&
                            this.Version >= ODataVersion.V2)
                        {
                            VerifyEntityReferenceLinksElementNotFound(
                                ref elementsReadBitmask,
                                DuplicateEntityReferenceLinksElementBitMask.Count,
                                this.XmlReader.ODataMetadataNamespace,
                                AtomConstants.ODataCountElementName);

                            // Note that we allow negative values to be read.
                            long countValue = (long)AtomValueUtils.ReadPrimitiveValue(this.XmlReader, EdmCoreModel.Instance.GetInt64(false));
                            links.Count = countValue;

                            // Read over the end element of the <m:count> element
                            this.XmlReader.Read();

                            continue;
                        }

                        if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                        {
                            // <d:uri>
                            if (this.XmlReader.LocalNameEquals(this.ODataUriElementName))
                            {
                                ODataEntityReferenceLink link = this.ReadUriElement();
                                linkList.Add(link);

                                continue;
                            }

                            // <d:next>
                            if (this.XmlReader.LocalNameEquals(this.ODataNextElementName) && this.Version >= ODataVersion.V2)
                            {
                                VerifyEntityReferenceLinksElementNotFound(
                                    ref elementsReadBitmask,
                                    DuplicateEntityReferenceLinksElementBitMask.NextLink,
                                    this.XmlReader.ODataNamespace,
                                    AtomConstants.ODataNextLinkElementName);

                                // NOTE: get the base URI here before we read the content as string; reading the content as string will move the
                                //       reader to the end element and thus we lose the xml:base definition on the element.
                                Uri    xmlBaseUri = this.XmlReader.XmlBaseUri;
                                string uriString  = this.XmlReader.ReadElementValue();
                                links.NextPageLink = this.ProcessUriFromPayload(uriString, xmlBaseUri);

                                continue;
                            }
                        }

                        break;

                    default:
                        break;
                    }

                    this.XmlReader.Skip();
                }while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            }

            // Read over the end element, or empty start element.
            this.XmlReader.Read();

            links.Links = new ReadOnlyEnumerable <ODataEntityReferenceLink>(linkList);
            return(links);
        }